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