From 7e37dc1db594d5a4ed062dbaf51ef89596a9df8a Mon Sep 17 00:00:00 2001 From: Alan Douglas Date: Mon, 25 Jun 2018 09:30:49 +0100 Subject: PCI: cadence: Update cdns_pcie_writel() function signature cdns_pcie_writel() writes a long value; change the value parameter type from u16 to u32 to rectify the function signature and related behaviour. Signed-off-by: Alan Douglas [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-cadence.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-cadence.h b/drivers/pci/controller/pcie-cadence.h index 4bb27333b05c..ed336cc7f4ba 100644 --- a/drivers/pci/controller/pcie-cadence.h +++ b/drivers/pci/controller/pcie-cadence.h @@ -279,7 +279,7 @@ static inline void cdns_pcie_ep_fn_writew(struct cdns_pcie *pcie, u8 fn, } static inline void cdns_pcie_ep_fn_writel(struct cdns_pcie *pcie, u8 fn, - u32 reg, u16 value) + u32 reg, u32 value) { writel(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); } -- cgit v1.2.3-70-g09d2 From dfb80534692ddc5b97e1da4384f13dc0287fccb2 Mon Sep 17 00:00:00 2001 From: Alan Douglas Date: Mon, 25 Jun 2018 09:30:50 +0100 Subject: PCI: cadence: Add generic PHY support to host and EP drivers If PHYs are present, initialize and enable them at driver probe. Signed-off-by: Alan Douglas [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-cadence-ep.c | 14 ++++- drivers/pci/controller/pcie-cadence-host.c | 15 +++++ drivers/pci/controller/pcie-cadence.c | 93 ++++++++++++++++++++++++++++++ drivers/pci/controller/pcie-cadence.h | 7 +++ 4 files changed, 128 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index e3fe4124e3af..c02f33d8e506 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -439,6 +439,7 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) struct pci_epc *epc; struct resource *res; int ret; + int phy_count; ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); if (!ep) @@ -473,6 +474,12 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) if (!ep->ob_addr) return -ENOMEM; + ret = cdns_pcie_init_phy(dev, pcie); + if (ret) { + dev_err(dev, "failed to init phy\n"); + return ret; + } + platform_set_drvdata(pdev, pcie); pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { @@ -521,6 +528,10 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) err_get_sync: pm_runtime_disable(dev); + cdns_pcie_disable_phy(pcie); + phy_count = pcie->phy_count; + while (phy_count--) + device_link_del(pcie->link[phy_count]); return ret; } @@ -528,6 +539,7 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) static void cdns_pcie_ep_shutdown(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct cdns_pcie *pcie = dev_get_drvdata(dev); int ret; ret = pm_runtime_put_sync(dev); @@ -536,7 +548,7 @@ static void cdns_pcie_ep_shutdown(struct platform_device *pdev) pm_runtime_disable(dev); - /* The PCIe controller can't be disabled. */ + cdns_pcie_disable_phy(pcie); } static struct platform_driver cdns_pcie_ep_driver = { diff --git a/drivers/pci/controller/pcie-cadence-host.c b/drivers/pci/controller/pcie-cadence-host.c index a4ebbd37b553..36f31092562f 100644 --- a/drivers/pci/controller/pcie-cadence-host.c +++ b/drivers/pci/controller/pcie-cadence-host.c @@ -58,6 +58,9 @@ static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, return pcie->reg_base + (where & 0xfff); } + /* Check that the link is up */ + if (!(cdns_pcie_readl(pcie, CDNS_PCIE_LM_BASE) & 0x1)) + return NULL; /* Update Output registers for AXI region 0. */ addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | @@ -239,6 +242,7 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) struct cdns_pcie *pcie; struct resource *res; int ret; + int phy_count; bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); if (!bridge) @@ -290,6 +294,13 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) } pcie->mem_res = res; + ret = cdns_pcie_init_phy(dev, pcie); + if (ret) { + dev_err(dev, "failed to init phy\n"); + return ret; + } + platform_set_drvdata(pdev, pcie); + pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { @@ -322,6 +333,10 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) err_get_sync: pm_runtime_disable(dev); + cdns_pcie_disable_phy(pcie); + phy_count = pcie->phy_count; + while (phy_count--) + device_link_del(pcie->link[phy_count]); return ret; } diff --git a/drivers/pci/controller/pcie-cadence.c b/drivers/pci/controller/pcie-cadence.c index 138d113eb45d..2edc12661e44 100644 --- a/drivers/pci/controller/pcie-cadence.c +++ b/drivers/pci/controller/pcie-cadence.c @@ -124,3 +124,96 @@ void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r) cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), 0); cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), 0); } + +void cdns_pcie_disable_phy(struct cdns_pcie *pcie) +{ + int i = pcie->phy_count; + + while (i--) { + phy_power_off(pcie->phy[i]); + phy_exit(pcie->phy[i]); + } +} + +int cdns_pcie_enable_phy(struct cdns_pcie *pcie) +{ + int ret; + int i; + + for (i = 0; i < pcie->phy_count; i++) { + ret = phy_init(pcie->phy[i]); + if (ret < 0) + goto err_phy; + + ret = phy_power_on(pcie->phy[i]); + if (ret < 0) { + phy_exit(pcie->phy[i]); + goto err_phy; + } + } + + return 0; + +err_phy: + while (--i >= 0) { + phy_power_off(pcie->phy[i]); + phy_exit(pcie->phy[i]); + } + + return ret; +} + +int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie) +{ + struct device_node *np = dev->of_node; + int phy_count; + struct phy **phy; + struct device_link **link; + int i; + int ret; + const char *name; + + phy_count = of_property_count_strings(np, "phy-names"); + if (phy_count < 1) { + dev_err(dev, "no phy-names. PHY will not be initialized\n"); + pcie->phy_count = 0; + return 0; + } + + phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL); + if (!phy) + return -ENOMEM; + + link = devm_kzalloc(dev, sizeof(*link) * phy_count, GFP_KERNEL); + if (!link) + return -ENOMEM; + + for (i = 0; i < phy_count; i++) { + of_property_read_string_index(np, "phy-names", i, &name); + phy[i] = devm_phy_optional_get(dev, name); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS); + if (!link[i]) { + ret = -EINVAL; + goto err_link; + } + } + + pcie->phy_count = phy_count; + pcie->phy = phy; + pcie->link = link; + + ret = cdns_pcie_enable_phy(pcie); + if (ret) + goto err_link; + + return 0; + +err_link: + while (--i >= 0) + device_link_del(link[i]); + + return ret; +} diff --git a/drivers/pci/controller/pcie-cadence.h b/drivers/pci/controller/pcie-cadence.h index ed336cc7f4ba..b342c808f7d6 100644 --- a/drivers/pci/controller/pcie-cadence.h +++ b/drivers/pci/controller/pcie-cadence.h @@ -8,6 +8,7 @@ #include #include +#include /* * Local Management Registers @@ -229,6 +230,9 @@ struct cdns_pcie { struct resource *mem_res; bool is_rc; u8 bus; + int phy_count; + struct phy **phy; + struct device_link **link; }; /* Register access */ @@ -307,5 +311,8 @@ void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, u32 r, u64 cpu_addr); void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); +void cdns_pcie_disable_phy(struct cdns_pcie *pcie); +int cdns_pcie_enable_phy(struct cdns_pcie *pcie); +int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie); #endif /* _PCIE_CADENCE_H */ -- cgit v1.2.3-70-g09d2 From 82dfbd27c837b5a7c2a7a13b54a4f0b16c51222f Mon Sep 17 00:00:00 2001 From: Alan Douglas Date: Mon, 25 Jun 2018 09:30:51 +0100 Subject: dt-bindings: PCI: cadence: Add DT bindings for optional PHYs Update DT documentation to include optional PHYs for cadence PCIe host and endpoint controllers. Signed-off-by: Alan Douglas Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/pci/cdns,cdns-pcie-ep.txt | 5 +++++ Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-ep.txt b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-ep.txt index 9a305237fa6e..4a0475e2ba7e 100644 --- a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-ep.txt +++ b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-ep.txt @@ -9,6 +9,9 @@ Required properties: Optional properties: - max-functions: Maximum number of functions that can be configured (default 1). +- phys: From PHY bindings: List of Generic PHY phandles. One per lane if more + than one in the list. If only one PHY listed it must manage all lanes. +- phy-names: List of names to identify the PHY. Example: @@ -19,4 +22,6 @@ pcie@fc000000 { reg-names = "reg", "mem"; cdns,max-outbound-regions = <16>; max-functions = /bits/ 8 <8>; + phys = <&ep_phy0 &ep_phy1>; + phy-names = "pcie-lane0","pcie-lane1"; }; diff --git a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt index 20a33f38f69d..91de69c713a9 100644 --- a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt +++ b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt @@ -24,6 +24,9 @@ Optional properties: translations (default 32) - vendor-id: The PCI vendor ID (16 bits, default is design dependent) - device-id: The PCI device ID (16 bits, default is design dependent) +- phys: From PHY bindings: List of Generic PHY phandles. One per lane if more + than one in the list. If only one PHY listed it must manage all lanes. +- phy-names: List of names to identify the PHY. Example: @@ -57,4 +60,7 @@ pcie@fb000000 { interrupt-map-mask = <0x0 0x0 0x0 0x7>; msi-parent = <&its_pci>; + + phys = <&pcie_phy0>; + phy-names = "pcie-phy"; }; -- cgit v1.2.3-70-g09d2 From ee12c9efe685428ebfae1bf5347b5375f54ce44e Mon Sep 17 00:00:00 2001 From: Alan Douglas Date: Mon, 25 Jun 2018 09:30:52 +0100 Subject: PCI: cadence: Add Power Management ops for host and EP These PM ops will enable/disable the optional PHYs if present. The AXI link-down register in the host driver is now cleared in cdns_pci_map_bus() since the link-down bit will be set if the PHY has been disabled. It is not cleared when enabling the PHY, since the link will not yet be up (e.g. when an EP controller is connected back-to-back to the host controller and its PHY is still disabled). Link: http://lkml.kernel.org/r/1529915453-4633-5-git-send-email-adouglas@cadence.com Signed-off-by: Alan Douglas [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-cadence-ep.c | 1 + drivers/pci/controller/pcie-cadence-host.c | 3 +++ drivers/pci/controller/pcie-cadence.c | 30 ++++++++++++++++++++++++++++++ drivers/pci/controller/pcie-cadence.h | 4 ++++ 4 files changed, 38 insertions(+) diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index c02f33d8e506..3eabd99a1233 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -555,6 +555,7 @@ static struct platform_driver cdns_pcie_ep_driver = { .driver = { .name = "cdns-pcie-ep", .of_match_table = cdns_pcie_ep_of_match, + .pm = &cdns_pcie_pm_ops, }, .probe = cdns_pcie_ep_probe, .shutdown = cdns_pcie_ep_shutdown, diff --git a/drivers/pci/controller/pcie-cadence-host.c b/drivers/pci/controller/pcie-cadence-host.c index 36f31092562f..e3e9b7de8592 100644 --- a/drivers/pci/controller/pcie-cadence-host.c +++ b/drivers/pci/controller/pcie-cadence-host.c @@ -61,6 +61,8 @@ static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, /* Check that the link is up */ if (!(cdns_pcie_readl(pcie, CDNS_PCIE_LM_BASE) & 0x1)) return NULL; + /* Clear AXI link-down status */ + cdns_pcie_writel(pcie, CDNS_PCIE_AT_LINKDOWN, 0x0); /* Update Output registers for AXI region 0. */ addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | @@ -345,6 +347,7 @@ static struct platform_driver cdns_pcie_host_driver = { .driver = { .name = "cdns-pcie-host", .of_match_table = cdns_pcie_host_of_match, + .pm = &cdns_pcie_pm_ops, }, .probe = cdns_pcie_host_probe, }; diff --git a/drivers/pci/controller/pcie-cadence.c b/drivers/pci/controller/pcie-cadence.c index 2edc12661e44..86f1b002c846 100644 --- a/drivers/pci/controller/pcie-cadence.c +++ b/drivers/pci/controller/pcie-cadence.c @@ -217,3 +217,33 @@ err_link: return ret; } + +#ifdef CONFIG_PM_SLEEP +static int cdns_pcie_suspend_noirq(struct device *dev) +{ + struct cdns_pcie *pcie = dev_get_drvdata(dev); + + cdns_pcie_disable_phy(pcie); + + return 0; +} + +static int cdns_pcie_resume_noirq(struct device *dev) +{ + struct cdns_pcie *pcie = dev_get_drvdata(dev); + int ret; + + ret = cdns_pcie_enable_phy(pcie); + if (ret) { + dev_err(dev, "failed to enable phy\n"); + return ret; + } + + return 0; +} +#endif + +const struct dev_pm_ops cdns_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cdns_pcie_suspend_noirq, + cdns_pcie_resume_noirq) +}; diff --git a/drivers/pci/controller/pcie-cadence.h b/drivers/pci/controller/pcie-cadence.h index b342c808f7d6..ae6bf2a2b3d3 100644 --- a/drivers/pci/controller/pcie-cadence.h +++ b/drivers/pci/controller/pcie-cadence.h @@ -166,6 +166,9 @@ #define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \ (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008) +/* AXI link down register */ +#define CDNS_PCIE_AT_LINKDOWN (CDNS_PCIE_AT_BASE + 0x0824) + enum cdns_pcie_rp_bar { RP_BAR0, RP_BAR1, @@ -314,5 +317,6 @@ void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); void cdns_pcie_disable_phy(struct cdns_pcie *pcie); int cdns_pcie_enable_phy(struct cdns_pcie *pcie); int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie); +extern const struct dev_pm_ops cdns_pcie_pm_ops; #endif /* _PCIE_CADENCE_H */ -- cgit v1.2.3-70-g09d2 From 0ae7383e83c6b1cea1e31173e362765dc24068c7 Mon Sep 17 00:00:00 2001 From: Alan Douglas Date: Mon, 25 Jun 2018 09:30:53 +0100 Subject: PCI: cadence: Add shutdown callback to host driver Add shutdown callback to host driver which will disable PHY and PM runtime. Signed-off-by: Alan Douglas Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-cadence-host.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/pci/controller/pcie-cadence-host.c b/drivers/pci/controller/pcie-cadence-host.c index e3e9b7de8592..ec394f6a19c8 100644 --- a/drivers/pci/controller/pcie-cadence-host.c +++ b/drivers/pci/controller/pcie-cadence-host.c @@ -343,6 +343,20 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) return ret; } +static void cdns_pcie_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cdns_pcie *pcie = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_put_sync(dev); + if (ret < 0) + dev_dbg(dev, "pm_runtime_put_sync failed\n"); + + pm_runtime_disable(dev); + cdns_pcie_disable_phy(pcie); +} + static struct platform_driver cdns_pcie_host_driver = { .driver = { .name = "cdns-pcie-host", @@ -350,5 +364,6 @@ static struct platform_driver cdns_pcie_host_driver = { .pm = &cdns_pcie_pm_ops, }, .probe = cdns_pcie_host_probe, + .shutdown = cdns_pcie_shutdown, }; builtin_platform_driver(cdns_pcie_host_driver); -- cgit v1.2.3-70-g09d2 From eb1e39f784e83321353314da1b2e8cbb2fceaba9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 11 Jul 2018 09:01:03 +0100 Subject: PCI: pcie-cadence-ep: Remove redundant variable mmc Variable mmc is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'mmc' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King [lorenzo.pieralisi@arm.com: reworked commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Alan Douglas --- drivers/pci/controller/pcie-cadence-ep.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index 3eabd99a1233..04fe76c3d59d 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -238,7 +238,7 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; - u16 flags, mmc, mme; + u16 flags, mme; /* Validate that the MSI feature is actually enabled. */ flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); @@ -249,7 +249,6 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) * Get the Multiple Message Enable bitfield from the Message Control * register. */ - mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1; mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; return mme; -- cgit v1.2.3-70-g09d2