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 | |
| 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>
| -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; |
