diff options
author | Richard Zhu <r65037@freescale.com> | 2014-05-21 02:13:13 -0400 |
---|---|---|
committer | Richard Zhu <r65037@freescale.com> | 2014-05-26 01:18:35 -0400 |
commit | 671cc6a3c1f7ce2da92bccdf00d3fa9d9fb1633b (patch) | |
tree | 0f385e67c3d7b18c9f5eacf1f635ee4d01f1356a | |
parent | f64f01111d4fa3ec0a5fe6477be3a79ac1efef26 (diff) |
ENGR00314570-2 pcie:add pcie power control on imx6sx
imx6sx pcie has standalone ldo domain, add the power
control routines.
- pcie pm recovery works when imx6sx pcie is used as rc.
- pcie pm recovery works on both rc and ep modes.
- l2 mode had been validated on imx6sx sdb(rc) and
e1000e (ep) environment.
- fastmix and megamix can be turn off and turn on, during
system suspend/resume.
- hw:
- imx6sx sdb board.
- intel e1000e nic
- xhci pcie2usb device
Signed-off-by: Richard Zhu <r65037@freescale.com>
-rw-r--r-- | drivers/pci/host/Kconfig | 4 | ||||
-rw-r--r-- | drivers/pci/host/pci-imx6.c | 250 |
2 files changed, 200 insertions, 54 deletions
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 120714876e83..0512d1021a4f 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig | |||
@@ -20,6 +20,10 @@ config PCI_IMX6 | |||
20 | select PCIEPORTBUS | 20 | select PCIEPORTBUS |
21 | select PCIE_DW | 21 | select PCIE_DW |
22 | 22 | ||
23 | config PCI_IMX6SX_EXTREMELY_PWR_SAVE | ||
24 | bool "Freescale i.MX6SX PCIe controller extremely power save mode" | ||
25 | depends on PCI_IMX6 | ||
26 | |||
23 | config EP_MODE_IN_EP_RC_SYS | 27 | config EP_MODE_IN_EP_RC_SYS |
24 | bool "PCI Express EP mode in the IMX6 RC/EP interconnection system" | 28 | bool "PCI Express EP mode in the IMX6 RC/EP interconnection system" |
25 | depends on PCI_IMX6 | 29 | depends on PCI_IMX6 |
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index f893c1998a9b..064d40f3ae11 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/regmap.h> | 27 | #include <linux/regmap.h> |
28 | #include <linux/resource.h> | 28 | #include <linux/resource.h> |
29 | #include <linux/signal.h> | 29 | #include <linux/signal.h> |
30 | #include <linux/syscore_ops.h> | ||
30 | #include <linux/types.h> | 31 | #include <linux/types.h> |
31 | #include <linux/busfreq-imx6.h> | 32 | #include <linux/busfreq-imx6.h> |
32 | #include <linux/regulator/consumer.h> | 33 | #include <linux/regulator/consumer.h> |
@@ -71,6 +72,7 @@ struct imx6_pcie { | |||
71 | struct regulator *pcie_regulator; | 72 | struct regulator *pcie_regulator; |
72 | void __iomem *mem_base; | 73 | void __iomem *mem_base; |
73 | }; | 74 | }; |
75 | static struct imx6_pcie *imx6_pcie; | ||
74 | 76 | ||
75 | /* PCIe Port Logic registers (memory-mapped) */ | 77 | /* PCIe Port Logic registers (memory-mapped) */ |
76 | #define PL_OFFSET 0x700 | 78 | #define PL_OFFSET 0x700 |
@@ -288,12 +290,9 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) | |||
288 | goto err_pcie_axi; | 290 | goto err_pcie_axi; |
289 | } | 291 | } |
290 | 292 | ||
291 | /* allow the clocks to stabilize */ | ||
292 | usleep_range(200, 500); | ||
293 | |||
294 | if (gpio_is_valid(imx6_pcie->reset_gpio)) { | 293 | if (gpio_is_valid(imx6_pcie->reset_gpio)) { |
295 | gpio_set_value(imx6_pcie->reset_gpio, 0); | 294 | gpio_set_value(imx6_pcie->reset_gpio, 0); |
296 | msleep(100); | 295 | mdelay(1); |
297 | gpio_set_value(imx6_pcie->reset_gpio, 1); | 296 | gpio_set_value(imx6_pcie->reset_gpio, 1); |
298 | } | 297 | } |
299 | 298 | ||
@@ -658,9 +657,198 @@ static const struct of_device_id imx6_pcie_of_match[] = { | |||
658 | }; | 657 | }; |
659 | MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); | 658 | MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); |
660 | 659 | ||
660 | static void imx6_pcie_setup_ep(struct pcie_port *pp) | ||
661 | { | ||
662 | /* CMD reg:I/O space, MEM space, and Bus Master Enable */ | ||
663 | writel(readl(pp->dbi_base + PCI_COMMAND) | ||
664 | | PCI_COMMAND_IO | ||
665 | | PCI_COMMAND_MEMORY | ||
666 | | PCI_COMMAND_MASTER, | ||
667 | pp->dbi_base + PCI_COMMAND); | ||
668 | |||
669 | /* | ||
670 | * configure the class_rev(emaluate one memory ram ep device), | ||
671 | * bar0 and bar1 of ep | ||
672 | */ | ||
673 | writel(0xdeadbeaf, pp->dbi_base + PCI_VENDOR_ID); | ||
674 | writel(readl(pp->dbi_base + PCI_CLASS_REVISION) | ||
675 | | (PCI_CLASS_MEMORY_RAM << 16), | ||
676 | pp->dbi_base + PCI_CLASS_REVISION); | ||
677 | writel(0xdeadbeaf, pp->dbi_base | ||
678 | + PCI_SUBSYSTEM_VENDOR_ID); | ||
679 | |||
680 | /* 32bit none-prefetchable 8M bytes memory on bar0 */ | ||
681 | writel(0x0, pp->dbi_base + PCI_BASE_ADDRESS_0); | ||
682 | writel(SZ_8M - 1, pp->dbi_base + (1 << 12) | ||
683 | + PCI_BASE_ADDRESS_0); | ||
684 | |||
685 | /* None used bar1 */ | ||
686 | writel(0x0, pp->dbi_base + PCI_BASE_ADDRESS_1); | ||
687 | writel(0, pp->dbi_base + (1 << 12) + PCI_BASE_ADDRESS_1); | ||
688 | |||
689 | /* 4K bytes IO on bar2 */ | ||
690 | writel(0x1, pp->dbi_base + PCI_BASE_ADDRESS_2); | ||
691 | writel(SZ_4K - 1, pp->dbi_base + (1 << 12) + | ||
692 | PCI_BASE_ADDRESS_2); | ||
693 | |||
694 | /* | ||
695 | * 32bit prefetchable 1M bytes memory on bar3 | ||
696 | * FIXME BAR MASK3 is not changable, the size | ||
697 | * is fixed to 256 bytes. | ||
698 | */ | ||
699 | writel(0x8, pp->dbi_base + PCI_BASE_ADDRESS_3); | ||
700 | writel(SZ_1M - 1, pp->dbi_base + (1 << 12) | ||
701 | + PCI_BASE_ADDRESS_3); | ||
702 | |||
703 | /* | ||
704 | * 64bit prefetchable 1M bytes memory on bar4-5. | ||
705 | * FIXME BAR4,5 are not enabled yet | ||
706 | */ | ||
707 | writel(0xc, pp->dbi_base + PCI_BASE_ADDRESS_4); | ||
708 | writel(SZ_1M - 1, pp->dbi_base + (1 << 12) | ||
709 | + PCI_BASE_ADDRESS_4); | ||
710 | writel(0, pp->dbi_base + (1 << 12) + PCI_BASE_ADDRESS_5); | ||
711 | } | ||
712 | |||
713 | #ifdef CONFIG_PM_SLEEP | ||
714 | static int pci_imx_suspend(void) | ||
715 | { | ||
716 | int rc = 0; | ||
717 | |||
718 | if (is_imx6sx_pcie(imx6_pcie)) { | ||
719 | if (IS_ENABLED(CONFIG_PCI_IMX6SX_EXTREMELY_PWR_SAVE)) { | ||
720 | /* Disable clks and power down PCIe PHY */ | ||
721 | clk_disable_unprepare(imx6_pcie->pcie_axi); | ||
722 | if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS) | ||
723 | && !IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS)) | ||
724 | clk_disable_unprepare(imx6_pcie->lvds_gate); | ||
725 | clk_disable_unprepare(imx6_pcie->pcie_ref_125m); | ||
726 | clk_disable_unprepare(imx6_pcie->dis_axi); | ||
727 | release_bus_freq(BUS_FREQ_HIGH); | ||
728 | |||
729 | /* Put PCIe PHY to be isolation */ | ||
730 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR0, | ||
731 | BIT(6), 1 << 6); | ||
732 | |||
733 | /* | ||
734 | * Power down PCIe PHY. | ||
735 | */ | ||
736 | regulator_disable(imx6_pcie->pcie_regulator); | ||
737 | } else { | ||
738 | /* PM_TURN_OFF */ | ||
739 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
740 | BIT(16), 1 << 16); | ||
741 | udelay(10); | ||
742 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
743 | BIT(16), 0 << 16); | ||
744 | clk_disable_unprepare(imx6_pcie->pcie_axi); | ||
745 | if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS) | ||
746 | && !IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS)) | ||
747 | clk_disable_unprepare(imx6_pcie->lvds_gate); | ||
748 | clk_disable_unprepare(imx6_pcie->pcie_ref_125m); | ||
749 | clk_disable_unprepare(imx6_pcie->dis_axi); | ||
750 | release_bus_freq(BUS_FREQ_HIGH); | ||
751 | } | ||
752 | } | ||
753 | |||
754 | return rc; | ||
755 | } | ||
756 | |||
757 | static void pci_imx_resume(void) | ||
758 | { | ||
759 | struct pcie_port *pp = &imx6_pcie->pp; | ||
760 | |||
761 | if (is_imx6sx_pcie(imx6_pcie)) { | ||
762 | if (IS_ENABLED(CONFIG_PCI_IMX6SX_EXTREMELY_PWR_SAVE)) { | ||
763 | /* Power up PCIe PHY, and so on again */ | ||
764 | imx6_pcie_init_phy(pp); | ||
765 | imx6_pcie_deassert_core_reset(pp); | ||
766 | |||
767 | /* | ||
768 | * iMX6SX PCIe has the stand-alone power domain. | ||
769 | * refer to the initialization for iMX6SX PCIe, | ||
770 | * release the PCIe PHY reset here, | ||
771 | * before LTSSM enable is set | ||
772 | * . | ||
773 | */ | ||
774 | regmap_update_bits(imx6_pcie->iomuxc_gpr, | ||
775 | IOMUXC_GPR5, BIT(19), 0 << 19); | ||
776 | |||
777 | if (IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS)) { | ||
778 | imx6_pcie_setup_ep(pp); | ||
779 | } else { | ||
780 | /* | ||
781 | * CMD reg:I/O space, MEM space, | ||
782 | * and Bus Master | ||
783 | */ | ||
784 | writel(readl(pp->dbi_base + PCI_COMMAND) | ||
785 | | PCI_COMMAND_IO | ||
786 | | PCI_COMMAND_MEMORY | ||
787 | | PCI_COMMAND_MASTER, | ||
788 | pp->dbi_base + PCI_COMMAND); | ||
789 | /* | ||
790 | * Set the CLASS_REV of RC CFG header to | ||
791 | * PCI_CLASS_BRIDGE_PCI | ||
792 | */ | ||
793 | writel(readl(pp->dbi_base + PCI_CLASS_REVISION) | ||
794 | | (PCI_CLASS_BRIDGE_PCI << 16), | ||
795 | pp->dbi_base + PCI_CLASS_REVISION); | ||
796 | } | ||
797 | |||
798 | /* assert LTSSM enable */ | ||
799 | regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, | ||
800 | IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); | ||
801 | } else { | ||
802 | /* Wake up re-enable clks */ | ||
803 | request_bus_freq(BUS_FREQ_HIGH); | ||
804 | clk_prepare_enable(imx6_pcie->dis_axi); | ||
805 | if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS) | ||
806 | && !IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS)) | ||
807 | clk_prepare_enable(imx6_pcie->lvds_gate); | ||
808 | clk_prepare_enable(imx6_pcie->pcie_ref_125m); | ||
809 | clk_prepare_enable(imx6_pcie->pcie_axi); | ||
810 | |||
811 | /* reset iMX6SX PCIe */ | ||
812 | regmap_update_bits(imx6_pcie->iomuxc_gpr, | ||
813 | IOMUXC_GPR5, BIT(18), 1 << 18); | ||
814 | |||
815 | regmap_update_bits(imx6_pcie->iomuxc_gpr, | ||
816 | IOMUXC_GPR5, BIT(18), 0 << 18); | ||
817 | |||
818 | /* | ||
819 | * controller maybe turn off, re-configure again | ||
820 | * Set the CLASS_REV of RC CFG header to | ||
821 | * PCI_CLASS_BRIDGE_PCI | ||
822 | */ | ||
823 | writel(readl(pp->dbi_base + PCI_CLASS_REVISION) | ||
824 | | (PCI_CLASS_BRIDGE_PCI << 16), | ||
825 | pp->dbi_base + PCI_CLASS_REVISION); | ||
826 | |||
827 | dw_pcie_setup_rc(pp); | ||
828 | |||
829 | /* reset iMX6SX PCIe */ | ||
830 | regmap_update_bits(imx6_pcie->iomuxc_gpr, | ||
831 | IOMUXC_GPR5, BIT(18), 1 << 18); | ||
832 | |||
833 | regmap_update_bits(imx6_pcie->iomuxc_gpr, | ||
834 | IOMUXC_GPR5, BIT(18), 0 << 18); | ||
835 | |||
836 | /* RESET EP */ | ||
837 | gpio_set_value(imx6_pcie->reset_gpio, 0); | ||
838 | udelay(10); | ||
839 | gpio_set_value(imx6_pcie->reset_gpio, 1); | ||
840 | } | ||
841 | } | ||
842 | } | ||
843 | #endif | ||
844 | |||
845 | static struct syscore_ops pci_imx_syscore_ops = { | ||
846 | .suspend = pci_imx_suspend, | ||
847 | .resume = pci_imx_resume, | ||
848 | }; | ||
849 | |||
661 | static int __init imx6_pcie_probe(struct platform_device *pdev) | 850 | static int __init imx6_pcie_probe(struct platform_device *pdev) |
662 | { | 851 | { |
663 | struct imx6_pcie *imx6_pcie; | ||
664 | struct pcie_port *pp; | 852 | struct pcie_port *pp; |
665 | const struct of_device_id *of_id = | 853 | const struct of_device_id *of_id = |
666 | of_match_device(imx6_pcie_of_match, &pdev->dev); | 854 | of_match_device(imx6_pcie_of_match, &pdev->dev); |
@@ -874,55 +1062,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) | |||
874 | usleep_range(10, 20); | 1062 | usleep_range(10, 20); |
875 | } while ((readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & 0x10) == 0); | 1063 | } while ((readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & 0x10) == 0); |
876 | 1064 | ||
877 | /* CMD reg:I/O space, MEM space, and Bus Master Enable */ | 1065 | imx6_pcie_setup_ep(pp); |
878 | writel(readl(pp->dbi_base + PCI_COMMAND) | ||
879 | | PCI_COMMAND_IO | ||
880 | | PCI_COMMAND_MEMORY | ||
881 | | PCI_COMMAND_MASTER, | ||
882 | pp->dbi_base + PCI_COMMAND); | ||
883 | |||
884 | /* | ||
885 | * configure the class_rev(emaluate one memory ram ep device), | ||
886 | * bar0 and bar1 of ep | ||
887 | */ | ||
888 | writel(0xdeadbeaf, pp->dbi_base + PCI_VENDOR_ID); | ||
889 | writel(readl(pp->dbi_base + PCI_CLASS_REVISION) | ||
890 | | (PCI_CLASS_MEMORY_RAM << 16), | ||
891 | pp->dbi_base + PCI_CLASS_REVISION); | ||
892 | writel(0xdeadbeaf, pp->dbi_base | ||
893 | + PCI_SUBSYSTEM_VENDOR_ID); | ||
894 | |||
895 | /* 32bit none-prefetchable 8M bytes memory on bar0 */ | ||
896 | writel(0x0, pp->dbi_base + PCI_BASE_ADDRESS_0); | ||
897 | writel(SZ_8M - 1, pp->dbi_base + (1 << 12) | ||
898 | + PCI_BASE_ADDRESS_0); | ||
899 | |||
900 | /* None used bar1 */ | ||
901 | writel(0x0, pp->dbi_base + PCI_BASE_ADDRESS_1); | ||
902 | writel(0, pp->dbi_base + (1 << 12) + PCI_BASE_ADDRESS_1); | ||
903 | |||
904 | /* 4K bytes IO on bar2 */ | ||
905 | writel(0x1, pp->dbi_base + PCI_BASE_ADDRESS_2); | ||
906 | writel(SZ_4K - 1, pp->dbi_base + (1 << 12) + | ||
907 | PCI_BASE_ADDRESS_2); | ||
908 | |||
909 | /* | ||
910 | * 32bit prefetchable 1M bytes memory on bar3 | ||
911 | * FIXME BAR MASK3 is not changable, the size | ||
912 | * is fixed to 256 bytes. | ||
913 | */ | ||
914 | writel(0x8, pp->dbi_base + PCI_BASE_ADDRESS_3); | ||
915 | writel(SZ_1M - 1, pp->dbi_base + (1 << 12) | ||
916 | + PCI_BASE_ADDRESS_3); | ||
917 | |||
918 | /* | ||
919 | * 64bit prefetchable 1M bytes memory on bar4-5. | ||
920 | * FIXME BAR4,5 are not enabled yet | ||
921 | */ | ||
922 | writel(0xc, pp->dbi_base + PCI_BASE_ADDRESS_4); | ||
923 | writel(SZ_1M - 1, pp->dbi_base + (1 << 12) | ||
924 | + PCI_BASE_ADDRESS_4); | ||
925 | writel(0, pp->dbi_base + (1 << 12) + PCI_BASE_ADDRESS_5); | ||
926 | 1066 | ||
927 | /* Re-setup the iATU */ | 1067 | /* Re-setup the iATU */ |
928 | imx_pcie_regions_setup(&pdev->dev); | 1068 | imx_pcie_regions_setup(&pdev->dev); |
@@ -978,6 +1118,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) | |||
978 | /* Re-setup the iATU */ | 1118 | /* Re-setup the iATU */ |
979 | imx_pcie_regions_setup(&pdev->dev); | 1119 | imx_pcie_regions_setup(&pdev->dev); |
980 | } | 1120 | } |
1121 | |||
1122 | register_syscore_ops(&pci_imx_syscore_ops); | ||
981 | return 0; | 1123 | return 0; |
982 | 1124 | ||
983 | err: | 1125 | err: |