diff options
Diffstat (limited to 'drivers/pci/host/pci-imx6.c')
-rw-r--r-- | drivers/pci/host/pci-imx6.c | 47 |
1 files changed, 34 insertions, 13 deletions
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index e8663a8c3406..ee082509b0ba 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c | |||
@@ -424,20 +424,40 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp) | |||
424 | 424 | ||
425 | static int imx6_pcie_link_up(struct pcie_port *pp) | 425 | static int imx6_pcie_link_up(struct pcie_port *pp) |
426 | { | 426 | { |
427 | u32 rc, ltssm, rx_valid; | 427 | u32 rc, debug_r0, rx_valid; |
428 | int count = 5; | ||
428 | 429 | ||
429 | /* | 430 | /* |
430 | * Test if the PHY reports that the link is up and also that | 431 | * Test if the PHY reports that the link is up and also that the LTSSM |
431 | * the link training finished. It might happen that the PHY | 432 | * training finished. There are three possible states of the link when |
432 | * reports the link is already up, but the link training bit | 433 | * this code is called: |
433 | * is still set, so make sure to check the training is done | 434 | * 1) The link is DOWN (unlikely) |
434 | * as well here. | 435 | * The link didn't come up yet for some reason. This usually means |
436 | * we have a real problem somewhere. Reset the PHY and exit. This | ||
437 | * state calls for inspection of the DEBUG registers. | ||
438 | * 2) The link is UP, but still in LTSSM training | ||
439 | * Wait for the training to finish, which should take a very short | ||
440 | * time. If the training does not finish, we have a problem and we | ||
441 | * need to inspect the DEBUG registers. If the training does finish, | ||
442 | * the link is up and operating correctly. | ||
443 | * 3) The link is UP and no longer in LTSSM training | ||
444 | * The link is up and operating correctly. | ||
435 | */ | 445 | */ |
436 | rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); | 446 | while (1) { |
437 | if ((rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) && | 447 | rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); |
438 | !(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) | 448 | if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP)) |
439 | return 1; | 449 | break; |
440 | 450 | if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) | |
451 | return 1; | ||
452 | if (!count--) | ||
453 | break; | ||
454 | dev_dbg(pp->dev, "Link is up, but still in training\n"); | ||
455 | /* | ||
456 | * Wait a little bit, then re-check if the link finished | ||
457 | * the training. | ||
458 | */ | ||
459 | usleep_range(1000, 2000); | ||
460 | } | ||
441 | /* | 461 | /* |
442 | * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. | 462 | * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. |
443 | * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). | 463 | * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). |
@@ -446,15 +466,16 @@ static int imx6_pcie_link_up(struct pcie_port *pp) | |||
446 | * to gen2 is stuck | 466 | * to gen2 is stuck |
447 | */ | 467 | */ |
448 | pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid); | 468 | pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid); |
449 | ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F; | 469 | debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0); |
450 | 470 | ||
451 | if (rx_valid & 0x01) | 471 | if (rx_valid & 0x01) |
452 | return 0; | 472 | return 0; |
453 | 473 | ||
454 | if (ltssm != 0x0d) | 474 | if ((debug_r0 & 0x3f) != 0x0d) |
455 | return 0; | 475 | return 0; |
456 | 476 | ||
457 | dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); | 477 | dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); |
478 | dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc); | ||
458 | 479 | ||
459 | imx6_pcie_reset_phy(pp); | 480 | imx6_pcie_reset_phy(pp); |
460 | 481 | ||