* [PATCHSET] new Power Management for libata, take 4
@ 2006-07-03 7:07 Tejun Heo
2006-07-03 7:07 ` [PATCH 01/11] libata: implement ATA_EHI_RESUME_LINK Tejun Heo
` (10 more replies)
0 siblings, 11 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide, htejun
Hello, all.
This is the fourth take of new-power-management patchset. Changes
from the last take[1] are...
* Patches #1-4 added to implement
- ATA_EHI_RESUME_LINK, ATA_EHI_NO_AUTOPSY, ATA_EHI_QUIET
- better debounce timing selection
These changes allow PM to behave more accurately. e.g. Resume isn't
reported or recorded as ATA bus error anymore. No excessive EH
messages on suspend and resume...
* move-ata_flush_cache patch dropped
* PM implementation patches slighly updated to use new features
implemented in #1-4.
This patchset contains 11 patches.
#01-04 : EH/reset improvements
#05-07 : new core PM implementation
#08-11 : convert LLDs to use new PM
As before, ata_piix, sata_sil and sata_sil24 are converted. Both mem
and disk sleeps are tested and verifed to work on the following
configurations.
* On P5LD2 + latest BIOS
- onboard ICH7R in ata_piix mode
- sil3112 PCI card
- sil3114 PCI card
- sil3124 PCI-x card on a PCI slot
- sil3132 PCI-e card
* On Fujitsu lifebook P7120 (+ PATA enabled + patch for PCI ID)
- onboard ICH6M in ata_piix mode
- sil3112 PCMCIA card
This patchset is against
upstream[2]
+ add-ap-pflags [3]
+ fix-ehc_i_action-setting-in-ata_eh_autopsy [4]
+ cosmetic-replace-ap_lock [5]
This tree is also avilable in the following git tree.
http://htj.dyndns.org/git/?p=libata-tj.git;a=shortlog;h=new-pm
git://htj.dyndns.org/libata-tj new-pm
Thanks.
--
tejun
[1] http://article.gmane.org/gmane.linux.ide/11684
[2] 2154cfa6ba560401d25f6cc083fe3fb996cbb571
[3] http://article.gmane.org/gmane.linux.ide/11717
[4] http://article.gmane.org/gmane.linux.ide/11801
[5] http://article.gmane.org/gmane.linux.ide/11802
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 04/11] libata: separate out __ata_ehi_hotplugged()
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
` (3 preceding siblings ...)
2006-07-03 7:07 ` [PATCH 02/11] libata: clean up debounce parameters and improve parameter selection Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 05/11] libata: implement PM EH actions Tejun Heo
` (5 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Separate out __ata_ehi_hotplugged() from ata_ehi_hotplugged(). The
underscored version doesn't set AC_ERR_ATA_BUS. This will be used for
resume which is a hotplug event but not an ATA bus error.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
include/linux/libata.h | 9 +++++++--
1 files changed, 7 insertions(+), 2 deletions(-)
05c3b3361ede81cdf6e1331af21baa44f593be9b
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 36938ae..2aa1398 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -843,7 +843,7 @@ #define ata_ehi_clear_desc(ehi) do { \
(ehi)->desc_len = 0; \
} while (0)
-static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi)
+static inline void __ata_ehi_hotplugged(struct ata_eh_info *ehi)
{
if (ehi->flags & ATA_EHI_HOTPLUGGED)
return;
@@ -851,11 +851,16 @@ static inline void ata_ehi_hotplugged(st
ehi->flags |= ATA_EHI_HOTPLUGGED | ATA_EHI_RESUME_LINK;
ehi->hotplug_timestamp = jiffies;
- ehi->err_mask |= AC_ERR_ATA_BUS;
ehi->action |= ATA_EH_SOFTRESET;
ehi->probe_mask |= (1 << ATA_MAX_DEVICES) - 1;
}
+static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi)
+{
+ __ata_ehi_hotplugged(ehi);
+ ehi->err_mask |= AC_ERR_ATA_BUS;
+}
+
/*
* qc helpers
*/
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 01/11] libata: implement ATA_EHI_RESUME_LINK
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-06 2:16 ` Jeff Garzik
2006-07-03 7:07 ` [PATCH 06/11] libata: reimplement per-dev PM Tejun Heo
` (9 subsequent siblings)
10 siblings, 1 reply; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Implement ATA_EHI_RESUME_LINK, which indicates that the link needs to
be resumed. This used to be implied by ATA_EHI_HOTPLUGGED. However,
hotplug isn't the only event which requires link resume and separating
this out allows other places to request link resume. This
differentiation also allows better debounce timing selection.
This patch converts user scan to use ATA_EHI_RESUME_LINK.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 15 ++++++++-------
drivers/scsi/libata-scsi.c | 1 +
include/linux/libata.h | 3 ++-
3 files changed, 11 insertions(+), 8 deletions(-)
11afe177206757095a8b265cd3649b629db45179
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 11f2257..3d6e41b 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -2619,13 +2619,14 @@ int ata_std_prereset(struct ata_port *ap
const unsigned long *timing;
int rc;
- /* hotplug? */
- if (ehc->i.flags & ATA_EHI_HOTPLUGGED) {
- if (ap->flags & ATA_FLAG_HRST_TO_RESUME)
- ehc->i.action |= ATA_EH_HARDRESET;
- if (ap->flags & ATA_FLAG_SKIP_D2H_BSY)
- ata_wait_spinup(ap);
- }
+ /* handle link resume & hotplug spinup */
+ if ((ehc->i.flags & ATA_EHI_RESUME_LINK) &&
+ (ap->flags & ATA_FLAG_HRST_TO_RESUME))
+ ehc->i.action |= ATA_EH_HARDRESET;
+
+ if ((ehc->i.flags & ATA_EHI_HOTPLUGGED) &&
+ (ap->flags & ATA_FLAG_SKIP_D2H_BSY))
+ ata_wait_spinup(ap);
/* if we're about to do hardreset, nothing more to do */
if (ehc->i.action & ATA_EH_HARDRESET)
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 1946979..1ed89d8 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -3013,6 +3013,7 @@ static int ata_scsi_user_scan(struct Scs
if (dev) {
ap->eh_info.probe_mask |= 1 << dev->devno;
ap->eh_info.action |= ATA_EH_SOFTRESET;
+ ap->eh_info.flags |= ATA_EHI_RESUME_LINK;
} else
rc = -EINVAL;
}
diff --git a/include/linux/libata.h b/include/linux/libata.h
index b5d247d..4d4ed2c 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -259,6 +259,7 @@ 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_DID_RESET = (1 << 16), /* already reset this port */
@@ -836,7 +837,7 @@ static inline void ata_ehi_hotplugged(st
if (ehi->flags & ATA_EHI_HOTPLUGGED)
return;
- ehi->flags |= ATA_EHI_HOTPLUGGED;
+ ehi->flags |= ATA_EHI_HOTPLUGGED | ATA_EHI_RESUME_LINK;
ehi->hotplug_timestamp = jiffies;
ehi->err_mask |= AC_ERR_ATA_BUS;
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 02/11] libata: clean up debounce parameters and improve parameter selection
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
` (2 preceding siblings ...)
2006-07-03 7:07 ` [PATCH 03/11] libata: implement ATA_EHI_NO_AUTOPSY and QUIET Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 04/11] libata: separate out __ata_ehi_hotplugged() Tejun Heo
` (6 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
The names of predefined debounce timing parameters didn't exactly
match their usages. Rename to more generic names and implement param
selection helper sata_ehc_deb_timing() which uses EHI_HOTPLUGGED to
select params.
Combined with the previous EHI_RESUME_LINK differentiation, this makes
parameter selection accurate. e.g. user scan resumes link but normal
deb param is used instead of hotplug param.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 25 +++++++++++--------------
drivers/scsi/sata_sil24.c | 2 +-
include/linux/libata.h | 15 ++++++++++++---
3 files changed, 24 insertions(+), 18 deletions(-)
c392a0ecdc783060afead43fef27bb8a939c02d3
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 3d6e41b..988f385 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -62,9 +62,9 @@ #include <asm/byteorder.h>
#include "libata.h"
/* debounce timing parameters in msecs { interval, duration, timeout } */
-const unsigned long sata_deb_timing_boot[] = { 5, 100, 2000 };
-const unsigned long sata_deb_timing_eh[] = { 25, 500, 2000 };
-const unsigned long sata_deb_timing_before_fsrst[] = { 100, 2000, 5000 };
+const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 };
+const unsigned long sata_deb_timing_hotplug[] = { 25, 500, 2000 };
+const unsigned long sata_deb_timing_long[] = { 100, 2000, 5000 };
static unsigned int ata_dev_init_params(struct ata_device *dev,
u16 heads, u16 sectors);
@@ -2580,7 +2580,7 @@ static void ata_wait_spinup(struct ata_p
/* first, debounce phy if SATA */
if (ap->cbl == ATA_CBL_SATA) {
- rc = sata_phy_debounce(ap, sata_deb_timing_eh);
+ rc = sata_phy_debounce(ap, sata_deb_timing_hotplug);
/* if debounced successfully and offline, no need to wait */
if ((rc == 0 || rc == -EOPNOTSUPP) && ata_port_offline(ap))
@@ -2616,7 +2616,7 @@ static void ata_wait_spinup(struct ata_p
int ata_std_prereset(struct ata_port *ap)
{
struct ata_eh_context *ehc = &ap->eh_context;
- const unsigned long *timing;
+ const unsigned long *timing = sata_ehc_deb_timing(ehc);
int rc;
/* handle link resume & hotplug spinup */
@@ -2634,11 +2634,6 @@ int ata_std_prereset(struct ata_port *ap
/* if SATA, resume phy */
if (ap->cbl == ATA_CBL_SATA) {
- if (ap->pflags & ATA_PFLAG_LOADING)
- timing = sata_deb_timing_boot;
- else
- timing = sata_deb_timing_eh;
-
rc = sata_phy_resume(ap, timing);
if (rc && rc != -EOPNOTSUPP) {
/* phy resume failed */
@@ -2726,6 +2721,8 @@ int ata_std_softreset(struct ata_port *a
*/
int sata_std_hardreset(struct ata_port *ap, unsigned int *class)
{
+ struct ata_eh_context *ehc = &ap->eh_context;
+ const unsigned long *timing = sata_ehc_deb_timing(ehc);
u32 scontrol;
int rc;
@@ -2763,7 +2760,7 @@ int sata_std_hardreset(struct ata_port *
msleep(1);
/* bring phy back */
- sata_phy_resume(ap, sata_deb_timing_eh);
+ sata_phy_resume(ap, timing);
/* TODO: phy layer with polling, timeouts, etc. */
if (ata_port_offline(ap)) {
@@ -5844,9 +5841,9 @@ u32 ata_wait_register(void __iomem *reg,
* Do not depend on ABI/API stability.
*/
-EXPORT_SYMBOL_GPL(sata_deb_timing_boot);
-EXPORT_SYMBOL_GPL(sata_deb_timing_eh);
-EXPORT_SYMBOL_GPL(sata_deb_timing_before_fsrst);
+EXPORT_SYMBOL_GPL(sata_deb_timing_normal);
+EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug);
+EXPORT_SYMBOL_GPL(sata_deb_timing_long);
EXPORT_SYMBOL_GPL(ata_std_bios_param);
EXPORT_SYMBOL_GPL(ata_std_ports);
EXPORT_SYMBOL_GPL(ata_device_add);
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index b5f8fa9..fa3268d 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -607,7 +607,7 @@ static int sil24_hardreset(struct ata_po
/* SStatus oscillates between zero and valid status after
* DEV_RST, debounce it.
*/
- rc = sata_phy_debounce(ap, sata_deb_timing_before_fsrst);
+ rc = sata_phy_debounce(ap, sata_deb_timing_long);
if (rc) {
reason = "PHY debouncing failed";
goto err;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 4d4ed2c..2f7bbfc 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -629,9 +629,18 @@ struct ata_timing {
#define FIT(v,vmin,vmax) max_t(short,min_t(short,v,vmax),vmin)
-extern const unsigned long sata_deb_timing_boot[];
-extern const unsigned long sata_deb_timing_eh[];
-extern const unsigned long sata_deb_timing_before_fsrst[];
+extern const unsigned long sata_deb_timing_normal[];
+extern const unsigned long sata_deb_timing_hotplug[];
+extern const unsigned long sata_deb_timing_long[];
+
+static inline const unsigned long *
+sata_ehc_deb_timing(struct ata_eh_context *ehc)
+{
+ if (ehc->i.flags & ATA_EHI_HOTPLUGGED)
+ return sata_deb_timing_hotplug;
+ else
+ return sata_deb_timing_normal;
+}
extern void ata_port_probe(struct ata_port *);
extern void __sata_phy_reset(struct ata_port *ap);
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 03/11] libata: implement ATA_EHI_NO_AUTOPSY and QUIET
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
2006-07-03 7:07 ` [PATCH 01/11] libata: implement ATA_EHI_RESUME_LINK Tejun Heo
2006-07-03 7:07 ` [PATCH 06/11] libata: reimplement per-dev PM Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 02/11] libata: clean up debounce parameters and improve parameter selection Tejun Heo
` (7 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Implement ATA_EHI_NO_AUTOPSY and QUIET. These used to be implied by
ATA_PFLAG_LOADING, but new power management and PMP support need to
use these separately. e.g. Suspend/resume operations shouldn't print
full EH messages and resume shouldn't be recorded as an error.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 6 ++++--
drivers/scsi/libata-eh.c | 31 +++++++++++++++++--------------
include/linux/libata.h | 2 ++
3 files changed, 23 insertions(+), 16 deletions(-)
64088c397150035e6d20cee81d83b00f82909bb5
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 988f385..5364be0 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -5439,6 +5439,7 @@ int ata_device_add(const struct ata_prob
}
if (ap->ops->error_handler) {
+ struct ata_eh_info *ehi = &ap->eh_info;
unsigned long flags;
ata_port_probe(ap);
@@ -5446,8 +5447,9 @@ int ata_device_add(const struct ata_prob
/* kick EH for boot probing */
spin_lock_irqsave(ap->lock, flags);
- ap->eh_info.probe_mask = (1 << ATA_MAX_DEVICES) - 1;
- ap->eh_info.action |= ATA_EH_SOFTRESET;
+ ehi->probe_mask = (1 << ATA_MAX_DEVICES) - 1;
+ ehi->action |= ATA_EH_SOFTRESET;
+ ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
ap->pflags |= ATA_PFLAG_LOADING;
ata_port_schedule_eh(ap);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index d19666c..1e9e73d 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -322,14 +322,13 @@ void ata_scsi_error(struct Scsi_Host *ho
/* clean up */
spin_lock_irqsave(ap->lock, flags);
- if (ap->pflags & ATA_PFLAG_LOADING) {
+ if (ap->pflags & ATA_PFLAG_LOADING)
ap->pflags &= ~ATA_PFLAG_LOADING;
- } else {
- if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG)
- queue_work(ata_aux_wq, &ap->hotplug_task);
- if (ap->pflags & ATA_PFLAG_RECOVERED)
- ata_port_printk(ap, KERN_INFO, "EH complete\n");
- }
+ else if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG)
+ queue_work(ata_aux_wq, &ap->hotplug_task);
+
+ if (ap->pflags & ATA_PFLAG_RECOVERED)
+ ata_port_printk(ap, KERN_INFO, "EH complete\n");
ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED);
@@ -759,8 +758,12 @@ static void ata_eh_about_to_do(struct at
unsigned long flags;
spin_lock_irqsave(ap->lock, flags);
+
ata_eh_clear_action(dev, &ap->eh_info, action);
- ap->pflags |= ATA_PFLAG_RECOVERED;
+
+ if (!(ap->eh_context.i.flags & ATA_EHI_QUIET))
+ ap->pflags |= ATA_PFLAG_RECOVERED;
+
spin_unlock_irqrestore(ap->lock, flags);
}
@@ -1274,6 +1277,9 @@ static void ata_eh_autopsy(struct ata_po
DPRINTK("ENTER\n");
+ if (ehc->i.flags & ATA_EHI_NO_AUTOPSY)
+ return;
+
/* obtain and analyze SError */
rc = sata_scr_read(ap, SCR_ERROR, &serror);
if (rc == 0) {
@@ -1464,7 +1470,7 @@ static int ata_eh_reset(struct ata_port
struct ata_eh_context *ehc = &ap->eh_context;
unsigned int *classes = ehc->classes;
int tries = ATA_EH_RESET_TRIES;
- int verbose = !(ap->pflags & ATA_PFLAG_LOADING);
+ int verbose = !(ehc->i.flags & ATA_EHI_QUIET);
unsigned int action;
ata_reset_fn_t reset;
int i, did_followup_srst, rc;
@@ -1907,11 +1913,8 @@ void ata_do_eh(struct ata_port *ap, ata_
ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
ata_postreset_fn_t postreset)
{
- if (!(ap->pflags & ATA_PFLAG_LOADING)) {
- ata_eh_autopsy(ap);
- ata_eh_report(ap);
- }
-
+ ata_eh_autopsy(ap);
+ ata_eh_report(ap);
ata_eh_recover(ap, prereset, softreset, hardreset, postreset);
ata_eh_finish(ap);
}
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 2f7bbfc..36938ae 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -260,6 +260,8 @@ 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_DID_RESET = (1 << 16), /* already reset this port */
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 05/11] libata: implement PM EH actions
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
` (4 preceding siblings ...)
2006-07-03 7:07 ` [PATCH 04/11] libata: separate out __ata_ehi_hotplugged() Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 07/11] libata: reimplement controller-wide PM Tejun Heo
` (4 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Implement two PM per-dev EH actions - ATA_EH_SUSPEND and
ATA_EH_RESUME. Each action puts the target device into suspended mode
and resumes from it respectively.
Once a device is put to suspended mode, no EH operations other than
RESUME is allowed on the device. The device will stay suspended till
it gets resumed and thus reset and revalidated. To implement this, a
new device state helper - ata_dev_ready() - is implemented and used in
EH action implementations to make them operate only on attached &
running devices.
If all possible devices on a port are suspended, reset is skipped too.
This prevents spurious events including hotplug events from disrupting
suspended devices.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 5 +
drivers/scsi/libata-eh.c | 185 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/libata.h | 12 +++
3 files changed, 198 insertions(+), 4 deletions(-)
f84525e6a5fa6f79bd150cb7c382dc2e43857ecd
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 5364be0..7b21803 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -2138,7 +2138,7 @@ int ata_set_mode(struct ata_port *ap, st
* return error code and failing device on failure.
*/
for (i = 0; i < ATA_MAX_DEVICES; i++) {
- if (ata_dev_enabled(&ap->device[i])) {
+ if (ata_dev_ready(&ap->device[i])) {
ap->ops->set_mode(ap);
break;
}
@@ -2204,7 +2204,8 @@ int ata_set_mode(struct ata_port *ap, st
for (i = 0; i < ATA_MAX_DEVICES; i++) {
dev = &ap->device[i];
- if (!ata_dev_enabled(dev))
+ /* don't udpate suspended devices' xfer mode */
+ if (!ata_dev_ready(dev))
continue;
rc = ata_dev_set_mode(dev);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 1e9e73d..b9df49a 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -1610,7 +1610,7 @@ static int ata_eh_revalidate_and_attach(
dev = &ap->device[i];
action = ata_eh_dev_action(dev);
- if (action & ATA_EH_REVALIDATE && ata_dev_enabled(dev)) {
+ if (action & ATA_EH_REVALIDATE && ata_dev_ready(dev)) {
if (ata_port_offline(ap)) {
rc = -EIO;
break;
@@ -1653,6 +1653,164 @@ static int ata_eh_revalidate_and_attach(
return rc;
}
+/**
+ * ata_eh_suspend - handle suspend EH action
+ * @ap: target host port
+ * @r_failed_dev: result parameter to indicate failing device
+ *
+ * Handle suspend EH action. Disk devices are spinned down and
+ * other types of devices are just marked suspended. Once
+ * suspended, no EH action to the device is allowed until it is
+ * resumed.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise
+ */
+static int ata_eh_suspend(struct ata_port *ap, struct ata_device **r_failed_dev)
+{
+ struct ata_device *dev;
+ int i, rc = 0;
+
+ DPRINTK("ENTER\n");
+
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ unsigned long flags;
+ unsigned int action, err_mask;
+
+ dev = &ap->device[i];
+ action = ata_eh_dev_action(dev);
+
+ if (!ata_dev_enabled(dev) || !(action & ATA_EH_SUSPEND))
+ continue;
+
+ WARN_ON(dev->flags & ATA_DFLAG_SUSPENDED);
+
+ ata_eh_about_to_do(ap, dev, ATA_EH_SUSPEND);
+
+ if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) {
+ /* flush cache */
+ rc = ata_flush_cache(dev);
+ if (rc)
+ break;
+
+ /* spin down */
+ err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1);
+ if (err_mask) {
+ ata_dev_printk(dev, KERN_ERR, "failed to "
+ "spin down (err_mask=0x%x)\n",
+ err_mask);
+ rc = -EIO;
+ break;
+ }
+ }
+
+ spin_lock_irqsave(ap->lock, flags);
+ dev->flags |= ATA_DFLAG_SUSPENDED;
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ ata_eh_done(ap, dev, ATA_EH_SUSPEND);
+ }
+
+ if (rc)
+ *r_failed_dev = dev;
+
+ DPRINTK("EXIT\n");
+ return 0;
+}
+
+/**
+ * ata_eh_prep_resume - prep for resume EH action
+ * @ap: target host port
+ *
+ * Clear SUSPENDED in preparation for scheduled resume actions.
+ * This allows other parts of EH to access the devices being
+ * resumed.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+static void ata_eh_prep_resume(struct ata_port *ap)
+{
+ struct ata_device *dev;
+ unsigned long flags;
+ int i;
+
+ DPRINTK("ENTER\n");
+
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ unsigned int action;
+
+ dev = &ap->device[i];
+ action = ata_eh_dev_action(dev);
+
+ if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME))
+ continue;
+
+ spin_lock_irqsave(ap->lock, flags);
+ dev->flags &= ~ATA_DFLAG_SUSPENDED;
+ spin_unlock_irqrestore(ap->lock, flags);
+ }
+
+ DPRINTK("EXIT\n");
+}
+
+/**
+ * ata_eh_resume - handle resume EH action
+ * @ap: target host port
+ * @r_failed_dev: result parameter to indicate failing device
+ *
+ * Handle resume EH action. Target devices are already reset and
+ * revalidated. Spinning up is the only operation left.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise
+ */
+static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev)
+{
+ struct ata_device *dev;
+ int i, rc = 0;
+
+ DPRINTK("ENTER\n");
+
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ unsigned int action, err_mask;
+
+ dev = &ap->device[i];
+ action = ata_eh_dev_action(dev);
+
+ if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME))
+ continue;
+
+ ata_eh_about_to_do(ap, dev, ATA_EH_RESUME);
+
+ if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) {
+ err_mask = ata_do_simple_cmd(dev,
+ ATA_CMD_IDLEIMMEDIATE);
+ if (err_mask) {
+ ata_dev_printk(dev, KERN_ERR, "failed to "
+ "spin up (err_mask=0x%x)\n",
+ err_mask);
+ rc = -EIO;
+ break;
+ }
+ }
+
+ ata_eh_done(ap, dev, ATA_EH_RESUME);
+ }
+
+ if (rc)
+ *r_failed_dev = dev;
+
+ DPRINTK("EXIT\n");
+ return 0;
+}
+
static int ata_port_nr_enabled(struct ata_port *ap)
{
int i, cnt = 0;
@@ -1678,6 +1836,18 @@ static int ata_eh_skip_recovery(struct a
struct ata_eh_context *ehc = &ap->eh_context;
int i;
+ /* skip if all possible devices are suspended */
+ for (i = 0; i < ata_port_max_devices(ap); i++) {
+ struct ata_device *dev = &ap->device[i];
+
+ if (ata_dev_absent(dev) || ata_dev_ready(dev))
+ break;
+ }
+
+ if (i == ata_port_max_devices(ap))
+ return 1;
+
+ /* always thaw frozen port and recover failed devices */
if (ap->pflags & ATA_PFLAG_FROZEN || ata_port_nr_enabled(ap))
return 0;
@@ -1752,6 +1922,9 @@ static int ata_eh_recover(struct ata_por
if (ap->pflags & ATA_PFLAG_UNLOADING)
goto out;
+ /* prep for resume */
+ ata_eh_prep_resume(ap);
+
/* skip EH if possible. */
if (ata_eh_skip_recovery(ap))
ehc->i.action = 0;
@@ -1779,6 +1952,11 @@ static int ata_eh_recover(struct ata_por
if (rc)
goto dev_fail;
+ /* resume devices */
+ rc = ata_eh_resume(ap, &dev);
+ if (rc)
+ goto dev_fail;
+
/* configure transfer mode if the port has been reset */
if (ehc->i.flags & ATA_EHI_DID_RESET) {
rc = ata_set_mode(ap, &dev);
@@ -1788,6 +1966,11 @@ static int ata_eh_recover(struct ata_por
}
}
+ /* suspend devices */
+ rc = ata_eh_suspend(ap, &dev);
+ if (rc)
+ goto dev_fail;
+
goto out;
dev_fail:
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 2aa1398..363c750 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -131,6 +131,7 @@ enum {
ATA_DFLAG_CFG_MASK = (1 << 8) - 1,
ATA_DFLAG_PIO = (1 << 8), /* device currently in PIO mode */
+ ATA_DFLAG_SUSPENDED = (1 << 9), /* device suspended */
ATA_DFLAG_INIT_MASK = (1 << 16) - 1,
ATA_DFLAG_DETACH = (1 << 16),
@@ -253,9 +254,13 @@ enum {
ATA_EH_REVALIDATE = (1 << 0),
ATA_EH_SOFTRESET = (1 << 1),
ATA_EH_HARDRESET = (1 << 2),
+ ATA_EH_SUSPEND = (1 << 3),
+ ATA_EH_RESUME = (1 << 4),
+ ATA_EH_PM_FREEZE = (1 << 5),
ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET,
- ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE,
+ ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE | ATA_EH_SUSPEND |
+ ATA_EH_RESUME | ATA_EH_PM_FREEZE,
/* ata_eh_info->flags */
ATA_EHI_HOTPLUGGED = (1 << 0), /* could have been hotplugged */
@@ -944,6 +949,11 @@ static inline unsigned int ata_dev_absen
return ata_class_absent(dev->class);
}
+static inline unsigned int ata_dev_ready(const struct ata_device *dev)
+{
+ return ata_dev_enabled(dev) && !(dev->flags & ATA_DFLAG_SUSPENDED);
+}
+
/*
* port helpers
*/
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 06/11] libata: reimplement per-dev PM
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
2006-07-03 7:07 ` [PATCH 01/11] libata: implement ATA_EHI_RESUME_LINK Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 03/11] libata: implement ATA_EHI_NO_AUTOPSY and QUIET Tejun Heo
` (8 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Reimplement per-dev PM. The original implementation directly put the
device into suspended mode and didn't synchronize w/ EH operations
including hotplug. This patch reimplements ata_scsi_device_suspend()
and ata_scsi_device_resume() such that they request EH to perform the
respective operations. Both functions synchronize with hotplug such
that it doesn't operate on detached devices.
Suspend waits for completion but resume just issues request and
returns. This allows parallel wake up of devices and thus speeds up
system resume.
Due to sdev detach synchronization, it's not feasible to separate out
EH requesting from sdev handling; thus, ata_device_suspend/resume()
are removed and everything is implemented in the respective
libata-scsi functions.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 84 -------------------------------
drivers/scsi/libata-scsi.c | 121 ++++++++++++++++++++++++++++++++++++++++++--
include/linux/libata.h | 2 -
3 files changed, 115 insertions(+), 92 deletions(-)
99372db3336092bfb6d6dd3663a5d8dfec43da6b
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 7b21803..9703cbf 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -5001,88 +5001,6 @@ int ata_flush_cache(struct ata_device *d
return 0;
}
-static int ata_standby_drive(struct ata_device *dev)
-{
- unsigned int err_mask;
-
- err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1);
- if (err_mask) {
- ata_dev_printk(dev, KERN_ERR, "failed to standby drive "
- "(err_mask=0x%x)\n", err_mask);
- return -EIO;
- }
-
- return 0;
-}
-
-static int ata_start_drive(struct ata_device *dev)
-{
- unsigned int err_mask;
-
- err_mask = ata_do_simple_cmd(dev, ATA_CMD_IDLEIMMEDIATE);
- if (err_mask) {
- ata_dev_printk(dev, KERN_ERR, "failed to start drive "
- "(err_mask=0x%x)\n", err_mask);
- return -EIO;
- }
-
- return 0;
-}
-
-/**
- * ata_device_resume - wakeup a previously suspended devices
- * @dev: the device to resume
- *
- * Kick the drive back into action, by sending it an idle immediate
- * command and making sure its transfer mode matches between drive
- * and host.
- *
- */
-int ata_device_resume(struct ata_device *dev)
-{
- struct ata_port *ap = dev->ap;
-
- if (ap->pflags & ATA_PFLAG_SUSPENDED) {
- struct ata_device *failed_dev;
-
- ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
- ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 200000);
-
- ap->pflags &= ~ATA_PFLAG_SUSPENDED;
- while (ata_set_mode(ap, &failed_dev))
- ata_dev_disable(failed_dev);
- }
- if (!ata_dev_enabled(dev))
- return 0;
- if (dev->class == ATA_DEV_ATA)
- ata_start_drive(dev);
-
- return 0;
-}
-
-/**
- * ata_device_suspend - prepare a device for suspend
- * @dev: the device to suspend
- * @state: target power management state
- *
- * Flush the cache on the drive, if appropriate, then issue a
- * standbynow command.
- */
-int ata_device_suspend(struct ata_device *dev, pm_message_t state)
-{
- struct ata_port *ap = dev->ap;
-
- if (!ata_dev_enabled(dev))
- return 0;
- if (dev->class == ATA_DEV_ATA)
- ata_flush_cache(dev);
-
- if (state.event != PM_EVENT_FREEZE)
- ata_standby_drive(dev);
- ap->pflags |= ATA_PFLAG_SUSPENDED;
- return 0;
-}
-
/**
* ata_port_start - Set port up for dma.
* @ap: Port to initialize
@@ -5938,8 +5856,6 @@ EXPORT_SYMBOL_GPL(ata_pci_default_filter
EXPORT_SYMBOL_GPL(ata_pci_clear_simplex);
#endif /* CONFIG_PCI */
-EXPORT_SYMBOL_GPL(ata_device_suspend);
-EXPORT_SYMBOL_GPL(ata_device_resume);
EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 1ed89d8..31d30c6 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -399,20 +399,129 @@ void ata_dump_status(unsigned id, struct
}
}
-int ata_scsi_device_resume(struct scsi_device *sdev)
+/**
+ * ata_scsi_device_suspend - suspend ATA device associated with sdev
+ * @sdev: the SCSI device to suspend
+ * @state: target power management state
+ *
+ * Request suspend EH action on the ATA device associated with
+ * @sdev and wait for the operation to complete.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
- struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
+ struct ata_device *dev = ata_scsi_find_dev(ap, sdev);
+ unsigned long flags;
+ unsigned int action;
+ int rc = 0;
+
+ if (!dev)
+ goto out;
+
+ spin_lock_irqsave(ap->lock, flags);
+
+ /* wait for the previous resume to complete */
+ while (dev->flags & ATA_DFLAG_SUSPENDED) {
+ spin_unlock_irqrestore(ap->lock, flags);
+ ata_port_wait_eh(ap);
+ spin_lock_irqsave(ap->lock, flags);
+ }
+
+ /* if @sdev is already detached, nothing to do */
+ if (sdev->sdev_state == SDEV_OFFLINE ||
+ sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL)
+ goto out_unlock;
+
+ /* request suspend */
+ action = ATA_EH_SUSPEND;
+ if (state.event != PM_EVENT_SUSPEND)
+ action |= ATA_EH_PM_FREEZE;
+ ap->eh_info.dev_action[dev->devno] |= action;
+ ap->eh_info.flags |= ATA_EHI_QUIET;
+ ata_port_schedule_eh(ap);
+
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ /* wait for EH to do the job */
+ ata_port_wait_eh(ap);
+
+ spin_lock_irqsave(ap->lock, flags);
+
+ /* If @sdev is still attached but the associated ATA device
+ * isn't suspended, the operation failed.
+ */
+ if (sdev->sdev_state != SDEV_OFFLINE &&
+ sdev->sdev_state != SDEV_CANCEL && sdev->sdev_state != SDEV_DEL &&
+ !(dev->flags & ATA_DFLAG_SUSPENDED))
+ rc = -EIO;
- return ata_device_resume(dev);
+ out_unlock:
+ spin_unlock_irqrestore(ap->lock, flags);
+ out:
+ if (rc == 0)
+ sdev->sdev_gendev.power.power_state = state;
+ return rc;
}
-int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state)
+/**
+ * ata_scsi_device_resume - resume ATA device associated with sdev
+ * @sdev: the SCSI device to resume
+ *
+ * Request resume EH action on the ATA device associated with
+ * @sdev and return immediately. This enables parallel
+ * wakeup/spinup of devices.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0.
+ */
+int ata_scsi_device_resume(struct scsi_device *sdev)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
- struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
+ struct ata_device *dev = ata_scsi_find_dev(ap, sdev);
+ struct ata_eh_info *ehi = &ap->eh_info;
+ unsigned long flags;
+ unsigned int action;
+
+ if (!dev)
+ goto out;
+
+ spin_lock_irqsave(ap->lock, flags);
+
+ /* if @sdev is already detached, nothing to do */
+ if (sdev->sdev_state == SDEV_OFFLINE ||
+ sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL)
+ goto out_unlock;
- return ata_device_suspend(dev, state);
+ /* request resume */
+ action = ATA_EH_RESUME;
+ if (sdev->sdev_gendev.power.power_state.event == PM_EVENT_SUSPEND)
+ __ata_ehi_hotplugged(ehi);
+ else
+ action |= ATA_EH_PM_FREEZE | ATA_EH_SOFTRESET;
+ ehi->dev_action[dev->devno] |= action;
+
+ /* We don't want autopsy and verbose EH messages. Disable
+ * those if we're the only device on this link.
+ */
+ if (ata_port_max_devices(ap) == 1)
+ ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
+
+ ata_port_schedule_eh(ap);
+
+ out_unlock:
+ spin_unlock_irqrestore(ap->lock, flags);
+ out:
+ sdev->sdev_gendev.power.power_state = PMSG_ON;
+ return 0;
}
/**
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 363c750..5ac2626 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -687,8 +687,6 @@ extern int ata_port_online(struct ata_po
extern int ata_port_offline(struct ata_port *ap);
extern int ata_scsi_device_resume(struct scsi_device *);
extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t state);
-extern int ata_device_resume(struct ata_device *);
-extern int ata_device_suspend(struct ata_device *, pm_message_t state);
extern int ata_ratelimit(void);
extern unsigned int ata_busy_sleep(struct ata_port *ap,
unsigned long timeout_pat,
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 11/11] sata_sil24: add suspend/sleep support
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
` (9 preceding siblings ...)
2006-07-03 7:07 ` [PATCH 10/11] sata_sil24: separate out sil24_init_controller() Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Add suspend/sleep support.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 25 +++++++++++++++++++++++++
1 files changed, 25 insertions(+), 0 deletions(-)
44ecebd2a8841e439bea118f9014f509ce05b553
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 16565e2..b8e590d 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -92,6 +92,7 @@ enum {
HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */
HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */
HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */
+ HOST_CTRL_GLOBAL_RST = (1 << 31), /* global reset */
/*
* Port registers
@@ -338,6 +339,7 @@ static int sil24_port_start(struct ata_p
static void sil24_port_stop(struct ata_port *ap);
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 const struct pci_device_id sil24_pci_tbl[] = {
{ 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 },
@@ -353,6 +355,8 @@ static struct pci_driver sil24_pci_drive
.id_table = sil24_pci_tbl,
.probe = sil24_init_one,
.remove = ata_pci_remove_one, /* safe? */
+ .suspend = ata_pci_device_suspend,
+ .resume = sil24_pci_device_resume,
};
static struct scsi_host_template sil24_sht = {
@@ -372,6 +376,8 @@ static struct scsi_host_template sil24_s
.slave_configure = ata_scsi_slave_config,
.slave_destroy = ata_scsi_slave_destroy,
.bios_param = ata_std_bios_param,
+ .suspend = ata_scsi_device_suspend,
+ .resume = ata_scsi_device_resume,
};
static const struct ata_port_operations sil24_ops = {
@@ -1179,6 +1185,25 @@ static int sil24_init_one(struct pci_dev
return rc;
}
+static int sil24_pci_device_resume(struct pci_dev *pdev)
+{
+ struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+ struct sil24_host_priv *hpriv = host_set->private_data;
+
+ ata_pci_device_do_resume(pdev);
+
+ if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
+ writel(HOST_CTRL_GLOBAL_RST, hpriv->host_base + HOST_CTRL);
+
+ sil24_init_controller(pdev, host_set->n_ports,
+ host_set->ports[0]->flags,
+ hpriv->host_base, hpriv->port_base);
+
+ ata_host_set_resume(host_set);
+
+ return 0;
+}
+
static int __init sil24_init(void)
{
return pci_module_init(&sil24_pci_driver);
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 09/11] sata_sil: add suspend/sleep support
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
` (7 preceding siblings ...)
2006-07-03 7:07 ` [PATCH 08/11] sata_sil: separate out sil_init_controller() Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 10/11] sata_sil24: separate out sil24_init_controller() Tejun Heo
2006-07-03 7:07 ` [PATCH 11/11] sata_sil24: add suspend/sleep support Tejun Heo
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Add suspend/sleep support.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
05e29ec2fd515c78304633c314785393db137b4b
diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
index 2bbc463..9f2a48a 100644
--- a/drivers/scsi/sata_sil.c
+++ b/drivers/scsi/sata_sil.c
@@ -109,6 +109,7 @@ enum {
};
static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static int sil_pci_device_resume(struct pci_dev *pdev);
static void sil_dev_config(struct ata_port *ap, struct ata_device *dev);
static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
@@ -160,6 +161,8 @@ static struct pci_driver sil_pci_driver
.id_table = sil_pci_tbl,
.probe = sil_init_one,
.remove = ata_pci_remove_one,
+ .suspend = ata_pci_device_suspend,
+ .resume = sil_pci_device_resume,
};
static struct scsi_host_template sil_sht = {
@@ -178,6 +181,8 @@ static struct scsi_host_template sil_sht
.slave_configure = ata_scsi_slave_config,
.slave_destroy = ata_scsi_slave_destroy,
.bios_param = ata_std_bios_param,
+ .suspend = ata_scsi_device_suspend,
+ .resume = ata_scsi_device_resume,
};
static const struct ata_port_operations sil_ops = {
@@ -695,6 +700,18 @@ err_out:
return rc;
}
+static int sil_pci_device_resume(struct pci_dev *pdev)
+{
+ struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+
+ ata_pci_device_do_resume(pdev);
+ sil_init_controller(pdev, host_set->n_ports, host_set->ports[0]->flags,
+ host_set->mmio_base);
+ ata_host_set_resume(host_set);
+
+ return 0;
+}
+
static int __init sil_init(void)
{
return pci_module_init(&sil_pci_driver);
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 10/11] sata_sil24: separate out sil24_init_controller()
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
` (8 preceding siblings ...)
2006-07-03 7:07 ` [PATCH 09/11] sata_sil: add suspend/sleep support Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 11/11] sata_sil24: add suspend/sleep support Tejun Heo
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Separate out controller initialization from sil24_init_one() into
sil24_init_controller(). This will be used by resume.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 107 ++++++++++++++++++++++++++-------------------
1 files changed, 62 insertions(+), 45 deletions(-)
efd4e54c03974c6c924c3123b612f589b51ab9b1
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index fa3268d..16565e2 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -988,6 +988,64 @@ static void sil24_host_stop(struct ata_h
kfree(hpriv);
}
+static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
+ unsigned long host_flags,
+ void __iomem *host_base,
+ void __iomem *port_base)
+{
+ u32 tmp;
+ int i;
+
+ /* GPIO off */
+ writel(0, host_base + HOST_FLASH_CMD);
+
+ /* clear global reset & mask interrupts during initialization */
+ writel(0, host_base + HOST_CTRL);
+
+ /* init ports */
+ for (i = 0; i < n_ports; i++) {
+ void __iomem *port = port_base + i * PORT_REGS_SIZE;
+
+ /* Initial PHY setting */
+ writel(0x20c, port + PORT_PHY_CFG);
+
+ /* Clear port RST */
+ tmp = readl(port + PORT_CTRL_STAT);
+ if (tmp & PORT_CS_PORT_RST) {
+ writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
+ tmp = ata_wait_register(port + PORT_CTRL_STAT,
+ PORT_CS_PORT_RST,
+ PORT_CS_PORT_RST, 10, 100);
+ if (tmp & PORT_CS_PORT_RST)
+ dev_printk(KERN_ERR, &pdev->dev,
+ "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_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
+ }
+
+ /* Turn on interrupts */
+ writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
+}
+
static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
@@ -1076,9 +1134,6 @@ static int sil24_init_one(struct pci_dev
}
}
- /* GPIO off */
- writel(0, host_base + HOST_FLASH_CMD);
-
/* Apply workaround for completion IRQ loss on PCI-X errata */
if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) {
tmp = readl(host_base + HOST_CTRL);
@@ -1090,56 +1145,18 @@ static int sil24_init_one(struct pci_dev
probe_ent->host_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
}
- /* clear global reset & mask interrupts during initialization */
- writel(0, host_base + HOST_CTRL);
-
for (i = 0; i < probe_ent->n_ports; i++) {
- void __iomem *port = port_base + i * PORT_REGS_SIZE;
- unsigned long portu = (unsigned long)port;
+ unsigned long portu =
+ (unsigned long)port_base + i * PORT_REGS_SIZE;
probe_ent->port[i].cmd_addr = portu;
probe_ent->port[i].scr_addr = portu + PORT_SCONTROL;
ata_std_ports(&probe_ent->port[i]);
-
- /* Initial PHY setting */
- writel(0x20c, port + PORT_PHY_CFG);
-
- /* Clear port RST */
- tmp = readl(port + PORT_CTRL_STAT);
- if (tmp & PORT_CS_PORT_RST) {
- writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
- tmp = ata_wait_register(port + PORT_CTRL_STAT,
- PORT_CS_PORT_RST,
- PORT_CS_PORT_RST, 10, 100);
- if (tmp & PORT_CS_PORT_RST)
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to clear port RST\n");
- }
-
- /* Configure IRQ WoC */
- if (probe_ent->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_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
}
- /* Turn on interrupts */
- writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
+ sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags,
+ host_base, port_base);
pci_set_master(pdev);
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 08/11] sata_sil: separate out sil_init_controller()
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
` (6 preceding siblings ...)
2006-07-03 7:07 ` [PATCH 07/11] libata: reimplement controller-wide PM Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 09/11] sata_sil: add suspend/sleep support Tejun Heo
` (2 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Separate out controller initialization from sil_init_one() into
sil_init_controller(). This will be used by resume.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil.c | 86 ++++++++++++++++++++++++++---------------------
1 files changed, 48 insertions(+), 38 deletions(-)
182530869d14d9ec9aae527dcccb64e6cacc1f74
diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
index 5d52705..2bbc463 100644
--- a/drivers/scsi/sata_sil.c
+++ b/drivers/scsi/sata_sil.c
@@ -561,6 +561,52 @@ static void sil_dev_config(struct ata_po
}
}
+static void sil_init_controller(struct pci_dev *pdev,
+ int n_ports, unsigned long host_flags,
+ void __iomem *mmio_base)
+{
+ u8 cls;
+ u32 tmp;
+ int i;
+
+ /* Initialize FIFO PCI bus arbitration */
+ cls = sil_get_device_cache_line(pdev);
+ if (cls) {
+ cls >>= 3;
+ cls++; /* cls = (line_size/8)+1 */
+ for (i = 0; i < n_ports; i++)
+ writew(cls << 8 | cls,
+ mmio_base + sil_port[i].fifo_cfg);
+ } else
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "cache line size not set. Driver may not function\n");
+
+ /* Apply R_ERR on DMA activate FIS errata workaround */
+ if (host_flags & SIL_FLAG_RERR_ON_DMA_ACT) {
+ int cnt;
+
+ for (i = 0, cnt = 0; i < n_ports; i++) {
+ tmp = readl(mmio_base + sil_port[i].sfis_cfg);
+ if ((tmp & 0x3) != 0x01)
+ continue;
+ if (!cnt)
+ dev_printk(KERN_INFO, &pdev->dev,
+ "Applying R_ERR on DMA activate "
+ "FIS errata fix\n");
+ writel(tmp & ~0x3, mmio_base + sil_port[i].sfis_cfg);
+ cnt++;
+ }
+ }
+
+ if (n_ports == 4) {
+ /* flip the magic "make 4 ports work" bit */
+ tmp = readl(mmio_base + sil_port[2].bmdma);
+ if ((tmp & SIL_INTR_STEERING) == 0)
+ writel(tmp | SIL_INTR_STEERING,
+ mmio_base + sil_port[2].bmdma);
+ }
+}
+
static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
@@ -570,8 +616,6 @@ static int sil_init_one (struct pci_dev
int rc;
unsigned int i;
int pci_dev_busy = 0;
- u32 tmp;
- u8 cls;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
@@ -630,42 +674,8 @@ static int sil_init_one (struct pci_dev
ata_std_ports(&probe_ent->port[i]);
}
- /* Initialize FIFO PCI bus arbitration */
- cls = sil_get_device_cache_line(pdev);
- if (cls) {
- cls >>= 3;
- cls++; /* cls = (line_size/8)+1 */
- for (i = 0; i < probe_ent->n_ports; i++)
- writew(cls << 8 | cls,
- mmio_base + sil_port[i].fifo_cfg);
- } else
- dev_printk(KERN_WARNING, &pdev->dev,
- "cache line size not set. Driver may not function\n");
-
- /* Apply R_ERR on DMA activate FIS errata workaround */
- if (probe_ent->host_flags & SIL_FLAG_RERR_ON_DMA_ACT) {
- int cnt;
-
- for (i = 0, cnt = 0; i < probe_ent->n_ports; i++) {
- tmp = readl(mmio_base + sil_port[i].sfis_cfg);
- if ((tmp & 0x3) != 0x01)
- continue;
- if (!cnt)
- dev_printk(KERN_INFO, &pdev->dev,
- "Applying R_ERR on DMA activate "
- "FIS errata fix\n");
- writel(tmp & ~0x3, mmio_base + sil_port[i].sfis_cfg);
- cnt++;
- }
- }
-
- if (ent->driver_data == sil_3114) {
- /* flip the magic "make 4 ports work" bit */
- tmp = readl(mmio_base + sil_port[2].bmdma);
- if ((tmp & SIL_INTR_STEERING) == 0)
- writel(tmp | SIL_INTR_STEERING,
- mmio_base + sil_port[2].bmdma);
- }
+ sil_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags,
+ mmio_base);
pci_set_master(pdev);
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 07/11] libata: reimplement controller-wide PM
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
` (5 preceding siblings ...)
2006-07-03 7:07 ` [PATCH 05/11] libata: implement PM EH actions Tejun Heo
@ 2006-07-03 7:07 ` Tejun Heo
2006-07-03 7:07 ` [PATCH 08/11] sata_sil: separate out sil_init_controller() Tejun Heo
` (3 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2006-07-03 7:07 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
Reimplement controller-wide PM. ata_host_set_suspend/resume() are
defined to suspend and resume a host_set. While suspended, EHs for
all ports in the host_set are pegged using ATA_FLAG_SUSPENDED and
frozen.
Because SCSI device hotplug is done asynchronously against the rest of
libata EH and the same mutex is used when adding new device, suspend
cannot wait for hotplug to complete. So, if SCSI device hotplug is in
progress, suspend fails with -EBUSY.
In most cases, host_set resume is followed by device resume. As each
resume operation requires a reset, a single host_set-wide resume
operation may result in multiple resets. To avoid this, resume waits
upto 1 second giving PM to request resume for devices.
There is no room left for additional port flag, but several bits are
schedueled to be removed. This patch adds ATA_FLAG_RESUME as bit 31
which is reserved for LLD flags. No in-kernel LLD uses bit 31 now, so
this works for the time being and we can change it once some of the
obsolete bits are removed.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 165 +++++++++++++++++++++++++++++++++++++++++++-
drivers/scsi/libata-eh.c | 128 ++++++++++++++++++++++++++++++++++
include/linux/libata.h | 12 +++
3 files changed, 298 insertions(+), 7 deletions(-)
9e3f4adb6a59f90f0e894ae9cd5332841c6d6333
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 9703cbf..46ae4bc 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -5001,6 +5001,122 @@ int ata_flush_cache(struct ata_device *d
return 0;
}
+static int ata_host_set_request_pm(struct ata_host_set *host_set,
+ pm_message_t mesg, unsigned int action,
+ unsigned int ehi_flags, int wait)
+{
+ unsigned long flags;
+ int i, rc;
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ struct ata_port *ap = host_set->ports[i];
+
+ /* Previous resume operation might still be in
+ * progress. Wait for PM_PENDING to clear.
+ */
+ if (ap->pflags & ATA_PFLAG_PM_PENDING) {
+ ata_port_wait_eh(ap);
+ WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
+ }
+
+ /* request PM ops to EH */
+ spin_lock_irqsave(ap->lock, flags);
+
+ ap->pm_mesg = mesg;
+ if (wait) {
+ rc = 0;
+ ap->pm_result = &rc;
+ }
+
+ ap->pflags |= ATA_PFLAG_PM_PENDING;
+ ap->eh_info.action |= action;
+ ap->eh_info.flags |= ehi_flags;
+
+ ata_port_schedule_eh(ap);
+
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ /* wait and check result */
+ if (wait) {
+ ata_port_wait_eh(ap);
+ WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ata_host_set_suspend - suspend host_set
+ * @host_set: host_set to suspend
+ * @mesg: PM message
+ *
+ * Suspend @host_set. Actual operation is performed by EH. This
+ * function requests EH to perform PM operations and waits for EH
+ * to finish.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int ata_host_set_suspend(struct ata_host_set *host_set, pm_message_t mesg)
+{
+ int i, j, rc;
+
+ rc = ata_host_set_request_pm(host_set, mesg, 0, ATA_EHI_QUIET, 1);
+ if (rc)
+ goto fail;
+
+ /* EH is quiescent now. Fail if we have any ready device.
+ * This happens if hotplug occurs between completion of device
+ * suspension and here.
+ */
+ for (i = 0; i < host_set->n_ports; i++) {
+ struct ata_port *ap = host_set->ports[i];
+
+ for (j = 0; j < ATA_MAX_DEVICES; j++) {
+ struct ata_device *dev = &ap->device[j];
+
+ if (ata_dev_ready(dev)) {
+ ata_port_printk(ap, KERN_WARNING,
+ "suspend failed, device %d "
+ "still active\n", dev->devno);
+ rc = -EBUSY;
+ goto fail;
+ }
+ }
+ }
+
+ host_set->dev->power.power_state = mesg;
+ return 0;
+
+ fail:
+ ata_host_set_resume(host_set);
+ return rc;
+}
+
+/**
+ * ata_host_set_resume - resume host_set
+ * @host_set: host_set to resume
+ *
+ * Resume @host_set. Actual operation is performed by EH. This
+ * function requests EH to perform PM operations and returns.
+ * Note that all resume operations are performed parallely.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_host_set_resume(struct ata_host_set *host_set)
+{
+ ata_host_set_request_pm(host_set, PMSG_ON, ATA_EH_SOFTRESET,
+ ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
+ host_set->dev->power.power_state = PMSG_ON;
+}
+
/**
* ata_port_start - Set port up for dma.
* @ap: Port to initialize
@@ -5643,20 +5759,55 @@ int pci_test_config_bits(struct pci_dev
return (tmp == bits->val) ? 1 : 0;
}
-int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state)
{
pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
- return 0;
+
+ if (state.event == PM_EVENT_SUSPEND) {
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+ }
}
-int ata_pci_device_resume(struct pci_dev *pdev)
+void ata_pci_device_do_resume(struct pci_dev *pdev)
{
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
pci_enable_device(pdev);
pci_set_master(pdev);
+}
+
+int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+ int rc = 0;
+
+ rc = ata_host_set_suspend(host_set, state);
+ if (rc)
+ return rc;
+
+ if (host_set->next) {
+ rc = ata_host_set_suspend(host_set->next, state);
+ if (rc) {
+ ata_host_set_resume(host_set);
+ return rc;
+ }
+ }
+
+ ata_pci_device_do_suspend(pdev, state);
+
+ return 0;
+}
+
+int ata_pci_device_resume(struct pci_dev *pdev)
+{
+ struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+
+ ata_pci_device_do_resume(pdev);
+ ata_host_set_resume(host_set);
+ if (host_set->next)
+ ata_host_set_resume(host_set->next);
+
return 0;
}
#endif /* CONFIG_PCI */
@@ -5836,6 +5987,8 @@ EXPORT_SYMBOL_GPL(sata_scr_write);
EXPORT_SYMBOL_GPL(sata_scr_write_flush);
EXPORT_SYMBOL_GPL(ata_port_online);
EXPORT_SYMBOL_GPL(ata_port_offline);
+EXPORT_SYMBOL_GPL(ata_host_set_suspend);
+EXPORT_SYMBOL_GPL(ata_host_set_resume);
EXPORT_SYMBOL_GPL(ata_id_string);
EXPORT_SYMBOL_GPL(ata_id_c_string);
EXPORT_SYMBOL_GPL(ata_scsi_simulate);
@@ -5850,6 +6003,8 @@ EXPORT_SYMBOL_GPL(ata_pci_host_stop);
EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
EXPORT_SYMBOL_GPL(ata_pci_init_one);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
+EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend);
+EXPORT_SYMBOL_GPL(ata_pci_device_do_resume);
EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
EXPORT_SYMBOL_GPL(ata_pci_device_resume);
EXPORT_SYMBOL_GPL(ata_pci_default_filter);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index b9df49a..4b6aa30 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -47,6 +47,8 @@ #include "libata.h"
static void __ata_port_freeze(struct ata_port *ap);
static void ata_eh_finish(struct ata_port *ap);
+static void ata_eh_handle_port_suspend(struct ata_port *ap);
+static void ata_eh_handle_port_resume(struct ata_port *ap);
static void ata_ering_record(struct ata_ering *ering, int is_io,
unsigned int err_mask)
@@ -262,6 +264,9 @@ void ata_scsi_error(struct Scsi_Host *ho
repeat:
/* invoke error handler */
if (ap->ops->error_handler) {
+ /* process port resume request */
+ ata_eh_handle_port_resume(ap);
+
/* fetch & clear EH info */
spin_lock_irqsave(ap->lock, flags);
@@ -274,12 +279,15 @@ void ata_scsi_error(struct Scsi_Host *ho
spin_unlock_irqrestore(ap->lock, flags);
- /* invoke EH. if unloading, just finish failed qcs */
- if (!(ap->pflags & ATA_PFLAG_UNLOADING))
+ /* invoke EH, skip if unloading or suspended */
+ if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
ap->ops->error_handler(ap);
else
ata_eh_finish(ap);
+ /* process port suspend request */
+ ata_eh_handle_port_suspend(ap);
+
/* Exception might have happend after ->error_handler
* recovered the port but before this point. Repeat
* EH in such case.
@@ -2101,3 +2109,119 @@ void ata_do_eh(struct ata_port *ap, ata_
ata_eh_recover(ap, prereset, softreset, hardreset, postreset);
ata_eh_finish(ap);
}
+
+/**
+ * ata_eh_handle_port_suspend - perform port suspend operation
+ * @ap: port to suspend
+ *
+ * Suspend @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+static void ata_eh_handle_port_suspend(struct ata_port *ap)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ /* are we suspending? */
+ spin_lock_irqsave(ap->lock, flags);
+ if (!(ap->pflags & ATA_PFLAG_PM_PENDING) ||
+ ap->pm_mesg.event == PM_EVENT_ON) {
+ spin_unlock_irqrestore(ap->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED);
+
+ /* suspend */
+ ata_eh_freeze_port(ap);
+
+ if (ap->ops->port_suspend)
+ rc = ap->ops->port_suspend(ap, ap->pm_mesg);
+
+ /* report result */
+ spin_lock_irqsave(ap->lock, flags);
+
+ ap->pflags &= ~ATA_PFLAG_PM_PENDING;
+ if (rc == 0)
+ ap->pflags |= ATA_PFLAG_SUSPENDED;
+ else
+ ata_port_schedule_eh(ap);
+
+ if (ap->pm_result) {
+ *ap->pm_result = rc;
+ ap->pm_result = NULL;
+ }
+
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ return;
+}
+
+/**
+ * ata_eh_handle_port_resume - perform port resume operation
+ * @ap: port to resume
+ *
+ * Resume @ap.
+ *
+ * This function also waits upto one second until all devices
+ * hanging off this port requests resume EH action. This is to
+ * prevent invoking EH and thus reset multiple times on resume.
+ *
+ * On DPM resume, where some of devices might not be resumed
+ * together, this may delay port resume upto one second, but such
+ * DPM resumes are rare and 1 sec delay isn't too bad.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+static void ata_eh_handle_port_resume(struct ata_port *ap)
+{
+ unsigned long timeout;
+ unsigned long flags;
+ int i, rc = 0;
+
+ /* are we resuming? */
+ spin_lock_irqsave(ap->lock, flags);
+ if (!(ap->pflags & ATA_PFLAG_PM_PENDING) ||
+ ap->pm_mesg.event != PM_EVENT_ON) {
+ spin_unlock_irqrestore(ap->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ /* spurious? */
+ if (!(ap->pflags & ATA_PFLAG_SUSPENDED))
+ goto done;
+
+ if (ap->ops->port_resume)
+ rc = ap->ops->port_resume(ap);
+
+ /* give devices time to request EH */
+ timeout = jiffies + HZ; /* 1s max */
+ while (1) {
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ struct ata_device *dev = &ap->device[i];
+ unsigned int action = ata_eh_dev_action(dev);
+
+ if ((dev->flags & ATA_DFLAG_SUSPENDED) &&
+ !(action & ATA_EH_RESUME))
+ break;
+ }
+
+ if (i == ATA_MAX_DEVICES || time_after(jiffies, timeout))
+ break;
+ msleep(10);
+ }
+
+ done:
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
+ if (ap->pm_result) {
+ *ap->pm_result = rc;
+ ap->pm_result = NULL;
+ }
+ spin_unlock_irqrestore(ap->lock, flags);
+}
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 5ac2626..6cc497a 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -182,6 +182,7 @@ enum {
ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */
ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */
+ ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */
/* struct ata_queued_cmd flags */
ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */
@@ -549,6 +550,9 @@ struct ata_port {
struct list_head eh_done_q;
wait_queue_head_t eh_wait_q;
+ pm_message_t pm_mesg;
+ int *pm_result;
+
void *private_data;
u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */
@@ -603,6 +607,9 @@ struct ata_port_operations {
void (*scr_write) (struct ata_port *ap, unsigned int sc_reg,
u32 val);
+ int (*port_suspend) (struct ata_port *ap, pm_message_t mesg);
+ int (*port_resume) (struct ata_port *ap);
+
int (*port_start) (struct ata_port *ap);
void (*port_stop) (struct ata_port *ap);
@@ -667,6 +674,8 @@ #ifdef CONFIG_PCI
extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
unsigned int n_ports);
extern void ata_pci_remove_one (struct pci_dev *pdev);
+extern void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state);
+extern void ata_pci_device_do_resume(struct pci_dev *pdev);
extern int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state);
extern int ata_pci_device_resume(struct pci_dev *pdev);
extern int ata_pci_clear_simplex(struct pci_dev *pdev);
@@ -687,6 +696,9 @@ extern int ata_port_online(struct ata_po
extern int ata_port_offline(struct ata_port *ap);
extern int ata_scsi_device_resume(struct scsi_device *);
extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t state);
+extern int ata_host_set_suspend(struct ata_host_set *host_set,
+ pm_message_t mesg);
+extern void ata_host_set_resume(struct ata_host_set *host_set);
extern int ata_ratelimit(void);
extern unsigned int ata_busy_sleep(struct ata_port *ap,
unsigned long timeout_pat,
--
1.3.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 01/11] libata: implement ATA_EHI_RESUME_LINK
2006-07-03 7:07 ` [PATCH 01/11] libata: implement ATA_EHI_RESUME_LINK Tejun Heo
@ 2006-07-06 2:16 ` Jeff Garzik
0 siblings, 0 replies; 13+ messages in thread
From: Jeff Garzik @ 2006-07-06 2:16 UTC (permalink / raw)
To: Tejun Heo; +Cc: lkml, axboe, forrest.zhao, alan, linux-ide
applied patches 1-11
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2006-07-06 2:16 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-03 7:07 [PATCHSET] new Power Management for libata, take 4 Tejun Heo
2006-07-03 7:07 ` [PATCH 01/11] libata: implement ATA_EHI_RESUME_LINK Tejun Heo
2006-07-06 2:16 ` Jeff Garzik
2006-07-03 7:07 ` [PATCH 06/11] libata: reimplement per-dev PM Tejun Heo
2006-07-03 7:07 ` [PATCH 03/11] libata: implement ATA_EHI_NO_AUTOPSY and QUIET Tejun Heo
2006-07-03 7:07 ` [PATCH 02/11] libata: clean up debounce parameters and improve parameter selection Tejun Heo
2006-07-03 7:07 ` [PATCH 04/11] libata: separate out __ata_ehi_hotplugged() Tejun Heo
2006-07-03 7:07 ` [PATCH 05/11] libata: implement PM EH actions Tejun Heo
2006-07-03 7:07 ` [PATCH 07/11] libata: reimplement controller-wide PM Tejun Heo
2006-07-03 7:07 ` [PATCH 08/11] sata_sil: separate out sil_init_controller() Tejun Heo
2006-07-03 7:07 ` [PATCH 09/11] sata_sil: add suspend/sleep support Tejun Heo
2006-07-03 7:07 ` [PATCH 10/11] sata_sil24: separate out sil24_init_controller() Tejun Heo
2006-07-03 7:07 ` [PATCH 11/11] sata_sil24: add suspend/sleep support 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).