diff options
author | Tejun Heo <tj@kernel.org> | 2010-09-01 11:50:07 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2010-10-21 20:21:04 -0400 |
commit | 6c8ea89cecd780faa4f4c8ed8b3b6ab88f9fa841 (patch) | |
tree | a38ecca3c52cd4ca021137086b39d7dcd8b042cc /drivers | |
parent | 6b7ae9545ad9875a289f4191c0216b473e313cb9 (diff) |
libata: implement LPM support for port multipliers
Port multipliers can do DIPM on fan-out links fine. Implement support
for it. Tested w/ SIMG 57xx and marvell PMPs. Both the host and
fan-out links enter power save modes nicely.
SIMG 37xx and 47xx report link offline on SStatus causing EH to detach
the devices. Blacklisted.
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/libata-eh.c | 21 | ||||
-rw-r--r-- | drivers/ata/libata-pmp.c | 48 | ||||
-rw-r--r-- | drivers/ata/libata.h | 8 |
3 files changed, 67 insertions, 10 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a645cd3ab163..06a4db1ec10e 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
@@ -3232,7 +3232,7 @@ static int ata_eh_maybe_retry_flush(struct ata_device *dev) | |||
3232 | static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | 3232 | static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, |
3233 | struct ata_device **r_failed_dev) | 3233 | struct ata_device **r_failed_dev) |
3234 | { | 3234 | { |
3235 | struct ata_port *ap = link->ap; | 3235 | struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL; |
3236 | struct ata_eh_context *ehc = &link->eh_context; | 3236 | struct ata_eh_context *ehc = &link->eh_context; |
3237 | struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL; | 3237 | struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL; |
3238 | unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM; | 3238 | unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM; |
@@ -3278,9 +3278,12 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |||
3278 | } | 3278 | } |
3279 | } | 3279 | } |
3280 | 3280 | ||
3281 | rc = ap->ops->set_lpm(link, policy, hints); | 3281 | if (ap) { |
3282 | if (!rc && ap->slave_link) | 3282 | rc = ap->ops->set_lpm(link, policy, hints); |
3283 | rc = ap->ops->set_lpm(ap->slave_link, policy, hints); | 3283 | if (!rc && ap->slave_link) |
3284 | rc = ap->ops->set_lpm(ap->slave_link, policy, hints); | ||
3285 | } else | ||
3286 | rc = sata_pmp_set_lpm(link, policy, hints); | ||
3284 | 3287 | ||
3285 | /* | 3288 | /* |
3286 | * Attribute link config failure to the first (LPM) enabled | 3289 | * Attribute link config failure to the first (LPM) enabled |
@@ -3412,8 +3415,14 @@ static int ata_eh_schedule_probe(struct ata_device *dev) | |||
3412 | ehc->saved_ncq_enabled &= ~(1 << dev->devno); | 3415 | ehc->saved_ncq_enabled &= ~(1 << dev->devno); |
3413 | 3416 | ||
3414 | /* the link maybe in a deep sleep, wake it up */ | 3417 | /* the link maybe in a deep sleep, wake it up */ |
3415 | if (link->lpm_policy > ATA_LPM_MAX_POWER) | 3418 | if (link->lpm_policy > ATA_LPM_MAX_POWER) { |
3416 | link->ap->ops->set_lpm(link, ATA_LPM_MAX_POWER, ATA_LPM_EMPTY); | 3419 | if (ata_is_host_link(link)) |
3420 | link->ap->ops->set_lpm(link, ATA_LPM_MAX_POWER, | ||
3421 | ATA_LPM_EMPTY); | ||
3422 | else | ||
3423 | sata_pmp_set_lpm(link, ATA_LPM_MAX_POWER, | ||
3424 | ATA_LPM_EMPTY); | ||
3425 | } | ||
3417 | 3426 | ||
3418 | /* Record and count probe trials on the ering. The specific | 3427 | /* Record and count probe trials on the ering. The specific |
3419 | * error mask used is irrelevant. Because a successful device | 3428 | * error mask used is irrelevant. Because a successful device |
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 505470237d79..3120596d4afc 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c | |||
@@ -186,6 +186,27 @@ int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val) | |||
186 | } | 186 | } |
187 | 187 | ||
188 | /** | 188 | /** |
189 | * sata_pmp_set_lpm - configure LPM for a PMP link | ||
190 | * @link: PMP link to configure LPM for | ||
191 | * @policy: target LPM policy | ||
192 | * @hints: LPM hints | ||
193 | * | ||
194 | * Configure LPM for @link. This function will contain any PMP | ||
195 | * specific workarounds if necessary. | ||
196 | * | ||
197 | * LOCKING: | ||
198 | * EH context. | ||
199 | * | ||
200 | * RETURNS: | ||
201 | * 0 on success, -errno on failure. | ||
202 | */ | ||
203 | int sata_pmp_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | ||
204 | unsigned hints) | ||
205 | { | ||
206 | return sata_link_scr_lpm(link, policy, true); | ||
207 | } | ||
208 | |||
209 | /** | ||
189 | * sata_pmp_read_gscr - read GSCR block of SATA PMP | 210 | * sata_pmp_read_gscr - read GSCR block of SATA PMP |
190 | * @dev: PMP device | 211 | * @dev: PMP device |
191 | * @gscr: buffer to read GSCR block into | 212 | * @gscr: buffer to read GSCR block into |
@@ -365,6 +386,9 @@ static void sata_pmp_quirks(struct ata_port *ap) | |||
365 | if (vendor == 0x1095 && devid == 0x3726) { | 386 | if (vendor == 0x1095 && devid == 0x3726) { |
366 | /* sil3726 quirks */ | 387 | /* sil3726 quirks */ |
367 | ata_for_each_link(link, ap, EDGE) { | 388 | ata_for_each_link(link, ap, EDGE) { |
389 | /* link reports offline after LPM */ | ||
390 | link->flags |= ATA_LFLAG_NO_LPM; | ||
391 | |||
368 | /* Class code report is unreliable and SRST | 392 | /* Class code report is unreliable and SRST |
369 | * times out under certain configurations. | 393 | * times out under certain configurations. |
370 | */ | 394 | */ |
@@ -380,6 +404,9 @@ static void sata_pmp_quirks(struct ata_port *ap) | |||
380 | } else if (vendor == 0x1095 && devid == 0x4723) { | 404 | } else if (vendor == 0x1095 && devid == 0x4723) { |
381 | /* sil4723 quirks */ | 405 | /* sil4723 quirks */ |
382 | ata_for_each_link(link, ap, EDGE) { | 406 | ata_for_each_link(link, ap, EDGE) { |
407 | /* link reports offline after LPM */ | ||
408 | link->flags |= ATA_LFLAG_NO_LPM; | ||
409 | |||
383 | /* class code report is unreliable */ | 410 | /* class code report is unreliable */ |
384 | if (link->pmp < 2) | 411 | if (link->pmp < 2) |
385 | link->flags |= ATA_LFLAG_ASSUME_ATA; | 412 | link->flags |= ATA_LFLAG_ASSUME_ATA; |
@@ -392,6 +419,9 @@ static void sata_pmp_quirks(struct ata_port *ap) | |||
392 | } else if (vendor == 0x1095 && devid == 0x4726) { | 419 | } else if (vendor == 0x1095 && devid == 0x4726) { |
393 | /* sil4726 quirks */ | 420 | /* sil4726 quirks */ |
394 | ata_for_each_link(link, ap, EDGE) { | 421 | ata_for_each_link(link, ap, EDGE) { |
422 | /* link reports offline after LPM */ | ||
423 | link->flags |= ATA_LFLAG_NO_LPM; | ||
424 | |||
395 | /* Class code report is unreliable and SRST | 425 | /* Class code report is unreliable and SRST |
396 | * times out under certain configurations. | 426 | * times out under certain configurations. |
397 | * Config device can be at port 0 or 5 and | 427 | * Config device can be at port 0 or 5 and |
@@ -952,15 +982,25 @@ static int sata_pmp_eh_recover(struct ata_port *ap) | |||
952 | if (rc) | 982 | if (rc) |
953 | goto link_fail; | 983 | goto link_fail; |
954 | 984 | ||
955 | /* Connection status might have changed while resetting other | ||
956 | * links, check SATA_PMP_GSCR_ERROR before returning. | ||
957 | */ | ||
958 | |||
959 | /* clear SNotification */ | 985 | /* clear SNotification */ |
960 | rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); | 986 | rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); |
961 | if (rc == 0) | 987 | if (rc == 0) |
962 | sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf); | 988 | sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf); |
963 | 989 | ||
990 | /* | ||
991 | * If LPM is active on any fan-out port, hotplug wouldn't | ||
992 | * work. Return w/ PHY event notification disabled. | ||
993 | */ | ||
994 | ata_for_each_link(link, ap, EDGE) | ||
995 | if (link->lpm_policy > ATA_LPM_MAX_POWER) | ||
996 | return 0; | ||
997 | |||
998 | /* | ||
999 | * Connection status might have changed while resetting other | ||
1000 | * links, enable notification and check SATA_PMP_GSCR_ERROR | ||
1001 | * before returning. | ||
1002 | */ | ||
1003 | |||
964 | /* enable notification */ | 1004 | /* enable notification */ |
965 | if (pmp_dev->flags & ATA_DFLAG_AN) { | 1005 | if (pmp_dev->flags & ATA_DFLAG_AN) { |
966 | gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY; | 1006 | gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY; |
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 55a6f413a550..7c070a4b1c08 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h | |||
@@ -176,6 +176,8 @@ extern int ata_ering_map(struct ata_ering *ering, | |||
176 | #ifdef CONFIG_SATA_PMP | 176 | #ifdef CONFIG_SATA_PMP |
177 | extern int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val); | 177 | extern int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val); |
178 | extern int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val); | 178 | extern int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val); |
179 | extern int sata_pmp_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | ||
180 | unsigned hints); | ||
179 | extern int sata_pmp_attach(struct ata_device *dev); | 181 | extern int sata_pmp_attach(struct ata_device *dev); |
180 | #else /* CONFIG_SATA_PMP */ | 182 | #else /* CONFIG_SATA_PMP */ |
181 | static inline int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val) | 183 | static inline int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val) |
@@ -188,6 +190,12 @@ static inline int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val) | |||
188 | return -EINVAL; | 190 | return -EINVAL; |
189 | } | 191 | } |
190 | 192 | ||
193 | static inline int sata_pmp_set_lpm(struct ata_link *link, | ||
194 | enum ata_lpm_policy policy, unsigned hints) | ||
195 | { | ||
196 | return -EINVAL; | ||
197 | } | ||
198 | |||
191 | static inline int sata_pmp_attach(struct ata_device *dev) | 199 | static inline int sata_pmp_attach(struct ata_device *dev) |
192 | { | 200 | { |
193 | return -EINVAL; | 201 | return -EINVAL; |