diff options
author | Kishon Vijay Abraham I <kishon@ti.com> | 2018-10-17 03:41:04 -0400 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-10-17 04:46:15 -0400 |
commit | 49229238ab47fa1852a89ee55d1e7fb251ee4eb0 (patch) | |
tree | 70a341f0fc41fbe5402543535a96a2ded502e2a9 /drivers/pci/controller/dwc/pci-keystone.c | |
parent | b51a625b784aa9cdac4a177560e19f0a0041ce19 (diff) |
PCI: keystone: Cleanup PHY handling
Cleanup PHY handling by using devm_phy_optional_get() to get PHYs if
the PHYs are optional, creating a device link between the PHY device
and the controller device and disable PHY on error cases here.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Diffstat (limited to 'drivers/pci/controller/dwc/pci-keystone.c')
-rw-r--r-- | drivers/pci/controller/dwc/pci-keystone.c | 122 |
1 files changed, 106 insertions, 16 deletions
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index e22328f89c84..5ae73c185c99 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c | |||
@@ -105,6 +105,9 @@ struct keystone_pcie { | |||
105 | 105 | ||
106 | int num_msi_host_irqs; | 106 | int num_msi_host_irqs; |
107 | int msi_host_irqs[MAX_MSI_HOST_IRQS]; | 107 | int msi_host_irqs[MAX_MSI_HOST_IRQS]; |
108 | int num_lanes; | ||
109 | struct phy **phy; | ||
110 | struct device_link **link; | ||
108 | struct device_node *msi_intc_np; | 111 | struct device_node *msi_intc_np; |
109 | struct irq_domain *legacy_irq_domain; | 112 | struct irq_domain *legacy_irq_domain; |
110 | struct device_node *np; | 113 | struct device_node *np; |
@@ -880,22 +883,57 @@ static const struct dw_pcie_ops ks_pcie_dw_pcie_ops = { | |||
880 | .link_up = ks_pcie_link_up, | 883 | .link_up = ks_pcie_link_up, |
881 | }; | 884 | }; |
882 | 885 | ||
883 | static int __exit ks_pcie_remove(struct platform_device *pdev) | 886 | static void ks_pcie_disable_phy(struct keystone_pcie *ks_pcie) |
884 | { | 887 | { |
885 | struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev); | 888 | int num_lanes = ks_pcie->num_lanes; |
886 | 889 | ||
887 | clk_disable_unprepare(ks_pcie->clk); | 890 | while (num_lanes--) { |
891 | phy_power_off(ks_pcie->phy[num_lanes]); | ||
892 | phy_exit(ks_pcie->phy[num_lanes]); | ||
893 | } | ||
894 | } | ||
895 | |||
896 | static int ks_pcie_enable_phy(struct keystone_pcie *ks_pcie) | ||
897 | { | ||
898 | int i; | ||
899 | int ret; | ||
900 | int num_lanes = ks_pcie->num_lanes; | ||
901 | |||
902 | for (i = 0; i < num_lanes; i++) { | ||
903 | ret = phy_init(ks_pcie->phy[i]); | ||
904 | if (ret < 0) | ||
905 | goto err_phy; | ||
906 | |||
907 | ret = phy_power_on(ks_pcie->phy[i]); | ||
908 | if (ret < 0) { | ||
909 | phy_exit(ks_pcie->phy[i]); | ||
910 | goto err_phy; | ||
911 | } | ||
912 | } | ||
888 | 913 | ||
889 | return 0; | 914 | return 0; |
915 | |||
916 | err_phy: | ||
917 | while (--i >= 0) { | ||
918 | phy_power_off(ks_pcie->phy[i]); | ||
919 | phy_exit(ks_pcie->phy[i]); | ||
920 | } | ||
921 | |||
922 | return ret; | ||
890 | } | 923 | } |
891 | 924 | ||
892 | static int __init ks_pcie_probe(struct platform_device *pdev) | 925 | static int __init ks_pcie_probe(struct platform_device *pdev) |
893 | { | 926 | { |
894 | struct device *dev = &pdev->dev; | 927 | struct device *dev = &pdev->dev; |
928 | struct device_node *np = dev->of_node; | ||
895 | struct dw_pcie *pci; | 929 | struct dw_pcie *pci; |
896 | struct keystone_pcie *ks_pcie; | 930 | struct keystone_pcie *ks_pcie; |
897 | struct phy *phy; | 931 | struct device_link **link; |
932 | struct phy **phy; | ||
933 | u32 num_lanes; | ||
934 | char name[10]; | ||
898 | int ret; | 935 | int ret; |
936 | int i; | ||
899 | 937 | ||
900 | ks_pcie = devm_kzalloc(dev, sizeof(*ks_pcie), GFP_KERNEL); | 938 | ks_pcie = devm_kzalloc(dev, sizeof(*ks_pcie), GFP_KERNEL); |
901 | if (!ks_pcie) | 939 | if (!ks_pcie) |
@@ -908,29 +946,59 @@ static int __init ks_pcie_probe(struct platform_device *pdev) | |||
908 | pci->dev = dev; | 946 | pci->dev = dev; |
909 | pci->ops = &ks_pcie_dw_pcie_ops; | 947 | pci->ops = &ks_pcie_dw_pcie_ops; |
910 | 948 | ||
911 | ks_pcie->pci = pci; | 949 | ret = of_property_read_u32(np, "num-lanes", &num_lanes); |
950 | if (ret) | ||
951 | num_lanes = 1; | ||
912 | 952 | ||
913 | /* initialize SerDes Phy if present */ | 953 | phy = devm_kzalloc(dev, sizeof(*phy) * num_lanes, GFP_KERNEL); |
914 | phy = devm_phy_get(dev, "pcie-phy"); | 954 | if (!phy) |
915 | if (PTR_ERR_OR_ZERO(phy) == -EPROBE_DEFER) | 955 | return -ENOMEM; |
916 | return PTR_ERR(phy); | ||
917 | 956 | ||
918 | if (!IS_ERR_OR_NULL(phy)) { | 957 | link = devm_kzalloc(dev, sizeof(*link) * num_lanes, GFP_KERNEL); |
919 | ret = phy_init(phy); | 958 | if (!link) |
920 | if (ret < 0) | 959 | return -ENOMEM; |
921 | return ret; | 960 | |
961 | for (i = 0; i < num_lanes; i++) { | ||
962 | snprintf(name, sizeof(name), "pcie-phy%d", i); | ||
963 | phy[i] = devm_phy_optional_get(dev, name); | ||
964 | if (IS_ERR(phy[i])) { | ||
965 | ret = PTR_ERR(phy[i]); | ||
966 | goto err_link; | ||
967 | } | ||
968 | |||
969 | if (!phy[i]) | ||
970 | continue; | ||
971 | |||
972 | link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS); | ||
973 | if (!link[i]) { | ||
974 | ret = -EINVAL; | ||
975 | goto err_link; | ||
976 | } | ||
977 | } | ||
978 | |||
979 | ks_pcie->np = np; | ||
980 | ks_pcie->pci = pci; | ||
981 | ks_pcie->link = link; | ||
982 | ks_pcie->num_lanes = num_lanes; | ||
983 | ks_pcie->phy = phy; | ||
984 | |||
985 | ret = ks_pcie_enable_phy(ks_pcie); | ||
986 | if (ret) { | ||
987 | dev_err(dev, "failed to enable phy\n"); | ||
988 | goto err_link; | ||
922 | } | 989 | } |
923 | 990 | ||
924 | ks_pcie->np = dev->of_node; | ||
925 | platform_set_drvdata(pdev, ks_pcie); | 991 | platform_set_drvdata(pdev, ks_pcie); |
926 | ks_pcie->clk = devm_clk_get(dev, "pcie"); | 992 | ks_pcie->clk = devm_clk_get(dev, "pcie"); |
927 | if (IS_ERR(ks_pcie->clk)) { | 993 | if (IS_ERR(ks_pcie->clk)) { |
928 | dev_err(dev, "Failed to get pcie rc clock\n"); | 994 | dev_err(dev, "Failed to get pcie rc clock\n"); |
929 | return PTR_ERR(ks_pcie->clk); | 995 | ret = PTR_ERR(ks_pcie->clk); |
996 | goto err_phy; | ||
930 | } | 997 | } |
998 | |||
931 | ret = clk_prepare_enable(ks_pcie->clk); | 999 | ret = clk_prepare_enable(ks_pcie->clk); |
932 | if (ret) | 1000 | if (ret) |
933 | return ret; | 1001 | goto err_phy; |
934 | 1002 | ||
935 | ret = ks_pcie_add_pcie_port(ks_pcie, pdev); | 1003 | ret = ks_pcie_add_pcie_port(ks_pcie, pdev); |
936 | if (ret < 0) | 1004 | if (ret < 0) |
@@ -940,9 +1008,31 @@ static int __init ks_pcie_probe(struct platform_device *pdev) | |||
940 | fail_clk: | 1008 | fail_clk: |
941 | clk_disable_unprepare(ks_pcie->clk); | 1009 | clk_disable_unprepare(ks_pcie->clk); |
942 | 1010 | ||
1011 | err_phy: | ||
1012 | ks_pcie_disable_phy(ks_pcie); | ||
1013 | |||
1014 | err_link: | ||
1015 | while (--i >= 0 && link[i]) | ||
1016 | device_link_del(link[i]); | ||
1017 | |||
943 | return ret; | 1018 | return ret; |
944 | } | 1019 | } |
945 | 1020 | ||
1021 | static int __exit ks_pcie_remove(struct platform_device *pdev) | ||
1022 | { | ||
1023 | struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev); | ||
1024 | struct device_link **link = ks_pcie->link; | ||
1025 | int num_lanes = ks_pcie->num_lanes; | ||
1026 | |||
1027 | clk_disable_unprepare(ks_pcie->clk); | ||
1028 | ks_pcie_disable_phy(ks_pcie); | ||
1029 | |||
1030 | while (num_lanes--) | ||
1031 | device_link_del(link[num_lanes]); | ||
1032 | |||
1033 | return 0; | ||
1034 | } | ||
1035 | |||
946 | static struct platform_driver ks_pcie_driver __refdata = { | 1036 | static struct platform_driver ks_pcie_driver __refdata = { |
947 | .probe = ks_pcie_probe, | 1037 | .probe = ks_pcie_probe, |
948 | .remove = __exit_p(ks_pcie_remove), | 1038 | .remove = __exit_p(ks_pcie_remove), |