|
33 | 33 |
|
34 | 34 | #include "pci-host-common.h" |
35 | 35 |
|
| 36 | +static int link_up_timeout = 500; |
| 37 | +module_param(link_up_timeout, int, 0644); |
| 38 | +MODULE_PARM_DESC(link_up_timeout, "PCIe link training timeout in milliseconds"); |
| 39 | + |
36 | 40 | /* T8103 (original M1) and related SoCs */ |
37 | 41 | #define CORE_RC_PHYIF_CTL 0x00024 |
38 | 42 | #define CORE_RC_PHYIF_CTL_RUN BIT(0) |
@@ -550,22 +554,105 @@ static u32 apple_pcie_rid2sid_write(struct apple_pcie_port *port, |
550 | 554 | return readl_relaxed(port_rid2sid_addr(port, idx)); |
551 | 555 | } |
552 | 556 |
|
| 557 | +static int apple_pcie_setup_link(struct apple_pcie *pcie, |
| 558 | + struct apple_pcie_port *port, |
| 559 | + struct device_node *np) |
| 560 | +{ |
| 561 | +#define MAX_AUX_PERST 3 |
| 562 | + struct gpio_desc *aux_reset[MAX_AUX_PERST] = { NULL }; |
| 563 | + u32 num_aux_resets = 0; |
| 564 | + struct gpio_desc *reset, *pwren = NULL; |
| 565 | + u32 stat; |
| 566 | + int ret; |
| 567 | + |
| 568 | + /* |
| 569 | + * Assert PERST# and configure the pin as output. |
| 570 | + * The Aquantia AQC113 10GB nic used desktop macs is sensitive to |
| 571 | + * deasserting it without prior clock setup. |
| 572 | + * Observed on M1 Max/Ultra Mac Studios under m1n1's hypervisor. |
| 573 | + */ |
| 574 | + reset = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "reset", |
| 575 | + GPIOD_OUT_HIGH, "PERST#"); |
| 576 | + if (IS_ERR(reset)) |
| 577 | + return PTR_ERR(reset); |
| 578 | + // HACK: use additional "reset-gpios" until pci-pwrctrl gains PERST# support. |
| 579 | + for (u32 idx = 0; idx < MAX_AUX_PERST; idx++) { |
| 580 | + aux_reset[idx] = devm_fwnode_gpiod_get_index(pcie->dev, |
| 581 | + of_fwnode_handle(np), |
| 582 | + "reset", idx + 1, |
| 583 | + GPIOD_OUT_HIGH, |
| 584 | + "PERST#"); |
| 585 | + if (IS_ERR(aux_reset[idx])) { |
| 586 | + if (PTR_ERR(aux_reset[idx]) == -ENOENT) |
| 587 | + break; |
| 588 | + else |
| 589 | + return PTR_ERR(aux_reset[idx]); |
| 590 | + } |
| 591 | + num_aux_resets++; |
| 592 | + } |
| 593 | + dev_info(pcie->dev, "Using %u auxiliary PERST#\n", num_aux_resets); |
| 594 | + |
| 595 | + pwren = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "pwren", |
| 596 | + GPIOD_ASIS, "PWREN"); |
| 597 | + if (IS_ERR(pwren)) { |
| 598 | + if (PTR_ERR(pwren) == -ENOENT) |
| 599 | + pwren = NULL; |
| 600 | + else |
| 601 | + return PTR_ERR(pwren); |
| 602 | + } |
| 603 | + |
| 604 | + rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK); |
| 605 | + |
| 606 | + /* Assert PERST# before setting up the clock */ |
| 607 | + gpiod_set_value_cansleep(reset, 1); |
| 608 | + for (u32 idx = 0; idx < num_aux_resets; idx++) |
| 609 | + gpiod_set_value_cansleep(aux_reset[idx], 1); |
| 610 | + |
| 611 | + /* Power on the device if required */ |
| 612 | + gpiod_set_value_cansleep(pwren, 1); |
| 613 | + |
| 614 | + ret = apple_pcie_setup_refclk(pcie, port); |
| 615 | + if (ret < 0) |
| 616 | + return ret; |
| 617 | + |
| 618 | + /* |
| 619 | + * The minimal Tperst-clk value is 100us (PCIe CEM r5.0, 2.9.2) |
| 620 | + * If powering up, the minimal Tpvperl is 100ms |
| 621 | + */ |
| 622 | + if (pwren) |
| 623 | + msleep(100); |
| 624 | + else |
| 625 | + usleep_range(100, 200); |
| 626 | + |
| 627 | + /* Deassert PERST# */ |
| 628 | + rmw_set(PORT_PERST_OFF, port->base + pcie->hw->port_perst); |
| 629 | + gpiod_set_value_cansleep(reset, 0); |
| 630 | + for (u32 idx = 0; idx < num_aux_resets; idx++) |
| 631 | + gpiod_set_value_cansleep(aux_reset[idx], 0); |
| 632 | + |
| 633 | + /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ |
| 634 | + msleep(100); |
| 635 | + |
| 636 | + ret = readl_relaxed_poll_timeout(port->base + PORT_STATUS, stat, |
| 637 | + stat & PORT_STATUS_READY, 100, 250000); |
| 638 | + if (ret < 0) { |
| 639 | + dev_err(pcie->dev, "port %pOF ready wait timeout\n", np); |
| 640 | + return ret; |
| 641 | + } |
| 642 | + |
| 643 | + return 0; |
| 644 | +} |
| 645 | + |
553 | 646 | static int apple_pcie_setup_port(struct apple_pcie *pcie, |
554 | 647 | struct device_node *np) |
555 | 648 | { |
556 | 649 | struct platform_device *platform = to_platform_device(pcie->dev); |
557 | 650 | struct apple_pcie_port *port; |
558 | | - struct gpio_desc *reset; |
559 | 651 | struct resource *res; |
560 | 652 | char name[16]; |
561 | | - u32 stat, idx; |
| 653 | + u32 link_stat, idx; |
562 | 654 | int ret, i; |
563 | 655 |
|
564 | | - reset = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "reset", |
565 | | - GPIOD_OUT_LOW, "PERST#"); |
566 | | - if (IS_ERR(reset)) |
567 | | - return PTR_ERR(reset); |
568 | | - |
569 | 656 | port = devm_kzalloc(pcie->dev, sizeof(*port), GFP_KERNEL); |
570 | 657 | if (!port) |
571 | 658 | return -ENOMEM; |
@@ -601,30 +688,12 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, |
601 | 688 | else |
602 | 689 | port->phy = pcie->base + CORE_PHY_DEFAULT_BASE(port->idx); |
603 | 690 |
|
604 | | - rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK); |
605 | | - |
606 | | - /* Assert PERST# before setting up the clock */ |
607 | | - gpiod_set_value_cansleep(reset, 1); |
608 | | - |
609 | | - ret = apple_pcie_setup_refclk(pcie, port); |
610 | | - if (ret < 0) |
611 | | - return ret; |
612 | | - |
613 | | - /* The minimal Tperst-clk value is 100us (PCIe CEM r5.0, 2.9.2) */ |
614 | | - usleep_range(100, 200); |
615 | | - |
616 | | - /* Deassert PERST# */ |
617 | | - rmw_set(PORT_PERST_OFF, port->base + pcie->hw->port_perst); |
618 | | - gpiod_set_value_cansleep(reset, 0); |
619 | | - |
620 | | - /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ |
621 | | - msleep(100); |
622 | | - |
623 | | - ret = readl_relaxed_poll_timeout(port->base + PORT_STATUS, stat, |
624 | | - stat & PORT_STATUS_READY, 100, 250000); |
625 | | - if (ret < 0) { |
626 | | - dev_err(pcie->dev, "port %pOF ready wait timeout\n", np); |
627 | | - return ret; |
| 691 | + /* link might be already brought up by u-boot, skip setup then */ |
| 692 | + link_stat = readl_relaxed(port->base + PORT_LINKSTS); |
| 693 | + if (!(link_stat & PORT_LINKSTS_UP)) { |
| 694 | + ret = apple_pcie_setup_link(pcie, port, np); |
| 695 | + if (ret) |
| 696 | + return ret; |
628 | 697 | } |
629 | 698 |
|
630 | 699 | if (pcie->hw->port_refclk) |
@@ -658,10 +727,21 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, |
658 | 727 | ret = apple_pcie_port_register_irqs(port); |
659 | 728 | WARN_ON(ret); |
660 | 729 |
|
661 | | - writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); |
| 730 | + link_stat = readl_relaxed(port->base + PORT_LINKSTS); |
| 731 | + if (!(link_stat & PORT_LINKSTS_UP)) { |
| 732 | + unsigned long timeout, left; |
| 733 | + /* start link training */ |
| 734 | + writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); |
662 | 735 |
|
663 | | - if (!wait_for_completion_timeout(&pcie->event, HZ / 10)) |
664 | | - dev_warn(pcie->dev, "%pOF link didn't come up\n", np); |
| 736 | + timeout = link_up_timeout * HZ / 1000; |
| 737 | + left = wait_for_completion_timeout(&pcie->event, timeout); |
| 738 | + if (!left) |
| 739 | + dev_warn(pcie->dev, "%pOF link didn't come up\n", np); |
| 740 | + else |
| 741 | + dev_info(pcie->dev, "%pOF link up after %ldms\n", np, |
| 742 | + (timeout - left) * 1000 / HZ); |
| 743 | + |
| 744 | + } |
665 | 745 |
|
666 | 746 | return 0; |
667 | 747 | } |
@@ -845,13 +925,47 @@ static const struct pci_ecam_ops apple_pcie_cfg_ecam_ops = { |
845 | 925 | } |
846 | 926 | }; |
847 | 927 |
|
| 928 | +static int apple_pcie_probe_port(struct device_node *np) |
| 929 | +{ |
| 930 | + struct gpio_desc *gd; |
| 931 | + |
| 932 | + /* check whether the GPPIO pin exists but leave it as is */ |
| 933 | + gd = fwnode_gpiod_get_index(of_fwnode_handle(np), "reset", 0, |
| 934 | + GPIOD_ASIS, "PERST#"); |
| 935 | + if (IS_ERR(gd)) |
| 936 | + return PTR_ERR(gd); |
| 937 | + |
| 938 | + gpiod_put(gd); |
| 939 | + |
| 940 | + gd = fwnode_gpiod_get_index(of_fwnode_handle(np), "pwren", 0, |
| 941 | + GPIOD_ASIS, "PWREN"); |
| 942 | + if (IS_ERR(gd)) { |
| 943 | + if (PTR_ERR(gd) != -ENOENT) |
| 944 | + return PTR_ERR(gd); |
| 945 | + } else { |
| 946 | + gpiod_put(gd); |
| 947 | + } |
| 948 | + |
| 949 | + return 0; |
| 950 | +} |
| 951 | + |
848 | 952 | static int apple_pcie_probe(struct platform_device *pdev) |
849 | 953 | { |
850 | 954 | struct device *dev = &pdev->dev; |
851 | 955 | struct pci_host_bridge *bridge; |
| 956 | + struct device_node *of_port; |
852 | 957 | struct apple_pcie *pcie; |
853 | 958 | int ret; |
854 | 959 |
|
| 960 | + /* Check for probe dependencies for all ports first */ |
| 961 | + for_each_available_child_of_node(dev->of_node, of_port) { |
| 962 | + ret = apple_pcie_probe_port(of_port); |
| 963 | + if (ret) { |
| 964 | + of_node_put(of_port); |
| 965 | + return dev_err_probe(dev, ret, "Port %pOF probe fail\n", of_port); |
| 966 | + } |
| 967 | + } |
| 968 | + |
855 | 969 | bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); |
856 | 970 | if (!bridge) |
857 | 971 | return -ENOMEM; |
|
0 commit comments