Commit 6c8ea89cecd780faa4f4c8ed8b3b6ab88f9fa841
Committed by
Jeff Garzik
1 parent
6b7ae9545a
Exists in
master
and in
7 other branches
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>
Showing 3 changed files with 67 additions and 10 deletions Side-by-side Diff
drivers/ata/libata-eh.c
... | ... | @@ -3232,7 +3232,7 @@ |
3232 | 3232 | static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, |
3233 | 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 | 3236 | struct ata_eh_context *ehc = &link->eh_context; |
3237 | 3237 | struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL; |
3238 | 3238 | unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM; |
... | ... | @@ -3278,9 +3278,12 @@ |
3278 | 3278 | } |
3279 | 3279 | } |
3280 | 3280 | |
3281 | - rc = ap->ops->set_lpm(link, policy, hints); | |
3282 | - if (!rc && ap->slave_link) | |
3283 | - rc = ap->ops->set_lpm(ap->slave_link, policy, hints); | |
3281 | + if (ap) { | |
3282 | + rc = ap->ops->set_lpm(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 | 3289 | * Attribute link config failure to the first (LPM) enabled |
... | ... | @@ -3412,8 +3415,14 @@ |
3412 | 3415 | ehc->saved_ncq_enabled &= ~(1 << dev->devno); |
3413 | 3416 | |
3414 | 3417 | /* the link maybe in a deep sleep, wake it up */ |
3415 | - if (link->lpm_policy > ATA_LPM_MAX_POWER) | |
3416 | - link->ap->ops->set_lpm(link, ATA_LPM_MAX_POWER, ATA_LPM_EMPTY); | |
3418 | + if (link->lpm_policy > ATA_LPM_MAX_POWER) { | |
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 | 3427 | /* Record and count probe trials on the ering. The specific |
3419 | 3428 | * error mask used is irrelevant. Because a successful device |
drivers/ata/libata-pmp.c
... | ... | @@ -186,6 +186,27 @@ |
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 | 210 | * sata_pmp_read_gscr - read GSCR block of SATA PMP |
190 | 211 | * @dev: PMP device |
191 | 212 | * @gscr: buffer to read GSCR block into |
... | ... | @@ -365,6 +386,9 @@ |
365 | 386 | if (vendor == 0x1095 && devid == 0x3726) { |
366 | 387 | /* sil3726 quirks */ |
367 | 388 | ata_for_each_link(link, ap, EDGE) { |
389 | + /* link reports offline after LPM */ | |
390 | + link->flags |= ATA_LFLAG_NO_LPM; | |
391 | + | |
368 | 392 | /* Class code report is unreliable and SRST |
369 | 393 | * times out under certain configurations. |
370 | 394 | */ |
... | ... | @@ -380,6 +404,9 @@ |
380 | 404 | } else if (vendor == 0x1095 && devid == 0x4723) { |
381 | 405 | /* sil4723 quirks */ |
382 | 406 | ata_for_each_link(link, ap, EDGE) { |
407 | + /* link reports offline after LPM */ | |
408 | + link->flags |= ATA_LFLAG_NO_LPM; | |
409 | + | |
383 | 410 | /* class code report is unreliable */ |
384 | 411 | if (link->pmp < 2) |
385 | 412 | link->flags |= ATA_LFLAG_ASSUME_ATA; |
... | ... | @@ -392,6 +419,9 @@ |
392 | 419 | } else if (vendor == 0x1095 && devid == 0x4726) { |
393 | 420 | /* sil4726 quirks */ |
394 | 421 | ata_for_each_link(link, ap, EDGE) { |
422 | + /* link reports offline after LPM */ | |
423 | + link->flags |= ATA_LFLAG_NO_LPM; | |
424 | + | |
395 | 425 | /* Class code report is unreliable and SRST |
396 | 426 | * times out under certain configurations. |
397 | 427 | * Config device can be at port 0 or 5 and |
398 | 428 | |
... | ... | @@ -952,14 +982,24 @@ |
952 | 982 | if (rc) |
953 | 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 | 985 | /* clear SNotification */ |
960 | 986 | rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); |
961 | 987 | if (rc == 0) |
962 | 988 | sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf); |
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 | + */ | |
963 | 1003 | |
964 | 1004 | /* enable notification */ |
965 | 1005 | if (pmp_dev->flags & ATA_DFLAG_AN) { |
drivers/ata/libata.h
... | ... | @@ -176,6 +176,8 @@ |
176 | 176 | #ifdef CONFIG_SATA_PMP |
177 | 177 | extern int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val); |
178 | 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 | 181 | extern int sata_pmp_attach(struct ata_device *dev); |
180 | 182 | #else /* CONFIG_SATA_PMP */ |
181 | 183 | static inline int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val) |
... | ... | @@ -184,6 +186,12 @@ |
184 | 186 | } |
185 | 187 | |
186 | 188 | static inline int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val) |
189 | +{ | |
190 | + return -EINVAL; | |
191 | +} | |
192 | + | |
193 | +static inline int sata_pmp_set_lpm(struct ata_link *link, | |
194 | + enum ata_lpm_policy policy, unsigned hints) | |
187 | 195 | { |
188 | 196 | return -EINVAL; |
189 | 197 | } |