Add driver to configure the STM32MP25 SoC PCIe Gen2 controller based on the
DesignWare PCIe core in endpoint mode.
Uses the common reference clock provided by the host.
Signed-off-by: Christian Bruel <christian.bruel@foss.st.com>
---
drivers/pci/controller/dwc/Kconfig | 12 +
drivers/pci/controller/dwc/Makefile | 1 +
drivers/pci/controller/dwc/pcie-stm32-ep.c | 445 +++++++++++++++++++++
drivers/pci/controller/dwc/pcie-stm32.h | 2 +
4 files changed, 460 insertions(+)
create mode 100644 drivers/pci/controller/dwc/pcie-stm32-ep.c
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 0c18879b604c..f5601c81b56c 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -401,6 +401,18 @@ config PCIE_STM32
This driver can also be built as a module. If so, the module
will be called pcie-stm32.
+config PCIE_STM32_EP
+ tristate "STMicroelectronics STM32MP25 PCIe Controller (endpoint mode)"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on PCI_ENDPOINT
+ select PCIE_DW_EP
+ help
+ Enables endpoint support for DesignWare core based PCIe controller in found
+ in STM32MP25 SoC.
+
+ This driver can also be built as a module. If so, the module
+ will be called pcie-stm32-ep.
+
config PCI_DRA7XX
tristate
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index 576d99cb3bc5..caebd98f6dd3 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o
obj-$(CONFIG_PCIE_STM32) += pcie-stm32.o
+obj-$(CONFIG_PCIE_STM32_EP) += pcie-stm32-ep.o
# The following drivers are for devices that use the generic ACPI
# pci_root.c driver but don't support standard ECAM config access.
diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
new file mode 100644
index 000000000000..27f5725a7e76
--- /dev/null
+++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * STMicroelectronics STM32MP25 PCIe endpoint driver.
+ *
+ * Copyright (C) 2024 STMicroelectronics
+ * Author: Christian Bruel <christian.bruel@foss.st.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include "pcie-designware.h"
+#include "pcie-stm32.h"
+
+#define STM32MP25_PCIECR_EP 0
+#define STM32MP25_PCIECR_REQ_RETRY_EN BIT(3)
+
+enum stm32_pcie_ep_link_status {
+ STM32_PCIE_EP_LINK_DISABLED,
+ STM32_PCIE_EP_LINK_ENABLED,
+};
+
+struct stm32_pcie {
+ struct dw_pcie *pci;
+ struct regmap *regmap;
+ struct reset_control *rst;
+ struct phy *phy;
+ struct clk *clk;
+ struct gpio_desc *perst_gpio;
+ enum stm32_pcie_ep_link_status link_status;
+ unsigned int perst_irq;
+};
+
+static void stm32_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
+ enum pci_barno bar;
+
+ for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
+ dw_pcie_ep_reset_bar(pci, bar);
+
+ /* Defer Completion Requests until link started */
+ regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
+ STM32MP25_PCIECR_REQ_RETRY_EN,
+ STM32MP25_PCIECR_REQ_RETRY_EN);
+}
+
+static int stm32_pcie_enable_link(struct dw_pcie *pci)
+{
+ struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
+ int ret;
+
+ regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
+ STM32MP25_PCIECR_LTSSM_EN,
+ STM32MP25_PCIECR_LTSSM_EN);
+
+ ret = dw_pcie_wait_for_link(pci);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
+ STM32MP25_PCIECR_REQ_RETRY_EN,
+ 0);
+
+ return 0;
+}
+
+static void stm32_pcie_disable_link(struct dw_pcie *pci)
+{
+ struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
+
+ regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
+ STM32MP25_PCIECR_REQ_RETRY_EN,
+ STM32MP25_PCIECR_REQ_RETRY_EN);
+
+ regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, STM32MP25_PCIECR_LTSSM_EN, 0);
+}
+
+static int stm32_pcie_start_link(struct dw_pcie *pci)
+{
+ struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
+ int ret;
+
+ if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
+ dev_dbg(pci->dev, "Link is already enabled\n");
+ return 0;
+ }
+
+ ret = stm32_pcie_enable_link(pci);
+ if (ret) {
+ dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
+ return ret;
+ }
+
+ stm32_pcie->link_status = STM32_PCIE_EP_LINK_ENABLED;
+
+ enable_irq(stm32_pcie->perst_irq);
+
+ return 0;
+}
+
+static void stm32_pcie_stop_link(struct dw_pcie *pci)
+{
+ struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
+
+ if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_DISABLED) {
+ dev_dbg(pci->dev, "Link is already disabled\n");
+ return;
+ }
+
+ disable_irq(stm32_pcie->perst_irq);
+
+ stm32_pcie_disable_link(pci);
+
+ stm32_pcie->link_status = STM32_PCIE_EP_LINK_DISABLED;
+}
+
+static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
+ unsigned int type, u16 interrupt_num)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ switch (type) {
+ case PCI_IRQ_INTX:
+ return dw_pcie_ep_raise_intx_irq(ep, func_no);
+ case PCI_IRQ_MSI:
+ return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
+ default:
+ dev_err(pci->dev, "UNKNOWN IRQ type\n");
+ return -EINVAL;
+ }
+}
+
+static const struct pci_epc_features stm32_pcie_epc_features = {
+ .msi_capable = true,
+ .align = 1 << 16,
+};
+
+static const struct pci_epc_features*
+stm32_pcie_get_features(struct dw_pcie_ep *ep)
+{
+ return &stm32_pcie_epc_features;
+}
+
+static const struct dw_pcie_ep_ops stm32_pcie_ep_ops = {
+ .init = stm32_pcie_ep_init,
+ .raise_irq = stm32_pcie_raise_irq,
+ .get_features = stm32_pcie_get_features,
+};
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .start_link = stm32_pcie_start_link,
+ .stop_link = stm32_pcie_stop_link,
+};
+
+static int stm32_pcie_enable_resources(struct stm32_pcie *stm32_pcie)
+{
+ int ret;
+
+ ret = phy_init(stm32_pcie->phy);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(stm32_pcie->clk);
+ if (ret)
+ phy_exit(stm32_pcie->phy);
+
+ return ret;
+}
+
+static void stm32_pcie_disable_resources(struct stm32_pcie *stm32_pcie)
+{
+ clk_disable_unprepare(stm32_pcie->clk);
+
+ phy_exit(stm32_pcie->phy);
+}
+
+static void stm32_pcie_perst_assert(struct dw_pcie *pci)
+{
+ struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
+ struct device *dev = pci->dev;
+
+ dev_dbg(dev, "PERST asserted by host. Shutting down the PCIe link\n");
+
+ /*
+ * Do not try to release resources if the PERST# is
+ * asserted before the link is started.
+ */
+ if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_DISABLED) {
+ dev_dbg(pci->dev, "Link is already disabled\n");
+ return;
+ }
+
+ stm32_pcie_disable_link(pci);
+
+ stm32_pcie_disable_resources(stm32_pcie);
+
+ pm_runtime_put_sync(dev);
+
+ stm32_pcie->link_status = STM32_PCIE_EP_LINK_DISABLED;
+}
+
+static void stm32_pcie_perst_deassert(struct dw_pcie *pci)
+{
+ struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
+ struct device *dev = pci->dev;
+ struct dw_pcie_ep *ep = &pci->ep;
+ int ret;
+
+ if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
+ dev_dbg(pci->dev, "Link is already enabled\n");
+ return;
+ }
+
+ dev_dbg(dev, "PERST de-asserted by host. Starting link training\n");
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "pm runtime resume failed: %d\n", ret);
+ return;
+ }
+
+ ret = stm32_pcie_enable_resources(stm32_pcie);
+ if (ret) {
+ dev_err(dev, "Failed to enable resources: %d\n", ret);
+ goto err_clk;
+ }
+
+ ret = dw_pcie_ep_init_registers(ep);
+ if (ret) {
+ dev_err(dev, "Failed to complete initialization: %d\n", ret);
+ goto err_init_regs;
+ }
+
+ pci_epc_init_notify(ep->epc);
+
+ ret = stm32_pcie_enable_link(pci);
+ if (ret) {
+ dev_err(dev, "PCIe Cannot establish link: %d\n", ret);
+ goto err_link;
+ }
+
+ stm32_pcie->link_status = STM32_PCIE_EP_LINK_ENABLED;
+
+ return;
+
+err_link:
+ pci_epc_deinit_notify(ep->epc);
+
+err_init_regs:
+ stm32_pcie_disable_resources(stm32_pcie);
+
+err_clk:
+ pm_runtime_put_sync(dev);
+}
+
+static irqreturn_t stm32_pcie_ep_perst_irq_thread(int irq, void *data)
+{
+ struct stm32_pcie *stm32_pcie = data;
+ struct dw_pcie *pci = stm32_pcie->pci;
+ u32 perst;
+
+ perst = gpiod_get_value(stm32_pcie->perst_gpio);
+ if (perst)
+ stm32_pcie_perst_assert(pci);
+ else
+ stm32_pcie_perst_deassert(pci);
+
+ return IRQ_HANDLED;
+}
+
+static int stm32_add_pcie_ep(struct stm32_pcie *stm32_pcie,
+ struct platform_device *pdev)
+{
+ struct dw_pcie *pci = stm32_pcie->pci;
+ struct dw_pcie_ep *ep = &pci->ep;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
+ STM32MP25_PCIECR_TYPE_MASK,
+ STM32MP25_PCIECR_EP);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "pm runtime resume failed: %d\n", ret);
+ return ret;
+ }
+
+ reset_control_assert(stm32_pcie->rst);
+ reset_control_deassert(stm32_pcie->rst);
+
+ ep->ops = &stm32_pcie_ep_ops;
+
+ ret = dw_pcie_ep_init(ep);
+ if (ret) {
+ dev_err(dev, "failed to initialize ep: %d\n", ret);
+ goto err_init;
+ }
+
+ ret = stm32_pcie_enable_resources(stm32_pcie);
+ if (ret) {
+ dev_err(dev, "failed to enable resources: %d\n", ret);
+ goto err_clk;
+ }
+
+ ret = dw_pcie_ep_init_registers(ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ goto err_init_regs;
+ }
+
+ pci_epc_init_notify(ep->epc);
+
+ return 0;
+
+err_init_regs:
+ stm32_pcie_disable_resources(stm32_pcie);
+
+err_clk:
+ dw_pcie_ep_deinit(ep);
+
+err_init:
+ pm_runtime_put_sync(dev);
+ return ret;
+}
+
+static int stm32_pcie_probe(struct platform_device *pdev)
+{
+ struct stm32_pcie *stm32_pcie;
+ struct dw_pcie *dw;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL);
+ if (!stm32_pcie)
+ return -ENOMEM;
+
+ dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL);
+ if (!dw)
+ return -ENOMEM;
+
+ stm32_pcie->pci = dw;
+
+ dw->dev = dev;
+ dw->ops = &dw_pcie_ops;
+
+ stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg");
+ if (IS_ERR(stm32_pcie->regmap))
+ return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap),
+ "No syscfg specified\n");
+
+ stm32_pcie->phy = devm_phy_get(dev, "pcie-phy");
+ if (IS_ERR(stm32_pcie->phy))
+ return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy),
+ "failed to get pcie-phy\n");
+
+ stm32_pcie->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(stm32_pcie->clk))
+ return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk),
+ "Failed to get PCIe clock source\n");
+
+ stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(stm32_pcie->rst))
+ return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst),
+ "Failed to get PCIe reset\n");
+
+ stm32_pcie->perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN);
+ if (IS_ERR(stm32_pcie->perst_gpio))
+ return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio),
+ "Failed to get reset GPIO\n");
+
+ ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, stm32_pcie);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable pm runtime %d\n", ret);
+ return ret;
+ }
+
+ stm32_pcie->perst_irq = gpiod_to_irq(stm32_pcie->perst_gpio);
+
+ /* Will be enabled in start_link when device is initialized. */
+ irq_set_status_flags(stm32_pcie->perst_irq, IRQ_NOAUTOEN);
+
+ ret = devm_request_threaded_irq(dev, stm32_pcie->perst_irq, NULL,
+ stm32_pcie_ep_perst_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "perst_irq", stm32_pcie);
+ if (ret) {
+ dev_err(dev, "Failed to request PERST IRQ: %d\n", ret);
+ return ret;
+ }
+
+ return stm32_add_pcie_ep(stm32_pcie, pdev);
+}
+
+static void stm32_pcie_remove(struct platform_device *pdev)
+{
+ struct stm32_pcie *stm32_pcie = platform_get_drvdata(pdev);
+ struct dw_pcie_ep *ep = &stm32_pcie->pci->ep;
+
+ disable_irq(stm32_pcie->perst_irq);
+
+ dw_pcie_ep_deinit(ep);
+
+ stm32_pcie_disable_resources(stm32_pcie);
+
+ pm_runtime_put_sync(&pdev->dev);
+}
+
+static const struct of_device_id stm32_pcie_ep_of_match[] = {
+ { .compatible = "st,stm32mp25-pcie-ep" },
+ {},
+};
+
+static struct platform_driver stm32_pcie_ep_driver = {
+ .probe = stm32_pcie_probe,
+ .remove = stm32_pcie_remove,
+ .driver = {
+ .name = "stm32-ep-pcie",
+ .of_match_table = stm32_pcie_ep_of_match,
+ },
+};
+
+module_platform_driver(stm32_pcie_ep_driver);
+
+MODULE_AUTHOR("Christian Bruel <christian.bruel@foss.st.com>");
+MODULE_DESCRIPTION("STM32MP25 PCIe Endpoint Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(of, stm32_pcie_ep_of_match);
diff --git a/drivers/pci/controller/dwc/pcie-stm32.h b/drivers/pci/controller/dwc/pcie-stm32.h
index 3efd00937d3d..ac0955adf150 100644
--- a/drivers/pci/controller/dwc/pcie-stm32.h
+++ b/drivers/pci/controller/dwc/pcie-stm32.h
@@ -9,7 +9,9 @@
#define to_stm32_pcie(x) dev_get_drvdata((x)->dev)
#define STM32MP25_PCIECR_TYPE_MASK GENMASK(11, 8)
+#define STM32MP25_PCIECR_EP 0
#define STM32MP25_PCIECR_LTSSM_EN BIT(2)
+#define STM32MP25_PCIECR_REQ_RETRY_EN BIT(3)
#define STM32MP25_PCIECR_RC BIT(10)
#define SYSCFG_PCIECR 0x6000
--
2.34.1
On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
> Add driver to configure the STM32MP25 SoC PCIe Gen2 controller based on the
> DesignWare PCIe core in endpoint mode.
> +config PCIE_STM32_EP
> + tristate "STMicroelectronics STM32MP25 PCIe Controller (endpoint mode)"
> + depends on ARCH_STM32 || COMPILE_TEST
> + depends on PCI_ENDPOINT
> + select PCIE_DW_EP
> + help
> + Enables endpoint support for DesignWare core based PCIe controller in found
> + in STM32MP25 SoC.
s/in found in/in/ (or "found in" if you prefer)
Wrap so help text fits in 80 columns when for "make menuconfig".
> +static void stm32_pcie_ep_init(struct dw_pcie_ep *ep)
> +{
> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
> + enum pci_barno bar;
> +
> + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
> + dw_pcie_ep_reset_bar(pci, bar);
> +
> + /* Defer Completion Requests until link started */
I asked about this before [1] but didn't finish the conversation. My
main point is that I think "Completion Request" is a misnomer.
There's a "Configuration Request" and a "Completion," but no such
thing as a "Completion Request."
Based on your previous response, I think this should say something
like "respond to config requests with Request Retry Status (RRS) until
we're prepared to handle them."
> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
> + STM32MP25_PCIECR_REQ_RETRY_EN,
> + STM32MP25_PCIECR_REQ_RETRY_EN);
> +}
> +
> +static int stm32_pcie_enable_link(struct dw_pcie *pci)
> +{
> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
> + int ret;
> +
> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
> + STM32MP25_PCIECR_LTSSM_EN,
> + STM32MP25_PCIECR_LTSSM_EN);
> +
> + ret = dw_pcie_wait_for_link(pci);
> + if (ret)
> + return ret;
> +
> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
> + STM32MP25_PCIECR_REQ_RETRY_EN,
> + 0);
And I assume this means the endpoint will accept config requests and
handle them normally instead of responding with RRS.
Strictly speaking this is a different condition than "the link is up"
because the link must be up in order to even receive a config request.
The purpose of RRS is for devices that need more initialization time
after the link is up before they can respond to config requests.
The fact that the hardware provides this bit makes me think the
designer anticipated that the link might come up before the rest of
the device is fully initialized.
Bjorn
[1] https://lore.kernel.org/r/20241112203846.GA1856246@bhelgaas
On 12/5/24 18:27, Bjorn Helgaas wrote:
> On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
>> +static void stm32_pcie_ep_init(struct dw_pcie_ep *ep)
>> +{
>> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>> + enum pci_barno bar;
>> +
>> + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
>> + dw_pcie_ep_reset_bar(pci, bar);
>> +
>> + /* Defer Completion Requests until link started */
>
> I asked about this before [1] but didn't finish the conversation. My
> main point is that I think "Completion Request" is a misnomer.
> There's a "Configuration Request" and a "Completion," but no such
> thing as a "Completion Request."
>
> Based on your previous response, I think this should say something
> like "respond to config requests with Request Retry Status (RRS) until
> we're prepared to handle them."
>
>> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
>> + STM32MP25_PCIECR_REQ_RETRY_EN,
>> + STM32MP25_PCIECR_REQ_RETRY_EN);
>> +}
>> +
>> +static int stm32_pcie_enable_link(struct dw_pcie *pci)
>> +{
>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>> + int ret;
>> +
>> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
>> + STM32MP25_PCIECR_LTSSM_EN,
>> + STM32MP25_PCIECR_LTSSM_EN);
>> +
>> + ret = dw_pcie_wait_for_link(pci);
>> + if (ret)
>> + return ret;
>> +
>> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
>> + STM32MP25_PCIECR_REQ_RETRY_EN,
>> + 0);
>
> And I assume this means the endpoint will accept config requests and
> handle them normally instead of responding with RRS.
>
> Strictly speaking this is a different condition than "the link is up"
> because the link must be up in order to even receive a config request.
> The purpose of RRS is for devices that need more initialization time
> after the link is up before they can respond to config requests.
>
> The fact that the hardware provides this bit makes me think the
> designer anticipated that the link might come up before the rest of
> the device is fully initialized.
You are correct and I am unable to reproduce a scenario where this is
still required.
Upon reviewing the history, I initially implemented this to address a
dependency issue in the original code, where enable_link was invoked
prematurely following the deassertion of PERST, as soon as the host was
ready but before the device configuration space was initialized, so host
enumeration was wrong.
Since then, the perst_irq is enabled by start_link. Consequently, the
initialization, enable_link, and enumeration sequence is now correct
This bit is useless and can be dropped
Thank you for identifying it.
Christian
>
> Bjorn
>
> [1] https://lore.kernel.org/r/20241112203846.GA1856246@bhelgaas
>
On 12/5/24 18:27, Bjorn Helgaas wrote:
> On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
>> Add driver to configure the STM32MP25 SoC PCIe Gen2 controller based on the
>> DesignWare PCIe core in endpoint mode.
>
>> +config PCIE_STM32_EP
>> + tristate "STMicroelectronics STM32MP25 PCIe Controller (endpoint mode)"
>> + depends on ARCH_STM32 || COMPILE_TEST
>> + depends on PCI_ENDPOINT
>> + select PCIE_DW_EP
>> + help
>> + Enables endpoint support for DesignWare core based PCIe controller in found
>> + in STM32MP25 SoC.
>
> s/in found in/in/ (or "found in" if you prefer)
>
> Wrap so help text fits in 80 columns when for "make menuconfig".
>
>> +static void stm32_pcie_ep_init(struct dw_pcie_ep *ep)
>> +{
>> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>> + enum pci_barno bar;
>> +
>> + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
>> + dw_pcie_ep_reset_bar(pci, bar);
>> +
>> + /* Defer Completion Requests until link started */
>
> I asked about this before [1] but didn't finish the conversation. My
> main point is that I think "Completion Request" is a misnomer.
> There's a "Configuration Request" and a "Completion," but no such
> thing as a "Completion Request."
>
> Based on your previous response, I think this should say something
> like "respond to config requests with Request Retry Status (RRS) until
> we're prepared to handle them."
OK thanks for the phrasing. This is inline with the DWC doc:
"... controller completes incoming configuration requests with a
configuration request retry status."
The only thing is that the PCIe specs talks about CRS, not RRS.
so slightly change to
"respond to config requests with Configuration Request Retry Status
(CRS) until we're prepared to handle them."
>
>> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
>> + STM32MP25_PCIECR_REQ_RETRY_EN,
>> + STM32MP25_PCIECR_REQ_RETRY_EN);
>> +}
>> +
>> +static int stm32_pcie_enable_link(struct dw_pcie *pci)
>> +{
>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>> + int ret;
>> +
>> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
>> + STM32MP25_PCIECR_LTSSM_EN,
>> + STM32MP25_PCIECR_LTSSM_EN);
>> +
>> + ret = dw_pcie_wait_for_link(pci);
>> + if (ret)
>> + return ret;
>> +
>> + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
>> + STM32MP25_PCIECR_REQ_RETRY_EN,
>> + 0);
>
> And I assume this means the endpoint will accept config requests and
> handle them normally instead of responding with RRS.
>
> Strictly speaking this is a different condition than "the link is up"
> because the link must be up in order to even receive a config request.
> The purpose of RRS is for devices that need more initialization time
> after the link is up before they can respond to config requests.
>
> The fact that the hardware provides this bit makes me think the
> designer anticipated that the link might come up before the rest of
> the device is fully initialized.
Agree, this there are 2 conditions in this function: link is up +
accepting config requests. I'll split or rename
Thanks
Christian
>
> Bjorn
>
> [1] https://lore.kernel.org/r/20241112203846.GA1856246@bhelgaas
>
On Mon, Dec 16, 2024 at 03:00:58PM +0100, Christian Bruel wrote:
> On 12/5/24 18:27, Bjorn Helgaas wrote:
> > On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
> > > Add driver to configure the STM32MP25 SoC PCIe Gen2 controller based on the
> > > DesignWare PCIe core in endpoint mode.
> > > +static void stm32_pcie_ep_init(struct dw_pcie_ep *ep)
> > > +{
> > > + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
> > > + enum pci_barno bar;
> > > +
> > > + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
> > > + dw_pcie_ep_reset_bar(pci, bar);
> > > +
> > > + /* Defer Completion Requests until link started */
> >
> > I asked about this before [1] but didn't finish the conversation. My
> > main point is that I think "Completion Request" is a misnomer.
> > There's a "Configuration Request" and a "Completion," but no such
> > thing as a "Completion Request."
> >
> > Based on your previous response, I think this should say something
> > like "respond to config requests with Request Retry Status (RRS) until
> > we're prepared to handle them."
>
> OK thanks for the phrasing. This is inline with the DWC doc:
> "... controller completes incoming configuration requests with a
> configuration request retry status."
> The only thing is that the PCIe specs talks about CRS, not RRS.
>
> so slightly change to
> "respond to config requests with Configuration Request Retry Status (CRS)
> until we're prepared to handle them."
This terminology between PCIe r5.0 and r6.0. In r5.0, sec 2.2.9
labels Completion Status value 010b as "Configuration Request Retry
Status (CRS)", but in r6.0, sec 2.2.9.1 labels that same value as
"Request Retry Status (RRS)".
We changed most usage inside drivers/pci/ to align with the r6.0 term
with https://git.kernel.org/linus/87f10faf166a ("PCI: Rename CRS
Completion Status to RRS").
But with respect to this patch, changing "Completion Request" to
"Configuration Request" is the main thing.
Bjorn
On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
[...]
> +static int stm32_pcie_start_link(struct dw_pcie *pci)
> +{
> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
> + int ret;
> +
> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
> + dev_dbg(pci->dev, "Link is already enabled\n");
> + return 0;
> + }
> +
> + ret = stm32_pcie_enable_link(pci);
> + if (ret) {
> + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
> + return ret;
> + }
How the REFCLK is supplied to the endpoint? From host or generated locally?
> +
> + stm32_pcie->link_status = STM32_PCIE_EP_LINK_ENABLED;
> +
> + enable_irq(stm32_pcie->perst_irq);
> +
> + return 0;
> +}
> +
> +static void stm32_pcie_stop_link(struct dw_pcie *pci)
> +{
> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
> +
> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_DISABLED) {
> + dev_dbg(pci->dev, "Link is already disabled\n");
> + return;
> + }
> +
> + disable_irq(stm32_pcie->perst_irq);
> +
> + stm32_pcie_disable_link(pci);
> +
> + stm32_pcie->link_status = STM32_PCIE_EP_LINK_DISABLED;
> +}
> +
> +static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> + unsigned int type, u16 interrupt_num)
> +{
> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> +
> + switch (type) {
> + case PCI_IRQ_INTX:
> + return dw_pcie_ep_raise_intx_irq(ep, func_no);
> + case PCI_IRQ_MSI:
> + return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
> + default:
> + dev_err(pci->dev, "UNKNOWN IRQ type\n");
> + return -EINVAL;
> + }
> +}
> +
> +static const struct pci_epc_features stm32_pcie_epc_features = {
> + .msi_capable = true,
> + .align = 1 << 16,
Use SZ_64K
> +};
> +
[...]
> +static int stm32_add_pcie_ep(struct stm32_pcie *stm32_pcie,
> + struct platform_device *pdev)
> +{
> + struct dw_pcie *pci = stm32_pcie->pci;
> + struct dw_pcie_ep *ep = &pci->ep;
> + struct device *dev = &pdev->dev;
> + int ret;
> +
> + ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
> + STM32MP25_PCIECR_TYPE_MASK,
> + STM32MP25_PCIECR_EP);
> + if (ret)
> + return ret;
> +
> + ret = pm_runtime_resume_and_get(dev);
> + if (ret < 0) {
> + dev_err(dev, "pm runtime resume failed: %d\n", ret);
> + return ret;
> + }
You might want to do runtime resume before accessing regmap.
> +
> + reset_control_assert(stm32_pcie->rst);
> + reset_control_deassert(stm32_pcie->rst);
> +
> + ep->ops = &stm32_pcie_ep_ops;
> +
> + ret = dw_pcie_ep_init(ep);
> + if (ret) {
> + dev_err(dev, "failed to initialize ep: %d\n", ret);
> + goto err_init;
> + }
> +
> + ret = stm32_pcie_enable_resources(stm32_pcie);
> + if (ret) {
> + dev_err(dev, "failed to enable resources: %d\n", ret);
> + goto err_clk;
> + }
> +
> + ret = dw_pcie_ep_init_registers(ep);
> + if (ret) {
> + dev_err(dev, "Failed to initialize DWC endpoint registers\n");
> + goto err_init_regs;
> + }
> +
> + pci_epc_init_notify(ep->epc);
> +
> + return 0;
> +
> +err_init_regs:
> + stm32_pcie_disable_resources(stm32_pcie);
> +
> +err_clk:
> + dw_pcie_ep_deinit(ep);
> +
> +err_init:
> + pm_runtime_put_sync(dev);
> + return ret;
> +}
> +
> +static int stm32_pcie_probe(struct platform_device *pdev)
> +{
> + struct stm32_pcie *stm32_pcie;
> + struct dw_pcie *dw;
> + struct device *dev = &pdev->dev;
> + int ret;
> +
> + stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL);
> + if (!stm32_pcie)
> + return -ENOMEM;
> +
> + dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL);
> + if (!dw)
> + return -ENOMEM;
Why can't you allocate it statically inside 'struct stm32_pcie'?
> +
> + stm32_pcie->pci = dw;
> +
> + dw->dev = dev;
> + dw->ops = &dw_pcie_ops;
> +
> + stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg");
> + if (IS_ERR(stm32_pcie->regmap))
> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap),
> + "No syscfg specified\n");
> +
> + stm32_pcie->phy = devm_phy_get(dev, "pcie-phy");
> + if (IS_ERR(stm32_pcie->phy))
> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy),
> + "failed to get pcie-phy\n");
> +
> + stm32_pcie->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(stm32_pcie->clk))
> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk),
> + "Failed to get PCIe clock source\n");
> +
> + stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL);
> + if (IS_ERR(stm32_pcie->rst))
> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst),
> + "Failed to get PCIe reset\n");
> +
> + stm32_pcie->perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN);
> + if (IS_ERR(stm32_pcie->perst_gpio))
> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio),
> + "Failed to get reset GPIO\n");
> +
> + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
Hmm, so PHY mode is common for both endpoint and host?
- Mani
--
மணிவண்ணன் சதாசிவம்
Hi Mani,
On 12/3/24 16:22, Manivannan Sadhasivam wrote:
> On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
>
> [...]
>
>> +static int stm32_pcie_start_link(struct dw_pcie *pci)
>> +{
>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>> + int ret;
>> +
>> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
>> + dev_dbg(pci->dev, "Link is already enabled\n");
>> + return 0;
>> + }
>> +
>> + ret = stm32_pcie_enable_link(pci);
>> + if (ret) {
>> + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
>> + return ret;
>> + }
>
> How the REFCLK is supplied to the endpoint? From host or generated locally?
The REFCLK is supplied from the host, it does not support separated clocks
>
>> +
>> + stm32_pcie->link_status = STM32_PCIE_EP_LINK_ENABLED;
>> +
>> + enable_irq(stm32_pcie->perst_irq);
>> +
>> + return 0;
>> +}
>> +
>> +static void stm32_pcie_stop_link(struct dw_pcie *pci)
>> +{
>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>> +
>> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_DISABLED) {
>> + dev_dbg(pci->dev, "Link is already disabled\n");
>> + return;
>> + }
>> +
>> + disable_irq(stm32_pcie->perst_irq);
>> +
>> + stm32_pcie_disable_link(pci);
>> +
>> + stm32_pcie->link_status = STM32_PCIE_EP_LINK_DISABLED;
>> +}
>> +
>> +static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
>> + unsigned int type, u16 interrupt_num)
>> +{
>> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
>> +
>> + switch (type) {
>> + case PCI_IRQ_INTX:
>> + return dw_pcie_ep_raise_intx_irq(ep, func_no);
>> + case PCI_IRQ_MSI:
>> + return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
>> + default:
>> + dev_err(pci->dev, "UNKNOWN IRQ type\n");
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static const struct pci_epc_features stm32_pcie_epc_features = {
>> + .msi_capable = true,
>> + .align = 1 << 16,
>
> Use SZ_64K
>
>> +};
>> +
>
> [...]
>
>> +static int stm32_add_pcie_ep(struct stm32_pcie *stm32_pcie,
>> + struct platform_device *pdev)
>> +{
>> + struct dw_pcie *pci = stm32_pcie->pci;
>> + struct dw_pcie_ep *ep = &pci->ep;
>> + struct device *dev = &pdev->dev;
>> + int ret;
>> +
>> + ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
>> + STM32MP25_PCIECR_TYPE_MASK,
>> + STM32MP25_PCIECR_EP);
>> + if (ret)
>> + return ret;
>> +
>> + ret = pm_runtime_resume_and_get(dev);
>> + if (ret < 0) {
>> + dev_err(dev, "pm runtime resume failed: %d\n", ret);
>> + return ret;
>> + }
>
> You might want to do runtime resume before accessing regmap.
ok
>
>> +
>> + reset_control_assert(stm32_pcie->rst);
>> + reset_control_deassert(stm32_pcie->rst);
>> +
>> + ep->ops = &stm32_pcie_ep_ops;
>> +
>> + ret = dw_pcie_ep_init(ep);
>> + if (ret) {
>> + dev_err(dev, "failed to initialize ep: %d\n", ret);
>> + goto err_init;
>> + }
>> +
>> + ret = stm32_pcie_enable_resources(stm32_pcie);
>> + if (ret) {
>> + dev_err(dev, "failed to enable resources: %d\n", ret);
>> + goto err_clk;
>> + }
>> +
>> + ret = dw_pcie_ep_init_registers(ep);
>> + if (ret) {
>> + dev_err(dev, "Failed to initialize DWC endpoint registers\n");
>> + goto err_init_regs;
>> + }
>> +
>> + pci_epc_init_notify(ep->epc);
>> +
>> + return 0;
>> +
>> +err_init_regs:
>> + stm32_pcie_disable_resources(stm32_pcie);
>> +
>> +err_clk:
>> + dw_pcie_ep_deinit(ep);
>> +
>> +err_init:
>> + pm_runtime_put_sync(dev);
>> + return ret;
>> +}
>> +
>> +static int stm32_pcie_probe(struct platform_device *pdev)
>> +{
>> + struct stm32_pcie *stm32_pcie;
>> + struct dw_pcie *dw;
>> + struct device *dev = &pdev->dev;
>> + int ret;
>> +
>> + stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL);
>> + if (!stm32_pcie)
>> + return -ENOMEM;
>> +
>> + dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL);
>> + if (!dw)
>> + return -ENOMEM;
>
> Why can't you allocate it statically inside 'struct stm32_pcie'?
>
will do, as for the rc
>> +
>> + stm32_pcie->pci = dw;
>> +
>> + dw->dev = dev;
>> + dw->ops = &dw_pcie_ops;
>> +
>> + stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg");
>> + if (IS_ERR(stm32_pcie->regmap))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap),
>> + "No syscfg specified\n");
>> +
>> + stm32_pcie->phy = devm_phy_get(dev, "pcie-phy");
>> + if (IS_ERR(stm32_pcie->phy))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy),
>> + "failed to get pcie-phy\n");
>> +
>> + stm32_pcie->clk = devm_clk_get(dev, NULL);
>> + if (IS_ERR(stm32_pcie->clk))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk),
>> + "Failed to get PCIe clock source\n");
>> +
>> + stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL);
>> + if (IS_ERR(stm32_pcie->rst))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst),
>> + "Failed to get PCIe reset\n");
>> +
>> + stm32_pcie->perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN);
>> between PCIE and USB3+ if (IS_ERR(stm32_pcie->perst_gpio))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio),
>> + "Failed to get reset GPIO\n");
>> +
>> + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
>
> Hmm, so PHY mode is common for both endpoint and host?
the COMBOPHY MODESEL sysconf takes PCIE or USB3 as value
>
> - Mani
>
Hi Manivanna,
On 12/3/24 16:22, Manivannan Sadhasivam wrote:
> On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
>
> [...]
>
>> +static int stm32_pcie_start_link(struct dw_pcie *pci)
>> +{
>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>> + int ret;
>> +
>> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
>> + dev_dbg(pci->dev, "Link is already enabled\n");
>> + return 0;
>> + }
>> +
>> + ret = stm32_pcie_enable_link(pci);
>> + if (ret) {
>> + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
>> + return ret;
>> + }
>
> How the REFCLK is supplied to the endpoint? From host or generated locally?
From Host only, we don't support the separated clock model.
>
>> +
>> + stm32_pcie->link_status = STM32_PCIE_EP_LINK_ENABLED;
>> +
>> + enable_irq(stm32_pcie->perst_irq);
>> +
>> + return 0;
>> +}
>> +
>> +static void stm32_pcie_stop_link(struct dw_pcie *pci)
>> +{
>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>> +
>> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_DISABLED) {
>> + dev_dbg(pci->dev, "Link is already disabled\n");
>> + return;
>> + }
>> +
>> + disable_irq(stm32_pcie->perst_irq);
>> +
>> + stm32_pcie_disable_link(pci);
>> +
>> + stm32_pcie->link_status = STM32_PCIE_EP_LINK_DISABLED;
>> +}
>> +
>> +static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
>> + unsigned int type, u16 interrupt_num)
>> +{
>> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
>> +
>> + switch (type) {
>> + case PCI_IRQ_INTX:
>> + return dw_pcie_ep_raise_intx_irq(ep, func_no);
>> + case PCI_IRQ_MSI:
>> + return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
>> + default:
>> + dev_err(pci->dev, "UNKNOWN IRQ type\n");
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static const struct pci_epc_features stm32_pcie_epc_features = {
>> + .msi_capable = true,
>> + .align = 1 << 16,
>
> Use SZ_64K
>
OK
>> +};
>> +
>
> [...]
>
>> +static int stm32_add_pcie_ep(struct stm32_pcie *stm32_pcie,
>> + struct platform_device *pdev)
>> +{
>> + struct dw_pcie *pci = stm32_pcie->pci;
>> + struct dw_pcie_ep *ep = &pci->ep;
>> + struct device *dev = &pdev->dev;
>> + int ret;
>> +
>> + ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
>> + STM32MP25_PCIECR_TYPE_MASK,
>> + STM32MP25_PCIECR_EP);
>> + if (ret)
>> + return ret;
>> +
>> + ret = pm_runtime_resume_and_get(dev);
>> + if (ret < 0) {
>> + dev_err(dev, "pm runtime resume failed: %d\n", ret);
>> + return ret;
>> + }
>
> You might want to do runtime resume before accessing regmap.
OK
>> +
>> + reset_control_assert(stm32_pcie->rst);
>> + reset_control_deassert(stm32_pcie->rst);
>> +
>> + ep->ops = &stm32_pcie_ep_ops;
>> +
>> + ret = dw_pcie_ep_init(ep);
>> + if (ret) {
>> + dev_err(dev, "failed to initialize ep: %d\n", ret);
>> + goto err_init;
>> + }
>> +
>> + ret = stm32_pcie_enable_resources(stm32_pcie);
>> + if (ret) {
>> + dev_err(dev, "failed to enable resources: %d\n", ret);
>> + goto err_clk;
>> + }
>> +
>> + ret = dw_pcie_ep_init_registers(ep);
>> + if (ret) {
>> + dev_err(dev, "Failed to initialize DWC endpoint registers\n");
>> + goto err_init_regs;
>> + }
>> +
>> + pci_epc_init_notify(ep->epc);
>> +
>> + return 0;
>> +
>> +err_init_regs:
>> + stm32_pcie_disable_resources(stm32_pcie);
>> +
>> +err_clk:
>> + dw_pcie_ep_deinit(ep);
>> +
>> +err_init:
>> + pm_runtime_put_sync(dev);
>> + return ret;
>> +}
>> +
>> +static int stm32_pcie_probe(struct platform_device *pdev)
>> +{
>> + struct stm32_pcie *stm32_pcie;
>> + struct dw_pcie *dw;
>> + struct device *dev = &pdev->dev;
>> + int ret;
>> +
>> + stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL);
>> + if (!stm32_pcie)
>> + return -ENOMEM;
>> +
>> + dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL);
>> + if (!dw)
>> + return -ENOMEM;
>
> Why can't you allocate it statically inside 'struct stm32_pcie'?
Will do
>
>> +
>> + stm32_pcie->pci = dw;
>> +
>> + dw->dev = dev;
>> + dw->ops = &dw_pcie_ops;
>> +
>> + stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg");
>> + if (IS_ERR(stm32_pcie->regmap))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap),
>> + "No syscfg specified\n");
>> +
>> + stm32_pcie->phy = devm_phy_get(dev, "pcie-phy");
>> + if (IS_ERR(stm32_pcie->phy))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy),
>> + "failed to get pcie-phy\n");
>> +
>> + stm32_pcie->clk = devm_clk_get(dev, NULL);
>> + if (IS_ERR(stm32_pcie->clk))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk),
>> + "Failed to get PCIe clock source\n");
>> +
>> + stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL);
>> + if (IS_ERR(stm32_pcie->rst))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst),
>> + "Failed to get PCIe reset\n");
>> +
>> + stm32_pcie->perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN);
>> + if (IS_ERR(stm32_pcie->perst_gpio))
>> + return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio),
>> + "Failed to get reset GPIO\n");
>> +
>> + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
>
> Hmm, so PHY mode is common for both endpoint and host?
Yes it is. We need to init the phy here because it is a clock source for
the PCIe core clk
Thanks
Christian
>
> - Mani
>
On Mon, Dec 16, 2024 at 11:02:07AM +0100, Christian Bruel wrote:
> Hi Manivanna,
>
> On 12/3/24 16:22, Manivannan Sadhasivam wrote:
> > On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
> >
> > [...]
> >
> > > +static int stm32_pcie_start_link(struct dw_pcie *pci)
> > > +{
> > > + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
> > > + int ret;
> > > +
> > > + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
> > > + dev_dbg(pci->dev, "Link is already enabled\n");
> > > + return 0;
> > > + }
> > > +
> > > + ret = stm32_pcie_enable_link(pci);
> > > + if (ret) {
> > > + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
> > > + return ret;
> > > + }
> >
> > How the REFCLK is supplied to the endpoint? From host or generated locally?
>
> From Host only, we don't support the separated clock model.
>
OK. So even without refclk you are still able to access the controller
registers? So the controller CSRs should be accessible by separate local clock I
believe.
Anyhow, please add this limitation (refclk dependency from host) in commit
message.
[...]
> > > + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
> >
> > Hmm, so PHY mode is common for both endpoint and host?
>
> Yes it is. We need to init the phy here because it is a clock source for the
> PCIe core clk
>
Clock source? Is it coming directly to PCIe or through RCC? There is no direct
clock representation from PHY to PCIe in DT binding.
- Mani
--
மணிவண்ணன் சதாசிவம்
On 12/16/24 17:17, Manivannan Sadhasivam wrote:
> On Mon, Dec 16, 2024 at 11:02:07AM +0100, Christian Bruel wrote:
>> Hi Manivanna,
>>
>> On 12/3/24 16:22, Manivannan Sadhasivam wrote:
>>> On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
>>>
>>> [...]
>>>
>>>> +static int stm32_pcie_start_link(struct dw_pcie *pci)
>>>> +{
>>>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>>>> + int ret;
>>>> +
>>>> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
>>>> + dev_dbg(pci->dev, "Link is already enabled\n");
>>>> + return 0;
>>>> + }
>>>> +
>>>> + ret = stm32_pcie_enable_link(pci);
>>>> + if (ret) {
>>>> + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>
>>> How the REFCLK is supplied to the endpoint? From host or generated locally?
>>
>> From Host only, we don't support the separated clock model.
>>
>
> OK. So even without refclk you are still able to access the controller
> registers? So the controller CSRs should be accessible by separate local clock I
> believe.
>
> Anyhow, please add this limitation (refclk dependency from host) in commit
> message.
>
> [...]
>
>>>> + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
>>>
>>> Hmm, so PHY mode is common for both endpoint and host?
>>
>> Yes it is. We need to init the phy here because it is a clock source for the
>> PCIe core clk
>>
>
> Clock source? Is it coming directly to PCIe or through RCC? There is no direct
> clock representation from PHY to PCIe in DT binding.
We have the following simplified clock dependencies (details in the RM)
_____________
RCC ck_icn_pcie--- ------------|-> dbi_clk |
_________ | |
ck_icn_phy ---|-> | | |
| pipe0--|--|->core_clk |
ck_ker ----- -|-> | | |
| | | |
100mhz pad ---|-> pll | | |
|_________| |____________|
COMBOPHY PCIE
I considered adding the COMBOPHY pipe0 as the clock provider for the
PCIe core_clk, but this did not provide any advantage since the PLL
needs to be locked first and all settings need to be completed.
Therefore, using clock_prepare_enable(pipe0) would be redundant with
what phy_init already accomplishes. The phy_init function is necessary
because it is used by the USB3 driver.
Since the core_clk is operational when all three other clocks are
enabled and the PLL is locked, modeling pipe0 had minimal value,
especially considering the dependencies of the USB3 driver.
I will add a comment in the code to explain this.
>
> - Mani
>
On 12/16/24 17:17, Manivannan Sadhasivam wrote:
> On Mon, Dec 16, 2024 at 11:02:07AM +0100, Christian Bruel wrote:
>> Hi Manivanna,
>>
>> On 12/3/24 16:22, Manivannan Sadhasivam wrote:
>>> On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
>>>
>>> [...]
>>>
>>>> +static int stm32_pcie_start_link(struct dw_pcie *pci)
>>>> +{
>>>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>>>> + int ret;
>>>> +
>>>> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
>>>> + dev_dbg(pci->dev, "Link is already enabled\n");
>>>> + return 0;
>>>> + }
>>>> +
>>>> + ret = stm32_pcie_enable_link(pci);
>>>> + if (ret) {
>>>> + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>
>>> How the REFCLK is supplied to the endpoint? From host or generated locally?
>>
>> From Host only, we don't support the separated clock model.
>>
>
> OK. So even without refclk you are still able to access the controller
> registers? So the controller CSRs should be accessible by separate local clock I
> believe.
>
> Anyhow, please add this limitation (refclk dependency from host) in commit
> message.
>
> [...]
>
>>>> + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
>>>
>>> Hmm, so PHY mode is common for both endpoint and host?
>>
>> Yes it is. We need to init the phy here because it is a clock source for the
>> PCIe core clk
>>
>
> Clock source? Is it coming directly to PCIe or through RCC? There is no direct
> clock representation from PHY to PCIe in DT binding.
The core_clk is generated directly by the PLL in the COMBOPHY, gated by
the RCC
>
> - Mani
>
On Tue, Dec 17, 2024 at 10:48:43AM +0100, Christian Bruel wrote:
>
>
> On 12/16/24 17:17, Manivannan Sadhasivam wrote:
> > On Mon, Dec 16, 2024 at 11:02:07AM +0100, Christian Bruel wrote:
> > > Hi Manivanna,
> > >
> > > On 12/3/24 16:22, Manivannan Sadhasivam wrote:
> > > > On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
> > > >
> > > > [...]
> > > >
> > > > > +static int stm32_pcie_start_link(struct dw_pcie *pci)
> > > > > +{
> > > > > + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
> > > > > + int ret;
> > > > > +
> > > > > + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
> > > > > + dev_dbg(pci->dev, "Link is already enabled\n");
> > > > > + return 0;
> > > > > + }
> > > > > +
> > > > > + ret = stm32_pcie_enable_link(pci);
> > > > > + if (ret) {
> > > > > + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
> > > > > + return ret;
> > > > > + }
> > > >
> > > > How the REFCLK is supplied to the endpoint? From host or generated locally?
> > >
> > > From Host only, we don't support the separated clock model.
> > >
> >
> > OK. So even without refclk you are still able to access the controller
> > registers? So the controller CSRs should be accessible by separate local clock I
> > believe.
> >
> > Anyhow, please add this limitation (refclk dependency from host) in commit
> > message.
> >
> > [...]
> >
> > > > > + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
> > > >
> > > > Hmm, so PHY mode is common for both endpoint and host?
> > >
> > > Yes it is. We need to init the phy here because it is a clock source for the
> > > PCIe core clk
> > >
> >
> > Clock source? Is it coming directly to PCIe or through RCC? There is no direct
> > clock representation from PHY to PCIe in DT binding.
>
> The core_clk is generated directly by the PLL in the COMBOPHY, gated by the
> RCC
>
In that case, phy should be the clock provider to RCC and PCIe should get the
gated clock it.
- Mani
--
மணிவண்ணன் சதாசிவம்
On 12/18/24 10:08, Manivannan Sadhasivam wrote:
> On Tue, Dec 17, 2024 at 10:48:43AM +0100, Christian Bruel wrote:
>>
>>
>> On 12/16/24 17:17, Manivannan Sadhasivam wrote:
>>> On Mon, Dec 16, 2024 at 11:02:07AM +0100, Christian Bruel wrote:
>>>> Hi Manivanna,
>>>>
>>>> On 12/3/24 16:22, Manivannan Sadhasivam wrote:
>>>>> On Tue, Nov 26, 2024 at 04:51:18PM +0100, Christian Bruel wrote:
>>>>>
>>>>> [...]
>>>>>
>>>>>> +static int stm32_pcie_start_link(struct dw_pcie *pci)
>>>>>> +{
>>>>>> + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
>>>>>> + int ret;
>>>>>> +
>>>>>> + if (stm32_pcie->link_status == STM32_PCIE_EP_LINK_ENABLED) {
>>>>>> + dev_dbg(pci->dev, "Link is already enabled\n");
>>>>>> + return 0;
>>>>>> + }
>>>>>> +
>>>>>> + ret = stm32_pcie_enable_link(pci);
>>>>>> + if (ret) {
>>>>>> + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>
>>>>> How the REFCLK is supplied to the endpoint? From host or generated locally?
>>>>
>>>> From Host only, we don't support the separated clock model.
>>>>
>>>
>>> OK. So even without refclk you are still able to access the controller
>>> registers? So the controller CSRs should be accessible by separate local clock I
>>> believe.
>>>
>>> Anyhow, please add this limitation (refclk dependency from host) in commit
>>> message.
>>>
>>> [...]
>>>
>>>>>> + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
>>>>>
>>>>> Hmm, so PHY mode is common for both endpoint and host?
>>>>
>>>> Yes it is. We need to init the phy here because it is a clock source for the
>>>> PCIe core clk
>>>>
>>>
>>> Clock source? Is it coming directly to PCIe or through RCC? There is no direct
>>> clock representation from PHY to PCIe in DT binding.
>>
>> The core_clk is generated directly by the PLL in the COMBOPHY, gated by the
>> RCC
>>
>
> In that case, phy should be the clock provider to RCC and PCIe should get the
> gated clock it.
ok, seems sensible.
>
> - Mani
>
© 2016 - 2026 Red Hat, Inc.