diff options
Diffstat (limited to 'drivers/ata/libata-pmp.c')
| -rw-r--r-- | drivers/ata/libata-pmp.c | 66 |
1 files changed, 60 insertions, 6 deletions
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 224faabd7b7e..3120596d4afc 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <linux/libata.h> | 11 | #include <linux/libata.h> |
| 12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
| 13 | #include "libata.h" | 13 | #include "libata.h" |
| 14 | #include "libata-transport.h" | ||
| 14 | 15 | ||
| 15 | const struct ata_port_operations sata_pmp_port_ops = { | 16 | const struct ata_port_operations sata_pmp_port_ops = { |
| 16 | .inherits = &sata_port_ops, | 17 | .inherits = &sata_port_ops, |
| @@ -185,6 +186,27 @@ int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val) | |||
| 185 | } | 186 | } |
| 186 | 187 | ||
| 187 | /** | 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 | /** | ||
| 188 | * sata_pmp_read_gscr - read GSCR block of SATA PMP | 210 | * sata_pmp_read_gscr - read GSCR block of SATA PMP |
| 189 | * @dev: PMP device | 211 | * @dev: PMP device |
| 190 | * @gscr: buffer to read GSCR block into | 212 | * @gscr: buffer to read GSCR block into |
| @@ -312,10 +334,10 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) | |||
| 312 | return rc; | 334 | return rc; |
| 313 | } | 335 | } |
| 314 | 336 | ||
| 315 | static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) | 337 | static int sata_pmp_init_links (struct ata_port *ap, int nr_ports) |
| 316 | { | 338 | { |
| 317 | struct ata_link *pmp_link = ap->pmp_link; | 339 | struct ata_link *pmp_link = ap->pmp_link; |
| 318 | int i; | 340 | int i, err; |
| 319 | 341 | ||
| 320 | if (!pmp_link) { | 342 | if (!pmp_link) { |
| 321 | pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS, | 343 | pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS, |
| @@ -327,6 +349,13 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) | |||
| 327 | ata_link_init(ap, &pmp_link[i], i); | 349 | ata_link_init(ap, &pmp_link[i], i); |
| 328 | 350 | ||
| 329 | ap->pmp_link = pmp_link; | 351 | ap->pmp_link = pmp_link; |
| 352 | |||
| 353 | for (i = 0; i < SATA_PMP_MAX_PORTS; i++) { | ||
| 354 | err = ata_tlink_add(&pmp_link[i]); | ||
| 355 | if (err) { | ||
| 356 | goto err_tlink; | ||
| 357 | } | ||
| 358 | } | ||
| 330 | } | 359 | } |
| 331 | 360 | ||
| 332 | for (i = 0; i < nr_ports; i++) { | 361 | for (i = 0; i < nr_ports; i++) { |
| @@ -339,6 +368,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) | |||
| 339 | } | 368 | } |
| 340 | 369 | ||
| 341 | return 0; | 370 | return 0; |
| 371 | err_tlink: | ||
| 372 | while (--i >= 0) | ||
| 373 | ata_tlink_delete(&pmp_link[i]); | ||
| 374 | kfree(pmp_link); | ||
| 375 | ap->pmp_link = NULL; | ||
| 376 | return err; | ||
| 342 | } | 377 | } |
| 343 | 378 | ||
| 344 | static void sata_pmp_quirks(struct ata_port *ap) | 379 | static void sata_pmp_quirks(struct ata_port *ap) |
| @@ -351,6 +386,9 @@ static void sata_pmp_quirks(struct ata_port *ap) | |||
| 351 | if (vendor == 0x1095 && devid == 0x3726) { | 386 | if (vendor == 0x1095 && devid == 0x3726) { |
| 352 | /* sil3726 quirks */ | 387 | /* sil3726 quirks */ |
| 353 | 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 | |||
| 354 | /* Class code report is unreliable and SRST | 392 | /* Class code report is unreliable and SRST |
| 355 | * times out under certain configurations. | 393 | * times out under certain configurations. |
| 356 | */ | 394 | */ |
| @@ -366,6 +404,9 @@ static void sata_pmp_quirks(struct ata_port *ap) | |||
| 366 | } else if (vendor == 0x1095 && devid == 0x4723) { | 404 | } else if (vendor == 0x1095 && devid == 0x4723) { |
| 367 | /* sil4723 quirks */ | 405 | /* sil4723 quirks */ |
| 368 | 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 | |||
| 369 | /* class code report is unreliable */ | 410 | /* class code report is unreliable */ |
| 370 | if (link->pmp < 2) | 411 | if (link->pmp < 2) |
| 371 | link->flags |= ATA_LFLAG_ASSUME_ATA; | 412 | link->flags |= ATA_LFLAG_ASSUME_ATA; |
| @@ -378,6 +419,9 @@ static void sata_pmp_quirks(struct ata_port *ap) | |||
| 378 | } else if (vendor == 0x1095 && devid == 0x4726) { | 419 | } else if (vendor == 0x1095 && devid == 0x4726) { |
| 379 | /* sil4726 quirks */ | 420 | /* sil4726 quirks */ |
| 380 | 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 | |||
| 381 | /* Class code report is unreliable and SRST | 425 | /* Class code report is unreliable and SRST |
| 382 | * times out under certain configurations. | 426 | * times out under certain configurations. |
| 383 | * Config device can be at port 0 or 5 and | 427 | * Config device can be at port 0 or 5 and |
| @@ -938,15 +982,25 @@ static int sata_pmp_eh_recover(struct ata_port *ap) | |||
| 938 | if (rc) | 982 | if (rc) |
| 939 | goto link_fail; | 983 | goto link_fail; |
| 940 | 984 | ||
| 941 | /* Connection status might have changed while resetting other | ||
| 942 | * links, check SATA_PMP_GSCR_ERROR before returning. | ||
| 943 | */ | ||
| 944 | |||
| 945 | /* clear SNotification */ | 985 | /* clear SNotification */ |
| 946 | rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); | 986 | rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); |
| 947 | if (rc == 0) | 987 | if (rc == 0) |
| 948 | sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf); | 988 | sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf); |
| 949 | 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 | |||
| 950 | /* enable notification */ | 1004 | /* enable notification */ |
| 951 | if (pmp_dev->flags & ATA_DFLAG_AN) { | 1005 | if (pmp_dev->flags & ATA_DFLAG_AN) { |
| 952 | gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY; | 1006 | gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY; |
