Skip to content

Commit 151f3d2

Browse files
Christian Bruelbjorn-helgaas
authored andcommitted
PCI: stm32-ep: Add PCIe Endpoint support for STM32MP25
Add driver to configure the STM32MP25 SoC PCIe controller based on the DesignWare PCIe core in endpoint mode. Controller support 2.5 and 5 GT/s data rates and uses the common reference clock provided by the host. The PCIe core_clk receives the pipe0_clk from the ComboPHY as input, and the ComboPHY PLL must be locked for pipe0_clk to be ready. Consequently, PCIe core registers cannot be accessed until the ComboPHY is fully initialised and REFCLK is enabled and ready. Signed-off-by: Christian Bruel <christian.bruel@foss.st.com> [mani: reworded description] Signed-off-by: Manivannan Sadhasivam <mani@kernel.org> [bhelgaas: squash in https://patch.msgid.link/20250902122641.269725-1-christian.bruel@foss.st.com to remove redundant link_status checks] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Link: https://patch.msgid.link/20250820075411.1178729-7-christian.bruel@foss.st.com
1 parent b8ef623 commit 151f3d2

File tree

4 files changed

+378
-0
lines changed

4 files changed

+378
-0
lines changed

drivers/pci/controller/dwc/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,18 @@ config PCIE_STM32_HOST
434434
This driver can also be built as a module. If so, the module
435435
will be called pcie-stm32.
436436

437+
config PCIE_STM32_EP
438+
tristate "STMicroelectronics STM32MP25 PCIe Controller (endpoint mode)"
439+
depends on ARCH_STM32 || COMPILE_TEST
440+
depends on PCI_ENDPOINT
441+
select PCIE_DW_EP
442+
help
443+
Enables Endpoint (EP) support for the DesignWare core based PCIe
444+
controller found in STM32MP25 SoC.
445+
446+
This driver can also be built as a module. If so, the module
447+
will be called pcie-stm32-ep.
448+
437449
config PCI_DRA7XX
438450
tristate
439451

drivers/pci/controller/dwc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
3232
obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
3333
obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o
3434
obj-$(CONFIG_PCIE_STM32_HOST) += pcie-stm32.o
35+
obj-$(CONFIG_PCIE_STM32_EP) += pcie-stm32-ep.o
3536

3637
# The following drivers are for devices that use the generic ACPI
3738
# pci_root.c driver but don't support standard ECAM config access.
Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* STMicroelectronics STM32MP25 PCIe endpoint driver.
4+
*
5+
* Copyright (C) 2025 STMicroelectronics
6+
* Author: Christian Bruel <christian.bruel@foss.st.com>
7+
*/
8+
9+
#include <linux/clk.h>
10+
#include <linux/mfd/syscon.h>
11+
#include <linux/of_platform.h>
12+
#include <linux/of_gpio.h>
13+
#include <linux/phy/phy.h>
14+
#include <linux/platform_device.h>
15+
#include <linux/pm_runtime.h>
16+
#include <linux/regmap.h>
17+
#include <linux/reset.h>
18+
#include "pcie-designware.h"
19+
#include "pcie-stm32.h"
20+
21+
struct stm32_pcie {
22+
struct dw_pcie pci;
23+
struct regmap *regmap;
24+
struct reset_control *rst;
25+
struct phy *phy;
26+
struct clk *clk;
27+
struct gpio_desc *perst_gpio;
28+
unsigned int perst_irq;
29+
};
30+
31+
static void stm32_pcie_ep_init(struct dw_pcie_ep *ep)
32+
{
33+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
34+
enum pci_barno bar;
35+
36+
for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
37+
dw_pcie_ep_reset_bar(pci, bar);
38+
}
39+
40+
static int stm32_pcie_enable_link(struct dw_pcie *pci)
41+
{
42+
struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
43+
44+
regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
45+
STM32MP25_PCIECR_LTSSM_EN,
46+
STM32MP25_PCIECR_LTSSM_EN);
47+
48+
return dw_pcie_wait_for_link(pci);
49+
}
50+
51+
static void stm32_pcie_disable_link(struct dw_pcie *pci)
52+
{
53+
struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
54+
55+
regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, STM32MP25_PCIECR_LTSSM_EN, 0);
56+
}
57+
58+
static int stm32_pcie_start_link(struct dw_pcie *pci)
59+
{
60+
struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
61+
int ret;
62+
63+
dev_dbg(pci->dev, "Enable link\n");
64+
65+
ret = stm32_pcie_enable_link(pci);
66+
if (ret) {
67+
dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
68+
return ret;
69+
}
70+
71+
enable_irq(stm32_pcie->perst_irq);
72+
73+
return 0;
74+
}
75+
76+
static void stm32_pcie_stop_link(struct dw_pcie *pci)
77+
{
78+
struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
79+
80+
dev_dbg(pci->dev, "Disable link\n");
81+
82+
disable_irq(stm32_pcie->perst_irq);
83+
84+
stm32_pcie_disable_link(pci);
85+
}
86+
87+
static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
88+
unsigned int type, u16 interrupt_num)
89+
{
90+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
91+
92+
switch (type) {
93+
case PCI_IRQ_INTX:
94+
return dw_pcie_ep_raise_intx_irq(ep, func_no);
95+
case PCI_IRQ_MSI:
96+
return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
97+
default:
98+
dev_err(pci->dev, "UNKNOWN IRQ type\n");
99+
return -EINVAL;
100+
}
101+
}
102+
103+
static const struct pci_epc_features stm32_pcie_epc_features = {
104+
.msi_capable = true,
105+
.align = SZ_64K,
106+
};
107+
108+
static const struct pci_epc_features*
109+
stm32_pcie_get_features(struct dw_pcie_ep *ep)
110+
{
111+
return &stm32_pcie_epc_features;
112+
}
113+
114+
static const struct dw_pcie_ep_ops stm32_pcie_ep_ops = {
115+
.init = stm32_pcie_ep_init,
116+
.raise_irq = stm32_pcie_raise_irq,
117+
.get_features = stm32_pcie_get_features,
118+
};
119+
120+
static const struct dw_pcie_ops dw_pcie_ops = {
121+
.start_link = stm32_pcie_start_link,
122+
.stop_link = stm32_pcie_stop_link,
123+
};
124+
125+
static int stm32_pcie_enable_resources(struct stm32_pcie *stm32_pcie)
126+
{
127+
int ret;
128+
129+
ret = phy_init(stm32_pcie->phy);
130+
if (ret)
131+
return ret;
132+
133+
ret = clk_prepare_enable(stm32_pcie->clk);
134+
if (ret)
135+
phy_exit(stm32_pcie->phy);
136+
137+
return ret;
138+
}
139+
140+
static void stm32_pcie_disable_resources(struct stm32_pcie *stm32_pcie)
141+
{
142+
clk_disable_unprepare(stm32_pcie->clk);
143+
144+
phy_exit(stm32_pcie->phy);
145+
}
146+
147+
static void stm32_pcie_perst_assert(struct dw_pcie *pci)
148+
{
149+
struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
150+
struct dw_pcie_ep *ep = &stm32_pcie->pci.ep;
151+
struct device *dev = pci->dev;
152+
153+
dev_dbg(dev, "PERST asserted by host\n");
154+
155+
pci_epc_deinit_notify(ep->epc);
156+
157+
stm32_pcie_disable_resources(stm32_pcie);
158+
159+
pm_runtime_put_sync(dev);
160+
}
161+
162+
static void stm32_pcie_perst_deassert(struct dw_pcie *pci)
163+
{
164+
struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
165+
struct device *dev = pci->dev;
166+
struct dw_pcie_ep *ep = &pci->ep;
167+
int ret;
168+
169+
dev_dbg(dev, "PERST de-asserted by host\n");
170+
171+
ret = pm_runtime_resume_and_get(dev);
172+
if (ret < 0) {
173+
dev_err(dev, "Failed to resume runtime PM: %d\n", ret);
174+
return;
175+
}
176+
177+
ret = stm32_pcie_enable_resources(stm32_pcie);
178+
if (ret) {
179+
dev_err(dev, "Failed to enable resources: %d\n", ret);
180+
goto err_pm_put_sync;
181+
}
182+
183+
/*
184+
* Reprogram the configuration space registers here because the DBI
185+
* registers were reset by the PHY RCC during phy_init().
186+
*/
187+
ret = dw_pcie_ep_init_registers(ep);
188+
if (ret) {
189+
dev_err(dev, "Failed to complete initialization: %d\n", ret);
190+
goto err_disable_resources;
191+
}
192+
193+
pci_epc_init_notify(ep->epc);
194+
195+
return;
196+
197+
err_disable_resources:
198+
stm32_pcie_disable_resources(stm32_pcie);
199+
200+
err_pm_put_sync:
201+
pm_runtime_put_sync(dev);
202+
}
203+
204+
static irqreturn_t stm32_pcie_ep_perst_irq_thread(int irq, void *data)
205+
{
206+
struct stm32_pcie *stm32_pcie = data;
207+
struct dw_pcie *pci = &stm32_pcie->pci;
208+
u32 perst;
209+
210+
perst = gpiod_get_value(stm32_pcie->perst_gpio);
211+
if (perst)
212+
stm32_pcie_perst_assert(pci);
213+
else
214+
stm32_pcie_perst_deassert(pci);
215+
216+
irq_set_irq_type(gpiod_to_irq(stm32_pcie->perst_gpio),
217+
(perst ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW));
218+
219+
return IRQ_HANDLED;
220+
}
221+
222+
static int stm32_add_pcie_ep(struct stm32_pcie *stm32_pcie,
223+
struct platform_device *pdev)
224+
{
225+
struct dw_pcie_ep *ep = &stm32_pcie->pci.ep;
226+
struct device *dev = &pdev->dev;
227+
int ret;
228+
229+
ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
230+
STM32MP25_PCIECR_TYPE_MASK,
231+
STM32MP25_PCIECR_EP);
232+
if (ret)
233+
return ret;
234+
235+
reset_control_assert(stm32_pcie->rst);
236+
reset_control_deassert(stm32_pcie->rst);
237+
238+
ep->ops = &stm32_pcie_ep_ops;
239+
240+
ret = dw_pcie_ep_init(ep);
241+
if (ret) {
242+
dev_err(dev, "Failed to initialize ep: %d\n", ret);
243+
return ret;
244+
}
245+
246+
ret = stm32_pcie_enable_resources(stm32_pcie);
247+
if (ret) {
248+
dev_err(dev, "Failed to enable resources: %d\n", ret);
249+
dw_pcie_ep_deinit(ep);
250+
return ret;
251+
}
252+
253+
return 0;
254+
}
255+
256+
static int stm32_pcie_probe(struct platform_device *pdev)
257+
{
258+
struct stm32_pcie *stm32_pcie;
259+
struct device *dev = &pdev->dev;
260+
int ret;
261+
262+
stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL);
263+
if (!stm32_pcie)
264+
return -ENOMEM;
265+
266+
stm32_pcie->pci.dev = dev;
267+
stm32_pcie->pci.ops = &dw_pcie_ops;
268+
269+
stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg");
270+
if (IS_ERR(stm32_pcie->regmap))
271+
return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap),
272+
"No syscfg specified\n");
273+
274+
stm32_pcie->phy = devm_phy_get(dev, NULL);
275+
if (IS_ERR(stm32_pcie->phy))
276+
return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy),
277+
"failed to get pcie-phy\n");
278+
279+
stm32_pcie->clk = devm_clk_get(dev, NULL);
280+
if (IS_ERR(stm32_pcie->clk))
281+
return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk),
282+
"Failed to get PCIe clock source\n");
283+
284+
stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL);
285+
if (IS_ERR(stm32_pcie->rst))
286+
return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst),
287+
"Failed to get PCIe reset\n");
288+
289+
stm32_pcie->perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN);
290+
if (IS_ERR(stm32_pcie->perst_gpio))
291+
return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio),
292+
"Failed to get reset GPIO\n");
293+
294+
ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE);
295+
if (ret)
296+
return ret;
297+
298+
platform_set_drvdata(pdev, stm32_pcie);
299+
300+
pm_runtime_get_noresume(dev);
301+
302+
ret = devm_pm_runtime_enable(dev);
303+
if (ret < 0) {
304+
pm_runtime_put_noidle(&pdev->dev);
305+
return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
306+
}
307+
308+
stm32_pcie->perst_irq = gpiod_to_irq(stm32_pcie->perst_gpio);
309+
310+
/* Will be enabled in start_link when device is initialized. */
311+
irq_set_status_flags(stm32_pcie->perst_irq, IRQ_NOAUTOEN);
312+
313+
ret = devm_request_threaded_irq(dev, stm32_pcie->perst_irq, NULL,
314+
stm32_pcie_ep_perst_irq_thread,
315+
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
316+
"perst_irq", stm32_pcie);
317+
if (ret) {
318+
pm_runtime_put_noidle(&pdev->dev);
319+
return dev_err_probe(dev, ret, "Failed to request PERST IRQ\n");
320+
}
321+
322+
ret = stm32_add_pcie_ep(stm32_pcie, pdev);
323+
if (ret)
324+
pm_runtime_put_noidle(&pdev->dev);
325+
326+
return ret;
327+
}
328+
329+
static void stm32_pcie_remove(struct platform_device *pdev)
330+
{
331+
struct stm32_pcie *stm32_pcie = platform_get_drvdata(pdev);
332+
struct dw_pcie *pci = &stm32_pcie->pci;
333+
struct dw_pcie_ep *ep = &pci->ep;
334+
335+
dw_pcie_stop_link(pci);
336+
337+
pci_epc_deinit_notify(ep->epc);
338+
dw_pcie_ep_deinit(ep);
339+
340+
stm32_pcie_disable_resources(stm32_pcie);
341+
342+
pm_runtime_put_sync(&pdev->dev);
343+
}
344+
345+
static const struct of_device_id stm32_pcie_ep_of_match[] = {
346+
{ .compatible = "st,stm32mp25-pcie-ep" },
347+
{},
348+
};
349+
350+
static struct platform_driver stm32_pcie_ep_driver = {
351+
.probe = stm32_pcie_probe,
352+
.remove = stm32_pcie_remove,
353+
.driver = {
354+
.name = "stm32-ep-pcie",
355+
.of_match_table = stm32_pcie_ep_of_match,
356+
},
357+
};
358+
359+
module_platform_driver(stm32_pcie_ep_driver);
360+
361+
MODULE_AUTHOR("Christian Bruel <christian.bruel@foss.st.com>");
362+
MODULE_DESCRIPTION("STM32MP25 PCIe Endpoint Controller driver");
363+
MODULE_LICENSE("GPL");
364+
MODULE_DEVICE_TABLE(of, stm32_pcie_ep_of_match);

drivers/pci/controller/dwc/pcie-stm32.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#define to_stm32_pcie(x) dev_get_drvdata((x)->dev)
1010

1111
#define STM32MP25_PCIECR_TYPE_MASK GENMASK(11, 8)
12+
#define STM32MP25_PCIECR_EP 0
1213
#define STM32MP25_PCIECR_LTSSM_EN BIT(2)
1314
#define STM32MP25_PCIECR_RC BIT(10)
1415

0 commit comments

Comments
 (0)