|
7 | 7 | */
|
8 | 8 |
|
9 | 9 | #include <linux/clk.h>
|
| 10 | +#include <linux/clk-provider.h> |
| 11 | +#include <linux/container_of.h> |
10 | 12 | #include <linux/delay.h>
|
11 | 13 | #include <linux/gpio/consumer.h>
|
12 | 14 | #include <linux/io.h>
|
|
22 | 24 | #include "../../pci.h"
|
23 | 25 | #include "pcie-cadence.h"
|
24 | 26 |
|
| 27 | +#define cdns_pcie_to_rc(p) container_of(p, struct cdns_pcie_rc, pcie) |
| 28 | + |
25 | 29 | #define ENABLE_REG_SYS_2 0x108
|
26 | 30 | #define STATUS_REG_SYS_2 0x508
|
27 | 31 | #define STATUS_CLR_REG_SYS_2 0x708
|
@@ -568,12 +572,12 @@ static int j721e_pcie_probe(struct platform_device *pdev)
|
568 | 572 | pcie->refclk = clk;
|
569 | 573 |
|
570 | 574 | /*
|
571 |
| - * "Power Sequencing and Reset Signal Timings" table in |
572 |
| - * PCI EXPRESS CARD ELECTROMECHANICAL SPECIFICATION, REV. 3.0 |
573 |
| - * indicates PERST# should be deasserted after minimum of 100us |
574 |
| - * once REFCLK is stable. The REFCLK to the connector in RC |
575 |
| - * mode is selected while enabling the PHY. So deassert PERST# |
576 |
| - * after 100 us. |
| 575 | + * The "Power Sequencing and Reset Signal Timings" table of the |
| 576 | + * PCI Express Card Electromechanical Specification, Revision |
| 577 | + * 5.1, Section 2.9.2, Symbol "T_PERST-CLK", indicates PERST# |
| 578 | + * should be deasserted after minimum of 100us once REFCLK is |
| 579 | + * stable. The REFCLK to the connector in RC mode is selected |
| 580 | + * while enabling the PHY. So deassert PERST# after 100 us. |
577 | 581 | */
|
578 | 582 | if (gpiod) {
|
579 | 583 | fsleep(PCIE_T_PERST_CLK_US);
|
@@ -625,13 +629,95 @@ static void j721e_pcie_remove(struct platform_device *pdev)
|
625 | 629 | pm_runtime_disable(dev);
|
626 | 630 | }
|
627 | 631 |
|
| 632 | +static int j721e_pcie_suspend_noirq(struct device *dev) |
| 633 | +{ |
| 634 | + struct j721e_pcie *pcie = dev_get_drvdata(dev); |
| 635 | + |
| 636 | + if (pcie->mode == PCI_MODE_RC) { |
| 637 | + gpiod_set_value_cansleep(pcie->reset_gpio, 0); |
| 638 | + clk_disable_unprepare(pcie->refclk); |
| 639 | + } |
| 640 | + |
| 641 | + cdns_pcie_disable_phy(pcie->cdns_pcie); |
| 642 | + |
| 643 | + return 0; |
| 644 | +} |
| 645 | + |
| 646 | +static int j721e_pcie_resume_noirq(struct device *dev) |
| 647 | +{ |
| 648 | + struct j721e_pcie *pcie = dev_get_drvdata(dev); |
| 649 | + struct cdns_pcie *cdns_pcie = pcie->cdns_pcie; |
| 650 | + int ret; |
| 651 | + |
| 652 | + ret = j721e_pcie_ctrl_init(pcie); |
| 653 | + if (ret < 0) |
| 654 | + return ret; |
| 655 | + |
| 656 | + j721e_pcie_config_link_irq(pcie); |
| 657 | + |
| 658 | + /* |
| 659 | + * This is not called explicitly in the probe, it is called by |
| 660 | + * cdns_pcie_init_phy(). |
| 661 | + */ |
| 662 | + ret = cdns_pcie_enable_phy(pcie->cdns_pcie); |
| 663 | + if (ret < 0) |
| 664 | + return ret; |
| 665 | + |
| 666 | + if (pcie->mode == PCI_MODE_RC) { |
| 667 | + struct cdns_pcie_rc *rc = cdns_pcie_to_rc(cdns_pcie); |
| 668 | + |
| 669 | + ret = clk_prepare_enable(pcie->refclk); |
| 670 | + if (ret < 0) |
| 671 | + return ret; |
| 672 | + |
| 673 | + /* |
| 674 | + * The "Power Sequencing and Reset Signal Timings" table of the |
| 675 | + * PCI Express Card Electromechanical Specification, Revision |
| 676 | + * 5.1, Section 2.9.2, Symbol "T_PERST-CLK", indicates PERST# |
| 677 | + * should be deasserted after minimum of 100us once REFCLK is |
| 678 | + * stable. The REFCLK to the connector in RC mode is selected |
| 679 | + * while enabling the PHY. So deassert PERST# after 100 us. |
| 680 | + */ |
| 681 | + if (pcie->reset_gpio) { |
| 682 | + fsleep(PCIE_T_PERST_CLK_US); |
| 683 | + gpiod_set_value_cansleep(pcie->reset_gpio, 1); |
| 684 | + } |
| 685 | + |
| 686 | + ret = cdns_pcie_host_link_setup(rc); |
| 687 | + if (ret < 0) { |
| 688 | + clk_disable_unprepare(pcie->refclk); |
| 689 | + return ret; |
| 690 | + } |
| 691 | + |
| 692 | + /* |
| 693 | + * Reset internal status of BARs to force reinitialization in |
| 694 | + * cdns_pcie_host_init(). |
| 695 | + */ |
| 696 | + for (enum cdns_pcie_rp_bar bar = RP_BAR0; bar <= RP_NO_BAR; bar++) |
| 697 | + rc->avail_ib_bar[bar] = true; |
| 698 | + |
| 699 | + ret = cdns_pcie_host_init(rc); |
| 700 | + if (ret) { |
| 701 | + clk_disable_unprepare(pcie->refclk); |
| 702 | + return ret; |
| 703 | + } |
| 704 | + } |
| 705 | + |
| 706 | + return 0; |
| 707 | +} |
| 708 | + |
| 709 | +static DEFINE_NOIRQ_DEV_PM_OPS(j721e_pcie_pm_ops, |
| 710 | + j721e_pcie_suspend_noirq, |
| 711 | + j721e_pcie_resume_noirq); |
| 712 | + |
628 | 713 | static struct platform_driver j721e_pcie_driver = {
|
629 | 714 | .probe = j721e_pcie_probe,
|
630 | 715 | .remove_new = j721e_pcie_remove,
|
631 | 716 | .driver = {
|
632 | 717 | .name = "j721e-pcie",
|
633 | 718 | .of_match_table = of_j721e_pcie_match,
|
634 | 719 | .suppress_bind_attrs = true,
|
| 720 | + .pm = pm_sleep_ptr(&j721e_pcie_pm_ops), |
635 | 721 | },
|
636 | 722 | };
|
637 | 723 | builtin_platform_driver(j721e_pcie_driver);
|
0 commit comments