From: Tejun Heo <htejun@gmail.com>
To: jgarzik@pobox.com, alan@lxorguk.ukuu.org.uk, lkml@rtr.ca,
axboe@suse.de, forrest.zhao@intel.com, linux-ide@vger.kernel.org
Cc: Tejun Heo <htejun@gmail.com>
Subject: [PATCH 07/12] libata: implement sata_update_scontrol()
Date: Mon, 17 Jul 2006 15:52:32 +0900 [thread overview]
Message-ID: <1153119152202-git-send-email-htejun@gmail.com> (raw)
In-Reply-To: <11531191512028-git-send-email-htejun@gmail.com>
SControl register hosts several control fields and update to one field
shouldn't alter others. libata used to do read-modify-write to
achieve this. However, this results in excessive SControl reading and
loss of SControl setting under certain circumstances (e.g. suspend /
resume cycles, hardware glitch).
This patch adds SControl cache, ap->scontrol, and two helper functions
- sata_update_scontrol_push() which only updates the cache and
sata_update_scontrol() which updates & commits the updated value to
SControl.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 139 +++++++++++++++++++++++++++++---------------
drivers/scsi/sata_mv.c | 4 +
include/linux/libata.h | 4 +
3 files changed, 99 insertions(+), 48 deletions(-)
ac471e3d83d7a3a7ae2b4cf976b348ce7a74948b
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 3e35839..cc77bd5 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -1859,26 +1859,19 @@ int sata_down_spd_limit(struct ata_port
return 0;
}
-static int __sata_set_spd_needed(struct ata_port *ap, u32 *scontrol)
+static inline int sata_spd_val(struct ata_port *ap)
{
- u32 spd, limit;
-
if (ap->sata_spd_limit == UINT_MAX)
- limit = 0;
+ return 0;
else
- limit = fls(ap->sata_spd_limit);
-
- spd = (*scontrol >> 4) & 0xf;
- *scontrol = (*scontrol & ~0xf0) | ((limit & 0xf) << 4);
-
- return spd != limit;
+ return fls(ap->sata_spd_limit);
}
/**
* sata_set_spd_needed - is SATA spd configuration needed
* @ap: Port in question
*
- * Test whether the spd limit in SControl matches
+ * Test whether the spd limit in ap->scontrol matches
* @ap->sata_spd_limit. This function is used to determine
* whether hardreset is necessary to apply SATA spd
* configuration.
@@ -1891,12 +1884,9 @@ static int __sata_set_spd_needed(struct
*/
int sata_set_spd_needed(struct ata_port *ap)
{
- u32 scontrol;
+ u32 spd = (ap->scontrol >> 4) & 0xf;
- if (sata_scr_read(ap, SCR_CONTROL, &scontrol))
- return 0;
-
- return __sata_set_spd_needed(ap, &scontrol);
+ return spd != sata_spd_val(ap);
}
/**
@@ -1914,16 +1904,12 @@ int sata_set_spd_needed(struct ata_port
*/
int sata_set_spd(struct ata_port *ap)
{
- u32 scontrol;
int rc;
- if ((rc = sata_scr_read(ap, SCR_CONTROL, &scontrol)))
- return rc;
-
- if (!__sata_set_spd_needed(ap, &scontrol))
+ if (!sata_set_spd_needed(ap))
return 0;
- if ((rc = sata_scr_write(ap, SCR_CONTROL, scontrol)))
+ if ((rc = sata_update_scontrol(ap, ATA_SCTL_SPD, sata_spd_val(ap))))
return rc;
return 1;
@@ -2595,15 +2581,9 @@ int sata_phy_debounce(struct ata_port *a
*/
int sata_phy_resume(struct ata_port *ap, const unsigned long *params)
{
- u32 scontrol;
int rc;
- if ((rc = sata_scr_read(ap, SCR_CONTROL, &scontrol)))
- return rc;
-
- scontrol = (scontrol & 0x0f0) | 0x300;
-
- if ((rc = sata_scr_write(ap, SCR_CONTROL, scontrol)))
+ if ((rc = sata_update_scontrol(ap, ATA_SCTL_DET, 0x0)))
return rc;
/* Some PHYs react badly if SStatus is pounded immediately
@@ -2765,7 +2745,6 @@ int sata_std_hardreset(struct ata_port *
{
struct ata_eh_context *ehc = &ap->eh_context;
const unsigned long *timing = sata_ehc_deb_timing(ehc);
- u32 scontrol;
int rc;
DPRINTK("ENTER\n");
@@ -2776,24 +2755,14 @@ int sata_std_hardreset(struct ata_port *
* reconfiguration. This works for at least ICH7 AHCI
* and Sil3124.
*/
- if ((rc = sata_scr_read(ap, SCR_CONTROL, &scontrol)))
- return rc;
-
- scontrol = (scontrol & 0x0f0) | 0x304;
-
- if ((rc = sata_scr_write(ap, SCR_CONTROL, scontrol)))
+ if ((rc = sata_update_scontrol(ap, ATA_SCTL_DET, 0x4)))
return rc;
sata_set_spd(ap);
}
/* issue phy wake/reset */
- if ((rc = sata_scr_read(ap, SCR_CONTROL, &scontrol)))
- return rc;
-
- scontrol = (scontrol & 0x0f0) | 0x301;
-
- if ((rc = sata_scr_write_flush(ap, SCR_CONTROL, scontrol)))
+ if ((rc = sata_update_scontrol(ap, ATA_SCTL_DET, 0x1)))
return rc;
/* Couldn't find anything in SATA I/II specs, but AHCI-1.1
@@ -4975,6 +4944,77 @@ int sata_scr_write_flush(struct ata_port
}
/**
+ * sata_update_scontrol_push - push SControl register update
+ * @ap: ATA port to update SControl for
+ * @sel: ATA_SCTL_* subfield selector
+ * @val: value to be written
+ *
+ * SControl hosts several control subfields which need to be
+ * treated separately. SControl value is cached on
+ * initialization and this function updates only the requested
+ * field of the cache. This function only accumulates SControl
+ * updates in the cache and does NOT write the updated value to
+ * SControl.
+ *
+ * SControl cache reduces SControl access and helps preserving
+ * SControl when hardware forgets the configured value (e.g. over
+ * suspend/resume).
+ *
+ * LOCKING:
+ * ap->scontrol is protected by ap->lock while EH is inactive and
+ * owned by EH while it's active. So, spin_lock_irqsave(host_set
+ * lock) out of EH, and none during EH.
+ *
+ * RETURNS:
+ * 0 on success, negative errno on failure.
+ */
+void sata_update_scontrol_push(struct ata_port *ap, int sel, u8 val)
+{
+ u32 scontrol = ap->scontrol;
+ int shift = sel * 4;
+
+ WARN_ON(val > 0xf);
+ val &= 0xf;
+
+ scontrol = (scontrol & ~(0xf << shift)) | (val << shift);
+
+ ap->scontrol = scontrol;
+}
+
+/**
+ * sata_update_scontrol - update SControl register
+ * @ap: ATA port to update SControl for
+ * @sel: ATA_SCTL_* subfield selector
+ * @val: value to be written
+ *
+ * Update SControl cache using sata_update_scontrol_push() and
+ * write the cached value to the SControl register.
+ *
+ * LOCKING:
+ * ap->scontrol is protected by ap->lock while EH is inactive and
+ * owned by EH while it's active. So, spin_lock_irqsave(host_set
+ * lock) out of EH, and none during EH.
+ *
+ * RETURNS:
+ * 0 on success, negative errno on failure.
+ */
+int sata_update_scontrol(struct ata_port *ap, int sel, u8 val)
+{
+ int rc;
+
+ /* push requested change */
+ sata_update_scontrol_push(ap, sel, val);
+
+ /* always use the flushing version */
+ rc = sata_scr_write_flush(ap, SCR_CONTROL, ap->scontrol);
+
+ /* SPM is oneshot field, don't cache it over write */
+ ap->scontrol &= ~(0xf << (ATA_SCTL_SPM * 4));
+
+ return 0;
+}
+
+/**
* ata_port_online - test whether the given port is online
* @ap: ATA port to test
*
@@ -5492,7 +5532,6 @@ int ata_device_add(const struct ata_prob
DPRINTK("probe begin\n");
for (i = 0; i < count; i++) {
struct ata_port *ap;
- u32 scontrol;
int rc;
ap = host_set->ports[i];
@@ -5502,9 +5541,15 @@ int ata_device_add(const struct ata_prob
list_add_tail(&ap->all_ports_entry, &ata_all_ports);
mutex_unlock(&ata_all_ports_mutex);
- /* init sata_spd_limit to the current value */
- if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) {
- int spd = (scontrol >> 4) & 0xf;
+ /* init scontrol and sata_spd_limit to the current value */
+ ap->scontrol = 0x300; /* default value */
+ if (sata_scr_read(ap, SCR_CONTROL, &ap->scontrol) == 0) {
+ int spd;
+
+ /* zero PMP/SPM and no powersaving */
+ ap->scontrol = (ap->scontrol & 0xfff000ff) | 0x300;
+
+ spd = ata_scontrol_field(ap->scontrol, ATA_SCTL_SPD);
ap->hw_sata_spd_limit &= (1 << spd) - 1;
}
ap->sata_spd_limit = ap->hw_sata_spd_limit;
@@ -6036,6 +6081,8 @@ EXPORT_SYMBOL_GPL(sata_scr_valid);
EXPORT_SYMBOL_GPL(sata_scr_read);
EXPORT_SYMBOL_GPL(sata_scr_write);
EXPORT_SYMBOL_GPL(sata_scr_write_flush);
+EXPORT_SYMBOL_GPL(sata_update_scontrol_push);
+EXPORT_SYMBOL_GPL(sata_update_scontrol);
EXPORT_SYMBOL_GPL(ata_port_online);
EXPORT_SYMBOL_GPL(ata_port_offline);
EXPORT_SYMBOL_GPL(ata_host_set_suspend);
diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c
index 1053c7c..52bf790 100644
--- a/drivers/scsi/sata_mv.c
+++ b/drivers/scsi/sata_mv.c
@@ -1952,10 +1952,10 @@ static void __mv_phy_reset(struct ata_po
/* Issue COMRESET via SControl */
comreset_retry:
- sata_scr_write_flush(ap, SCR_CONTROL, 0x301);
+ sata_update_scontrol(ap, ATA_SCTL_DET, 0x1);
__msleep(1, can_sleep);
- sata_scr_write_flush(ap, SCR_CONTROL, 0x300);
+ sata_update_scontrol(ap, ATA_SCTL_DET, 0x0);
__msleep(20, can_sleep);
timeout = jiffies + msecs_to_jiffies(200);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index c9ed035..1656d5b 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -521,6 +521,8 @@ struct ata_port {
unsigned int mwdma_mask;
unsigned int udma_mask;
unsigned int cbl; /* cable type; ATA_CBL_xxx */
+
+ u32 scontrol; /* for update_scontrol */
unsigned int hw_sata_spd_limit;
unsigned int sata_spd_limit; /* SATA PHY speed limit */
@@ -696,6 +698,8 @@ extern int sata_scr_valid(struct ata_por
extern int sata_scr_read(struct ata_port *ap, int reg, u32 *val);
extern int sata_scr_write(struct ata_port *ap, int reg, u32 val);
extern int sata_scr_write_flush(struct ata_port *ap, int reg, u32 val);
+extern void sata_update_scontrol_push(struct ata_port *ap, int sel, u8 val);
+extern int sata_update_scontrol(struct ata_port *ap, int sel, u8 val);
extern int ata_port_online(struct ata_port *ap);
extern int ata_port_offline(struct ata_port *ap);
extern int ata_scsi_device_resume(struct scsi_device *);
--
1.3.2
next prev parent reply other threads:[~2006-07-17 6:51 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-07-17 6:52 [PATCHSET] libata: implement runtime link powersave Tejun Heo
2006-07-17 6:52 ` [PATCH 01/12] libata: add msec_to_jiffies() Tejun Heo
2006-07-17 6:52 ` [PATCH 04/12] libata: implement ata_all_ports list Tejun Heo
2006-07-19 19:34 ` Jeff Garzik
2006-07-17 6:52 ` [PATCH 02/12] libata: add ata_id_has_sata() and use it in ata_id_has_ncq() Tejun Heo
2006-07-17 6:52 ` [PATCH 03/12] libata: add more SATA specific constants and macros to ata.h Tejun Heo
2006-07-19 19:32 ` Jeff Garzik
2006-07-17 6:52 ` Tejun Heo [this message]
2006-07-19 19:35 ` [PATCH 07/12] libata: implement sata_update_scontrol() Jeff Garzik
2006-07-17 6:52 ` [PATCH 09/12] libata: implement powersave timer Tejun Heo
2006-07-19 19:48 ` Jeff Garzik
2006-07-19 20:22 ` Jens Axboe
2006-07-24 7:27 ` Tejun Heo
2006-07-25 8:01 ` Jens Axboe
2006-07-17 6:52 ` [PATCH 05/12] libata: make counting functions global Tejun Heo
2006-07-17 6:52 ` [PATCH 10/12] libata: implement standard powersave methods Tejun Heo
2006-07-19 19:50 ` Jeff Garzik
2006-07-17 6:52 ` [PATCH 08/12] libata: implement interface power management infrastructure Tejun Heo
2006-07-19 19:45 ` Jeff Garzik
2006-07-24 8:02 ` Tejun Heo
2006-07-17 6:52 ` [PATCH 11/12] ahci: implement link powersave Tejun Heo
2006-07-19 19:51 ` Jeff Garzik
2006-07-17 6:52 ` [PATCH 06/12] libata: add ata_port_nr_ready() Tejun Heo
2006-07-17 6:52 ` [PATCH 12/12] sata_sil24: implement link powersave Tejun Heo
2006-07-19 19:38 ` [PATCHSET] libata: implement runtime " Jeff Garzik
2006-07-24 7:33 ` Tejun Heo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1153119152202-git-send-email-htejun@gmail.com \
--to=htejun@gmail.com \
--cc=alan@lxorguk.ukuu.org.uk \
--cc=axboe@suse.de \
--cc=forrest.zhao@intel.com \
--cc=jgarzik@pobox.com \
--cc=linux-ide@vger.kernel.org \
--cc=lkml@rtr.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is 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).