]>
Commit | Line | Data |
---|---|---|
2f5ad77c SR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * PCIe host bridge driver for Xilinx / AMD ZynqMP NWL PCIe Bridge | |
4 | * | |
5 | * Based on the Linux driver which is: | |
6 | * (C) Copyright 2014 - 2015, Xilinx, Inc. | |
7 | * | |
8 | * Author: Stefan Roese <[email protected]> | |
9 | */ | |
10 | ||
11 | #include <clk.h> | |
12 | #include <dm.h> | |
13 | #include <dm/device_compat.h> | |
14 | #include <dm/devres.h> | |
15 | #include <mapmem.h> | |
16 | #include <pci.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/ioport.h> | |
20 | ||
21 | /* Bridge core config registers */ | |
22 | #define BRCFG_PCIE_RX0 0x00000000 | |
23 | #define BRCFG_PCIE_RX1 0x00000004 | |
24 | #define BRCFG_INTERRUPT 0x00000010 | |
25 | #define BRCFG_PCIE_RX_MSG_FILTER 0x00000020 | |
26 | ||
27 | /* Egress - Bridge translation registers */ | |
28 | #define E_BREG_CAPABILITIES 0x00000200 | |
29 | #define E_BREG_CONTROL 0x00000208 | |
30 | #define E_BREG_BASE_LO 0x00000210 | |
31 | #define E_BREG_BASE_HI 0x00000214 | |
32 | #define E_ECAM_CAPABILITIES 0x00000220 | |
33 | #define E_ECAM_CONTROL 0x00000228 | |
34 | #define E_ECAM_BASE_LO 0x00000230 | |
35 | #define E_ECAM_BASE_HI 0x00000234 | |
36 | ||
37 | #define I_ISUB_CONTROL 0x000003E8 | |
38 | #define SET_ISUB_CONTROL BIT(0) | |
39 | /* Rxed msg fifo - Interrupt status registers */ | |
40 | #define MSGF_MISC_STATUS 0x00000400 | |
41 | #define MSGF_MISC_MASK 0x00000404 | |
42 | #define MSGF_LEG_STATUS 0x00000420 | |
43 | #define MSGF_LEG_MASK 0x00000424 | |
44 | #define MSGF_MSI_STATUS_LO 0x00000440 | |
45 | #define MSGF_MSI_STATUS_HI 0x00000444 | |
46 | #define MSGF_MSI_MASK_LO 0x00000448 | |
47 | #define MSGF_MSI_MASK_HI 0x0000044C | |
48 | ||
49 | /* Msg filter mask bits */ | |
50 | #define CFG_ENABLE_PM_MSG_FWD BIT(1) | |
51 | #define CFG_ENABLE_INT_MSG_FWD BIT(2) | |
52 | #define CFG_ENABLE_ERR_MSG_FWD BIT(3) | |
53 | #define CFG_ENABLE_MSG_FILTER_MASK (CFG_ENABLE_PM_MSG_FWD | \ | |
54 | CFG_ENABLE_INT_MSG_FWD | \ | |
55 | CFG_ENABLE_ERR_MSG_FWD) | |
56 | ||
57 | /* Misc interrupt status mask bits */ | |
58 | #define MSGF_MISC_SR_RXMSG_AVAIL BIT(0) | |
59 | #define MSGF_MISC_SR_RXMSG_OVER BIT(1) | |
60 | #define MSGF_MISC_SR_SLAVE_ERR BIT(4) | |
61 | #define MSGF_MISC_SR_MASTER_ERR BIT(5) | |
62 | #define MSGF_MISC_SR_I_ADDR_ERR BIT(6) | |
63 | #define MSGF_MISC_SR_E_ADDR_ERR BIT(7) | |
64 | #define MSGF_MISC_SR_FATAL_AER BIT(16) | |
65 | #define MSGF_MISC_SR_NON_FATAL_AER BIT(17) | |
66 | #define MSGF_MISC_SR_CORR_AER BIT(18) | |
67 | #define MSGF_MISC_SR_UR_DETECT BIT(20) | |
68 | #define MSGF_MISC_SR_NON_FATAL_DEV BIT(22) | |
69 | #define MSGF_MISC_SR_FATAL_DEV BIT(23) | |
70 | #define MSGF_MISC_SR_LINK_DOWN BIT(24) | |
71 | #define MSGF_MSIC_SR_LINK_AUTO_BWIDTH BIT(25) | |
72 | #define MSGF_MSIC_SR_LINK_BWIDTH BIT(26) | |
73 | ||
74 | #define MSGF_MISC_SR_MASKALL (MSGF_MISC_SR_RXMSG_AVAIL | \ | |
75 | MSGF_MISC_SR_RXMSG_OVER | \ | |
76 | MSGF_MISC_SR_SLAVE_ERR | \ | |
77 | MSGF_MISC_SR_MASTER_ERR | \ | |
78 | MSGF_MISC_SR_I_ADDR_ERR | \ | |
79 | MSGF_MISC_SR_E_ADDR_ERR | \ | |
80 | MSGF_MISC_SR_FATAL_AER | \ | |
81 | MSGF_MISC_SR_NON_FATAL_AER | \ | |
82 | MSGF_MISC_SR_CORR_AER | \ | |
83 | MSGF_MISC_SR_UR_DETECT | \ | |
84 | MSGF_MISC_SR_NON_FATAL_DEV | \ | |
85 | MSGF_MISC_SR_FATAL_DEV | \ | |
86 | MSGF_MISC_SR_LINK_DOWN | \ | |
87 | MSGF_MSIC_SR_LINK_AUTO_BWIDTH | \ | |
88 | MSGF_MSIC_SR_LINK_BWIDTH) | |
89 | ||
90 | /* Legacy interrupt status mask bits */ | |
91 | #define MSGF_LEG_SR_INTA BIT(0) | |
92 | #define MSGF_LEG_SR_INTB BIT(1) | |
93 | #define MSGF_LEG_SR_INTC BIT(2) | |
94 | #define MSGF_LEG_SR_INTD BIT(3) | |
95 | #define MSGF_LEG_SR_MASKALL (MSGF_LEG_SR_INTA | MSGF_LEG_SR_INTB | \ | |
96 | MSGF_LEG_SR_INTC | MSGF_LEG_SR_INTD) | |
97 | ||
98 | /* MSI interrupt status mask bits */ | |
99 | #define MSGF_MSI_SR_LO_MASK GENMASK(31, 0) | |
100 | #define MSGF_MSI_SR_HI_MASK GENMASK(31, 0) | |
101 | ||
102 | /* Bridge config interrupt mask */ | |
103 | #define BRCFG_INTERRUPT_MASK BIT(0) | |
104 | #define BREG_PRESENT BIT(0) | |
105 | #define BREG_ENABLE BIT(0) | |
106 | #define BREG_ENABLE_FORCE BIT(1) | |
107 | ||
108 | /* E_ECAM status mask bits */ | |
109 | #define E_ECAM_PRESENT BIT(0) | |
110 | #define E_ECAM_CR_ENABLE BIT(0) | |
111 | #define E_ECAM_SIZE_LOC GENMASK(20, 16) | |
112 | #define E_ECAM_SIZE_SHIFT 16 | |
113 | #define NWL_ECAM_VALUE_DEFAULT 12 | |
114 | ||
115 | #define CFG_DMA_REG_BAR GENMASK(2, 0) | |
116 | #define CFG_PCIE_CACHE GENMASK(7, 0) | |
117 | ||
118 | /* Readin the PS_LINKUP */ | |
119 | #define PS_LINKUP_OFFSET 0x00000238 | |
120 | #define PCIE_PHY_LINKUP_BIT BIT(0) | |
121 | #define PHY_RDY_LINKUP_BIT BIT(1) | |
122 | ||
123 | /* Parameters for the waiting for link up routine */ | |
124 | #define LINK_WAIT_MAX_RETRIES 10 | |
125 | #define LINK_WAIT_USLEEP_MIN 90000 | |
126 | #define LINK_WAIT_USLEEP_MAX 100000 | |
127 | ||
128 | struct nwl_pcie { | |
129 | struct udevice *dev; | |
130 | void __iomem *breg_base; | |
131 | void __iomem *pcireg_base; | |
132 | void __iomem *ecam_base; | |
133 | phys_addr_t phys_breg_base; /* Physical Bridge Register Base */ | |
134 | phys_addr_t phys_ecam_base; /* Physical Configuration Base */ | |
135 | u32 ecam_value; | |
136 | }; | |
137 | ||
138 | static int nwl_pcie_config_address(const struct udevice *bus, | |
139 | pci_dev_t bdf, uint offset, | |
140 | void **paddress) | |
141 | { | |
142 | struct nwl_pcie *pcie = dev_get_priv(bus); | |
143 | void *addr; | |
144 | ||
145 | addr = pcie->ecam_base; | |
146 | addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf) - dev_seq(bus), | |
147 | PCI_DEV(bdf), PCI_FUNC(bdf), offset); | |
148 | *paddress = addr; | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static int nwl_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, | |
154 | uint offset, ulong *valuep, | |
155 | enum pci_size_t size) | |
156 | { | |
157 | return pci_generic_mmap_read_config(bus, nwl_pcie_config_address, | |
158 | bdf, offset, valuep, size); | |
159 | } | |
160 | ||
161 | static int nwl_pcie_write_config(struct udevice *bus, pci_dev_t bdf, | |
162 | uint offset, ulong value, | |
163 | enum pci_size_t size) | |
164 | { | |
165 | return pci_generic_mmap_write_config(bus, nwl_pcie_config_address, | |
166 | bdf, offset, value, size); | |
167 | } | |
168 | ||
169 | static const struct dm_pci_ops nwl_pcie_ops = { | |
170 | .read_config = nwl_pcie_read_config, | |
171 | .write_config = nwl_pcie_write_config, | |
172 | }; | |
173 | ||
174 | static inline u32 nwl_bridge_readl(struct nwl_pcie *pcie, u32 off) | |
175 | { | |
176 | return readl(pcie->breg_base + off); | |
177 | } | |
178 | ||
179 | static inline void nwl_bridge_writel(struct nwl_pcie *pcie, u32 val, u32 off) | |
180 | { | |
181 | writel(val, pcie->breg_base + off); | |
182 | } | |
183 | ||
184 | static bool nwl_pcie_link_up(struct nwl_pcie *pcie) | |
185 | { | |
186 | if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PCIE_PHY_LINKUP_BIT) | |
187 | return true; | |
188 | return false; | |
189 | } | |
190 | ||
191 | static bool nwl_phy_link_up(struct nwl_pcie *pcie) | |
192 | { | |
193 | if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PHY_RDY_LINKUP_BIT) | |
194 | return true; | |
195 | return false; | |
196 | } | |
197 | ||
198 | static int nwl_wait_for_link(struct nwl_pcie *pcie) | |
199 | { | |
200 | struct udevice *dev = pcie->dev; | |
201 | int retries; | |
202 | ||
203 | /* check if the link is up or not */ | |
204 | for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { | |
205 | if (nwl_phy_link_up(pcie)) | |
206 | return 0; | |
207 | udelay(LINK_WAIT_USLEEP_MIN); | |
208 | } | |
209 | ||
210 | dev_warn(dev, "PHY link never came up\n"); | |
211 | return -ETIMEDOUT; | |
212 | } | |
213 | ||
214 | static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) | |
215 | { | |
216 | struct udevice *dev = pcie->dev; | |
217 | u32 breg_val, ecam_val; | |
218 | int err; | |
219 | ||
220 | breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT; | |
221 | if (!breg_val) { | |
222 | dev_err(dev, "BREG is not present\n"); | |
223 | return breg_val; | |
224 | } | |
225 | ||
226 | /* Write bridge_off to breg base */ | |
227 | nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_breg_base), | |
228 | E_BREG_BASE_LO); | |
229 | nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_breg_base), | |
230 | E_BREG_BASE_HI); | |
231 | ||
232 | /* Enable BREG */ | |
233 | nwl_bridge_writel(pcie, ~BREG_ENABLE_FORCE & BREG_ENABLE, | |
234 | E_BREG_CONTROL); | |
235 | ||
236 | /* Disable DMA channel registers */ | |
237 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_PCIE_RX0) | | |
238 | CFG_DMA_REG_BAR, BRCFG_PCIE_RX0); | |
239 | ||
240 | /* Enable Ingress subtractive decode translation */ | |
241 | nwl_bridge_writel(pcie, SET_ISUB_CONTROL, I_ISUB_CONTROL); | |
242 | ||
243 | /* Enable msg filtering details */ | |
244 | nwl_bridge_writel(pcie, CFG_ENABLE_MSG_FILTER_MASK, | |
245 | BRCFG_PCIE_RX_MSG_FILTER); | |
246 | ||
247 | err = nwl_wait_for_link(pcie); | |
248 | if (err) | |
249 | return err; | |
250 | ||
251 | ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT; | |
252 | if (!ecam_val) { | |
253 | dev_err(dev, "ECAM is not present\n"); | |
254 | return ecam_val; | |
255 | } | |
256 | ||
257 | /* Enable ECAM */ | |
258 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | | |
259 | E_ECAM_CR_ENABLE, E_ECAM_CONTROL); | |
260 | ||
261 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | | |
262 | (pcie->ecam_value << E_ECAM_SIZE_SHIFT), | |
263 | E_ECAM_CONTROL); | |
264 | ||
265 | nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_ecam_base), | |
266 | E_ECAM_BASE_LO); | |
267 | nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_ecam_base), | |
268 | E_ECAM_BASE_HI); | |
269 | ||
270 | if (nwl_pcie_link_up(pcie)) | |
271 | dev_info(dev, "Link is UP\n"); | |
272 | else | |
273 | dev_info(dev, "Link is DOWN\n"); | |
274 | ||
275 | /* Disable all misc interrupts */ | |
276 | nwl_bridge_writel(pcie, (u32)~MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); | |
277 | ||
278 | /* Clear pending misc interrupts */ | |
279 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MISC_STATUS) & | |
280 | MSGF_MISC_SR_MASKALL, MSGF_MISC_STATUS); | |
281 | ||
282 | /* Disable all legacy interrupts */ | |
283 | nwl_bridge_writel(pcie, (u32)~MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); | |
284 | ||
285 | /* Clear pending legacy interrupts */ | |
286 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & | |
287 | MSGF_LEG_SR_MASKALL, MSGF_LEG_STATUS); | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | static int nwl_pcie_parse_dt(struct nwl_pcie *pcie) | |
293 | { | |
294 | struct udevice *dev = pcie->dev; | |
295 | struct resource res; | |
296 | int ret; | |
297 | ||
298 | ret = dev_read_resource_byname(dev, "breg", &res); | |
299 | if (ret) | |
300 | return ret; | |
301 | pcie->breg_base = devm_ioremap(dev, res.start, resource_size(&res)); | |
302 | if (IS_ERR(pcie->breg_base)) | |
303 | return PTR_ERR(pcie->breg_base); | |
304 | pcie->phys_breg_base = res.start; | |
305 | ||
306 | ret = dev_read_resource_byname(dev, "cfg", &res); | |
307 | if (ret) | |
308 | return ret; | |
309 | pcie->ecam_base = devm_ioremap(dev, res.start, resource_size(&res)); | |
310 | if (IS_ERR(pcie->ecam_base)) | |
311 | return PTR_ERR(pcie->ecam_base); | |
312 | pcie->phys_ecam_base = res.start; | |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
317 | static int nwl_pcie_probe(struct udevice *dev) | |
318 | { | |
319 | struct nwl_pcie *pcie = dev_get_priv(dev); | |
320 | int err; | |
321 | ||
322 | pcie->dev = dev; | |
323 | pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT; | |
324 | ||
325 | err = nwl_pcie_parse_dt(pcie); | |
326 | if (err) { | |
327 | dev_err(dev, "Parsing DT failed\n"); | |
328 | return err; | |
329 | } | |
330 | ||
331 | err = nwl_pcie_bridge_init(pcie); | |
332 | if (err) { | |
333 | dev_err(dev, "HW Initialization failed\n"); | |
334 | return err; | |
335 | } | |
336 | ||
337 | return 0; | |
338 | } | |
339 | ||
340 | static const struct udevice_id nwl_pcie_of_match[] = { | |
341 | { .compatible = "xlnx,nwl-pcie-2.11", }, | |
342 | { /* sentinel */ } | |
343 | }; | |
344 | ||
345 | U_BOOT_DRIVER(nwl_pcie) = { | |
346 | .name = "nwl-pcie", | |
347 | .id = UCLASS_PCI, | |
348 | .of_match = nwl_pcie_of_match, | |
349 | .probe = nwl_pcie_probe, | |
350 | .priv_auto = sizeof(struct nwl_pcie), | |
351 | .ops = &nwl_pcie_ops, | |
352 | }; |