Commit 6c8ea89cecd780faa4f4c8ed8b3b6ab88f9fa841

Authored by Tejun Heo
Committed by Jeff Garzik
1 parent 6b7ae9545a

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 }