File Index
Files and directories
Sign in
FILE

pcie-nxp-s32g.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pci/controller/dwc/pcie-nxp-s32g.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * PCIe host controller driver for NXP S32G SoCs
  4 *
  5 * Copyright 2019-2025 NXP
  6 */
  7
  8#include <linux/interrupt.h>
  9#include <linux/io.h>
 10#include <linux/module.h>
 11#include <linux/of_device.h>
 12#include <linux/of_address.h>
 13#include <linux/pci.h>
 14#include <linux/phy/phy.h>
 15#include <linux/platform_device.h>
 16#include <linux/pm_runtime.h>
 17#include <linux/sizes.h>
 18#include <linux/types.h>
 19
 20#include "pcie-designware.h"
 21
 22/* PCIe controller Sub-System */
 23
 24/* PCIe controller 0 General Control 1 */
 25#define PCIE_S32G_PE0_GEN_CTRL_1		0x50
 26#define DEVICE_TYPE_MASK			GENMASK(3, 0)
 27#define SRIS_MODE				BIT(8)
 28
 29/* PCIe controller 0 General Control 3 */
 30#define PCIE_S32G_PE0_GEN_CTRL_3		0x58
 31#define LTSSM_EN				BIT(0)
 32
 33/* PCIe Controller 0  Interrupt Status */
 34#define PCIE_S32G_PE0_INT_STS			0xE8
 35#define HP_INT_STS				BIT(6)
 36
 37/* Boundary between peripheral space and physical memory space */
 38#define S32G_MEMORY_BOUNDARY_ADDR		0x80000000
 39
 40struct s32g_pcie_port {
 41	struct list_head list;
 42	struct phy *phy;
 43};
 44
 45struct s32g_pcie {
 46	struct dw_pcie	pci;
 47	void __iomem *ctrl_base;
 48	struct list_head ports;
 49};
 50
 51#define to_s32g_from_dw_pcie(x) \
 52	container_of(x, struct s32g_pcie, pci)
 53
 54static void s32g_pcie_writel_ctrl(struct s32g_pcie *s32g_pp, u32 reg, u32 val)
 55{
 56	writel(val, s32g_pp->ctrl_base + reg);
 57}
 58
 59static u32 s32g_pcie_readl_ctrl(struct s32g_pcie *s32g_pp, u32 reg)
 60{
 61	return readl(s32g_pp->ctrl_base + reg);
 62}
 63
 64static void s32g_pcie_enable_ltssm(struct s32g_pcie *s32g_pp)
 65{
 66	u32 reg;
 67
 68	reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3);
 69	reg |= LTSSM_EN;
 70	s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg);
 71}
 72
 73static void s32g_pcie_disable_ltssm(struct s32g_pcie *s32g_pp)
 74{
 75	u32 reg;
 76
 77	reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3);
 78	reg &= ~LTSSM_EN;
 79	s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg);
 80}
 81
 82static int s32g_pcie_start_link(struct dw_pcie *pci)
 83{
 84	struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci);
 85
 86	s32g_pcie_enable_ltssm(s32g_pp);
 87
 88	return 0;
 89}
 90
 91static void s32g_pcie_stop_link(struct dw_pcie *pci)
 92{
 93	struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci);
 94
 95	s32g_pcie_disable_ltssm(s32g_pp);
 96}
 97
 98static struct dw_pcie_ops s32g_pcie_ops = {
 99	.start_link = s32g_pcie_start_link,
100	.stop_link = s32g_pcie_stop_link,
101};
102
103/* Configure the AMBA AXI Coherency Extensions (ACE) interface */
104static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci)
105{
106	u32 ddr_base_low = lower_32_bits(S32G_MEMORY_BOUNDARY_ADDR);
107	u32 ddr_base_high = upper_32_bits(S32G_MEMORY_BOUNDARY_ADDR);
108
109	dw_pcie_dbi_ro_wr_en(pci);
110	dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0);
111
112	/*
113	 * Ncore is a cache-coherent interconnect module that enables the
114	 * integration of heterogeneous coherent and non-coherent agents in
115	 * the chip. Ncore transactions to peripheral should be non-coherent
116	 * or it might drop them.
117	 *
118	 * One example where this is needed are PCIe MSIs, which use NoSnoop=0
119	 * and might end up routed to Ncore. PCIe coherent traffic (e.g. MSIs)
120	 * that targets peripheral space will be dropped by Ncore because
121	 * peripherals on S32G are not coherent as slaves. We add a hard
122	 * boundary in the PCIe controller coherency control registers to
123	 * separate physical memory space from peripheral space.
124	 *
125	 * Define the start of DDR as seen by Linux as this boundary between
126	 * "memory" and "peripherals", with peripherals being below.
127	 */
128	dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF,
129			   (ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK));
130	dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high);
131	dw_pcie_dbi_ro_wr_dis(pci);
132}
133
134static int s32g_init_pcie_controller(struct dw_pcie_rp *pp)
135{
136	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
137	struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci);
138	u32 val;
139
140	/* Set RP mode */
141	val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1);
142	val &= ~DEVICE_TYPE_MASK;
143	val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT);
144
145	/* Use default CRNS */
146	val &= ~SRIS_MODE;
147
148	s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1, val);
149
150	/*
151	 * Make sure we use the coherency defaults (just in case the settings
152	 * have been changed from their reset values)
153	 */
154	s32g_pcie_reset_mstr_ace(pci);
155
156	dw_pcie_dbi_ro_wr_en(pci);
157
158	val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE);
159	val |= PORT_FORCE_DO_DESKEW_FOR_SRIS;
160	dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val);
161
162	val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
163	val |= GEN3_RELATED_OFF_EQ_PHASE_2_3;
164	dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
165
166	dw_pcie_dbi_ro_wr_dis(pci);
167
168	return 0;
169}
170
171static const struct dw_pcie_host_ops s32g_pcie_host_ops = {
172	.init = s32g_init_pcie_controller,
173};
174
175static int s32g_init_pcie_phy(struct s32g_pcie *s32g_pp)
176{
177	struct dw_pcie *pci = &s32g_pp->pci;
178	struct device *dev = pci->dev;
179	struct s32g_pcie_port *port, *tmp;
180	int ret;
181
182	list_for_each_entry(port, &s32g_pp->ports, list) {
183		ret = phy_init(port->phy);
184		if (ret) {
185			dev_err(dev, "Failed to init serdes PHY\n");
186			goto err_phy_revert;
187		}
188
189		ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, 0);
190		if (ret) {
191			dev_err(dev, "Failed to set mode on serdes PHY\n");
192			goto err_phy_exit;
193		}
194
195		ret = phy_power_on(port->phy);
196		if (ret) {
197			dev_err(dev, "Failed to power on serdes PHY\n");
198			goto err_phy_exit;
199		}
200	}
201
202	return 0;
203
204err_phy_exit:
205	phy_exit(port->phy);
206
207err_phy_revert:
208	list_for_each_entry_continue_reverse(port, &s32g_pp->ports, list) {
209		phy_power_off(port->phy);
210		phy_exit(port->phy);
211	}
212
213	list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list)
214		list_del(&port->list);
215
216	return ret;
217}
218
219static void s32g_deinit_pcie_phy(struct s32g_pcie *s32g_pp)
220{
221	struct s32g_pcie_port *port, *tmp;
222
223	list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) {
224		phy_power_off(port->phy);
225		phy_exit(port->phy);
226		list_del(&port->list);
227	}
228}
229
230static int s32g_pcie_init(struct device *dev, struct s32g_pcie *s32g_pp)
231{
232	s32g_pcie_disable_ltssm(s32g_pp);
233
234	return s32g_init_pcie_phy(s32g_pp);
235}
236
237static void s32g_pcie_deinit(struct s32g_pcie *s32g_pp)
238{
239	s32g_pcie_disable_ltssm(s32g_pp);
240
241	s32g_deinit_pcie_phy(s32g_pp);
242}
243
244static int s32g_pcie_parse_port(struct s32g_pcie *s32g_pp, struct device_node *node)
245{
246	struct device *dev = s32g_pp->pci.dev;
247	struct s32g_pcie_port *port;
248	int num_lanes;
249
250	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
251	if (!port)
252		return -ENOMEM;
253
254	port->phy = devm_of_phy_get(dev, node, NULL);
255	if (IS_ERR(port->phy))
256		return dev_err_probe(dev, PTR_ERR(port->phy),
257				"Failed to get serdes PHY\n");
258
259	INIT_LIST_HEAD(&port->list);
260	list_add_tail(&port->list, &s32g_pp->ports);
261
262	/*
263	 * The DWC core initialization code cannot yet parse the num-lanes
264	 * attribute in the Root Port node. The S32G only supports one Root
265	 * Port for now so its driver can parse the node and set the num_lanes
266	 * field of struct dwc_pcie before calling dw_pcie_host_init().
267	 */
268	if (!of_property_read_u32(node, "num-lanes", &num_lanes))
269		s32g_pp->pci.num_lanes = num_lanes;
270
271	return 0;
272}
273
274static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp)
275{
276	struct s32g_pcie_port *port, *tmp;
277	int ret = -ENOENT;
278
279	for_each_available_child_of_node_scoped(dev->of_node, of_port) {
280		if (!of_node_is_type(of_port, "pci"))
281			continue;
282
283		ret = s32g_pcie_parse_port(s32g_pp, of_port);
284		if (ret)
285			break;
286	}
287
288	if (ret)
289		list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list)
290			list_del(&port->list);
291
292	return ret;
293}
294
295static int s32g_pcie_get_resources(struct platform_device *pdev,
296				   struct s32g_pcie *s32g_pp)
297{
298	struct dw_pcie *pci = &s32g_pp->pci;
299	struct device *dev = &pdev->dev;
300	int ret;
301
302	pci->dev = dev;
303	pci->ops = &s32g_pcie_ops;
304
305	s32g_pp->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "ctrl");
306	if (IS_ERR(s32g_pp->ctrl_base))
307		return PTR_ERR(s32g_pp->ctrl_base);
308
309	INIT_LIST_HEAD(&s32g_pp->ports);
310
311	ret = s32g_pcie_parse_ports(dev, s32g_pp);
312	if (ret)
313		return dev_err_probe(dev, ret,
314				"Failed to parse Root Port: %d\n", ret);
315
316	platform_set_drvdata(pdev, s32g_pp);
317
318	return 0;
319}
320
321static int s32g_pcie_probe(struct platform_device *pdev)
322{
323	struct device *dev = &pdev->dev;
324	struct s32g_pcie *s32g_pp;
325	struct dw_pcie_rp *pp;
326	int ret;
327
328	s32g_pp = devm_kzalloc(dev, sizeof(*s32g_pp), GFP_KERNEL);
329	if (!s32g_pp)
330		return -ENOMEM;
331
332	ret = s32g_pcie_get_resources(pdev, s32g_pp);
333	if (ret)
334		return ret;
335
336	pm_runtime_no_callbacks(dev);
337	devm_pm_runtime_enable(dev);
338	ret = pm_runtime_get_sync(dev);
339	if (ret < 0)
340		goto err_pm_runtime_put;
341
342	ret = s32g_pcie_init(dev, s32g_pp);
343	if (ret)
344		goto err_pm_runtime_put;
345
346	pp = &s32g_pp->pci.pp;
347	pp->ops = &s32g_pcie_host_ops;
348	pp->use_atu_msg = true;
349
350	ret = dw_pcie_host_init(pp);
351	if (ret)
352		goto err_pcie_deinit;
353
354	return 0;
355
356err_pcie_deinit:
357	s32g_pcie_deinit(s32g_pp);
358err_pm_runtime_put:
359	pm_runtime_put(dev);
360
361	return ret;
362}
363
364static int s32g_pcie_suspend_noirq(struct device *dev)
365{
366	struct s32g_pcie *s32g_pp = dev_get_drvdata(dev);
367	struct dw_pcie *pci = &s32g_pp->pci;
368
369	return dw_pcie_suspend_noirq(pci);
370}
371
372static int s32g_pcie_resume_noirq(struct device *dev)
373{
374	struct s32g_pcie *s32g_pp = dev_get_drvdata(dev);
375	struct dw_pcie *pci = &s32g_pp->pci;
376
377	return dw_pcie_resume_noirq(pci);
378}
379
380static const struct dev_pm_ops s32g_pcie_pm_ops = {
381	NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_pcie_suspend_noirq,
382				  s32g_pcie_resume_noirq)
383};
384
385static const struct of_device_id s32g_pcie_of_match[] = {
386	{ .compatible = "nxp,s32g2-pcie" },
387	{ /* sentinel */ },
388};
389MODULE_DEVICE_TABLE(of, s32g_pcie_of_match);
390
391static struct platform_driver s32g_pcie_driver = {
392	.driver = {
393		.name	= "s32g-pcie",
394		.of_match_table = s32g_pcie_of_match,
395		.suppress_bind_attrs = true,
396		.pm = pm_sleep_ptr(&s32g_pcie_pm_ops),
397		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
398	},
399	.probe = s32g_pcie_probe,
400};
401
402builtin_platform_driver(s32g_pcie_driver);
403
404MODULE_AUTHOR("Ionut Vicovan <Ionut.Vicovan@nxp.com>");
405MODULE_DESCRIPTION("NXP S32G PCIe Host controller driver");
406MODULE_LICENSE("GPL");
~/ Read-only file preview.