PCI: kirin: Add support for a PHY layer
authorMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Thu, 21 Oct 2021 10:45:09 +0000 (11:45 +0100)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 2 Nov 2021 15:43:37 +0000 (10:43 -0500)
The pcie-kirin driver contains both PHY and generic PCI driver.

The best would be, instead, to support a PCI PHY driver, making the driver
more generic.

However, it is too late to remove the Kirin 960 PHY, as a change like that
would make the DT schema incompatible with past versions.

So, add support for an external PHY driver without removing the existing
Kirin 960 PHY from it.

Link: https://lore.kernel.org/r/f38361df2e9d0dc5a38ff942b631f7fef64cdc12.1634812676.git.mchehab+huawei@kernel.org
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Kishon Vijay Abraham I <kishon@ti.com>
Acked-by: Xiaowei Song <songxiaowei@hisilicon.com>
drivers/pci/controller/dwc/pcie-kirin.c

index b4063a3434dfcd990a39f4b01fc99770cd9b21ee..91a7c096bf8f8847b375a71ea3932f72a33859ac 100644 (file)
@@ -8,16 +8,18 @@
  * Author: Xiaowei Song <songxiaowei@huawei.com>
  */
 
-#include <linux/compiler.h>
 #include <linux/clk.h>
+#include <linux/compiler.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/mfd/syscon.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/of_gpio.h>
 #include <linux/of_pci.h>
+#include <linux/phy/phy.h>
 #include <linux/pci.h>
 #include <linux/pci_regs.h>
 #include <linux/platform_device.h>
 #define PCIE_DEBOUNCE_PARAM    0xF0F400
 #define PCIE_OE_BYPASS         (0x3 << 28)
 
+enum pcie_kirin_phy_type {
+       PCIE_KIRIN_INTERNAL_PHY,
+       PCIE_KIRIN_EXTERNAL_PHY
+};
+
 struct kirin_pcie {
+       enum pcie_kirin_phy_type        type;
+
        struct dw_pcie  *pci;
        struct phy      *phy;
        void __iomem    *apb_base;
-       void            *phy_priv;      /* Needed for Kirin 960 PHY */
+       void            *phy_priv;      /* only for PCIE_KIRIN_INTERNAL_PHY */
 };
 
 /*
@@ -476,8 +485,63 @@ static const struct dw_pcie_host_ops kirin_pcie_host_ops = {
        .host_init = kirin_pcie_host_init,
 };
 
+static int kirin_pcie_power_on(struct platform_device *pdev,
+                              struct kirin_pcie *kirin_pcie)
+{
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       if (kirin_pcie->type == PCIE_KIRIN_INTERNAL_PHY) {
+               ret = hi3660_pcie_phy_init(pdev, kirin_pcie);
+               if (ret)
+                       return ret;
+
+               return hi3660_pcie_phy_power_on(kirin_pcie);
+       }
+
+       kirin_pcie->phy = devm_of_phy_get(dev, dev->of_node, NULL);
+       if (IS_ERR(kirin_pcie->phy))
+               return PTR_ERR(kirin_pcie->phy);
+
+       ret = phy_init(kirin_pcie->phy);
+       if (ret)
+               goto err;
+
+       ret = phy_power_on(kirin_pcie->phy);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       phy_exit(kirin_pcie->phy);
+       return ret;
+}
+
+static int __exit kirin_pcie_remove(struct platform_device *pdev)
+{
+       struct kirin_pcie *kirin_pcie = platform_get_drvdata(pdev);
+
+       if (kirin_pcie->type == PCIE_KIRIN_INTERNAL_PHY)
+               return 0;
+
+       phy_power_off(kirin_pcie->phy);
+       phy_exit(kirin_pcie->phy);
+
+       return 0;
+}
+
+static const struct of_device_id kirin_pcie_match[] = {
+       {
+               .compatible = "hisilicon,kirin960-pcie",
+               .data = (void *)PCIE_KIRIN_INTERNAL_PHY
+       },
+       {},
+};
+
 static int kirin_pcie_probe(struct platform_device *pdev)
 {
+       enum pcie_kirin_phy_type phy_type;
+       const struct of_device_id *of_id;
        struct device *dev = &pdev->dev;
        struct kirin_pcie *kirin_pcie;
        struct dw_pcie *pci;
@@ -488,6 +552,14 @@ static int kirin_pcie_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       of_id = of_match_device(kirin_pcie_match, dev);
+       if (!of_id) {
+               dev_err(dev, "OF data missing\n");
+               return -EINVAL;
+       }
+
+       phy_type = (long)of_id->data;
+
        kirin_pcie = devm_kzalloc(dev, sizeof(struct kirin_pcie), GFP_KERNEL);
        if (!kirin_pcie)
                return -ENOMEM;
@@ -500,31 +572,24 @@ static int kirin_pcie_probe(struct platform_device *pdev)
        pci->ops = &kirin_dw_pcie_ops;
        pci->pp.ops = &kirin_pcie_host_ops;
        kirin_pcie->pci = pci;
-
-       ret = hi3660_pcie_phy_init(pdev, kirin_pcie);
-       if (ret)
-               return ret;
+       kirin_pcie->type = phy_type;
 
        ret = kirin_pcie_get_resource(kirin_pcie, pdev);
        if (ret)
                return ret;
 
-       ret = hi3660_pcie_phy_power_on(kirin_pcie);
+       platform_set_drvdata(pdev, kirin_pcie);
+
+       ret = kirin_pcie_power_on(pdev, kirin_pcie);
        if (ret)
                return ret;
 
-       platform_set_drvdata(pdev, kirin_pcie);
-
        return dw_pcie_host_init(&pci->pp);
 }
 
-static const struct of_device_id kirin_pcie_match[] = {
-       { .compatible = "hisilicon,kirin960-pcie" },
-       {},
-};
-
 static struct platform_driver kirin_pcie_driver = {
        .probe                  = kirin_pcie_probe,
+       .remove                 = __exit_p(kirin_pcie_remove),
        .driver                 = {
                .name                   = "kirin-pcie",
                .of_match_table         = kirin_pcie_match,