]>
Commit | Line | Data |
---|---|---|
7c45862f LFT |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Intel FPGA PCIe host controller driver | |
4 | * | |
5 | * Copyright (C) 2013-2018 Intel Corporation. All rights reserved | |
6 | * | |
7 | */ | |
8 | ||
7c45862f LFT |
9 | #include <dm.h> |
10 | #include <pci.h> | |
401d1c4f | 11 | #include <asm/global_data.h> |
7c45862f | 12 | #include <asm/io.h> |
336d4615 | 13 | #include <dm/device_compat.h> |
cd93d625 | 14 | #include <linux/bitops.h> |
c05ed00a | 15 | #include <linux/delay.h> |
7c45862f LFT |
16 | |
17 | #define RP_TX_REG0 0x2000 | |
18 | #define RP_TX_CNTRL 0x2004 | |
19 | #define RP_TX_SOP BIT(0) | |
20 | #define RP_TX_EOP BIT(1) | |
21 | #define RP_RXCPL_STATUS 0x200C | |
22 | #define RP_RXCPL_SOP BIT(0) | |
23 | #define RP_RXCPL_EOP BIT(1) | |
24 | #define RP_RXCPL_REG 0x2008 | |
25 | #define P2A_INT_STATUS 0x3060 | |
26 | #define P2A_INT_STS_ALL 0xf | |
27 | #define P2A_INT_ENABLE 0x3070 | |
28 | #define RP_CAP_OFFSET 0x70 | |
29 | ||
30 | /* TLP configuration type 0 and 1 */ | |
31 | #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ | |
32 | #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ | |
33 | #define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */ | |
34 | #define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */ | |
35 | #define TLP_PAYLOAD_SIZE 0x01 | |
36 | #define TLP_READ_TAG 0x1d | |
37 | #define TLP_WRITE_TAG 0x10 | |
38 | #define RP_DEVFN 0 | |
39 | ||
40 | #define RP_CFG_ADDR(pcie, reg) \ | |
41 | ((pcie->hip_base) + (reg) + (1 << 20)) | |
d44f7932 LFT |
42 | #define RP_SECONDARY(pcie) \ |
43 | readb(RP_CFG_ADDR(pcie, PCI_SECONDARY_BUS)) | |
7c45862f LFT |
44 | #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) |
45 | ||
46 | #define TLP_CFGRD_DW0(pcie, bus) \ | |
d44f7932 LFT |
47 | ((((bus > RP_SECONDARY(pcie)) ? TLP_FMTTYPE_CFGRD1 \ |
48 | : TLP_FMTTYPE_CFGRD0) << 24) | \ | |
7c45862f LFT |
49 | TLP_PAYLOAD_SIZE) |
50 | ||
51 | #define TLP_CFGWR_DW0(pcie, bus) \ | |
d44f7932 LFT |
52 | ((((bus > RP_SECONDARY(pcie)) ? TLP_FMTTYPE_CFGWR1 \ |
53 | : TLP_FMTTYPE_CFGWR0) << 24) | \ | |
7c45862f LFT |
54 | TLP_PAYLOAD_SIZE) |
55 | ||
56 | #define TLP_CFG_DW1(pcie, tag, be) \ | |
57 | (((TLP_REQ_ID(pcie->first_busno, RP_DEVFN)) << 16) | (tag << 8) | (be)) | |
58 | #define TLP_CFG_DW2(bus, dev, fn, offset) \ | |
59 | (((bus) << 24) | ((dev) << 19) | ((fn) << 16) | (offset)) | |
60 | ||
61 | #define TLP_COMP_STATUS(s) (((s) >> 13) & 7) | |
62 | #define TLP_BYTE_COUNT(s) (((s) >> 0) & 0xfff) | |
63 | #define TLP_HDR_SIZE 3 | |
d0e52c6f | 64 | #define TLP_LOOP 20000 |
7c45862f LFT |
65 | #define DWORD_MASK 3 |
66 | ||
67 | #define IS_ROOT_PORT(pcie, bdf) \ | |
68 | ((PCI_BUS(bdf) == pcie->first_busno) ? true : false) | |
69 | ||
7c45862f LFT |
70 | /** |
71 | * struct intel_fpga_pcie - Intel FPGA PCIe controller state | |
72 | * @bus: Pointer to the PCI bus | |
73 | * @cra_base: The base address of CRA register space | |
74 | * @hip_base: The base address of Rootport configuration space | |
75 | * @first_busno: This driver supports multiple PCIe controllers. | |
76 | * first_busno stores the bus number of the PCIe root-port | |
77 | * number which may vary depending on the PCIe setup. | |
78 | */ | |
79 | struct intel_fpga_pcie { | |
80 | struct udevice *bus; | |
81 | void __iomem *cra_base; | |
82 | void __iomem *hip_base; | |
83 | int first_busno; | |
84 | }; | |
85 | ||
86 | /** | |
87 | * Intel FPGA PCIe port uses BAR0 of RC's configuration space as the | |
88 | * translation from PCI bus to native BUS. Entire DDR region is mapped | |
89 | * into PCIe space using these registers, so it can be reached by DMA from | |
90 | * EP devices. | |
91 | * The BAR0 of bridge should be hidden during enumeration to avoid the | |
92 | * sizing and resource allocation by PCIe core. | |
93 | */ | |
94 | static bool intel_fpga_pcie_hide_rc_bar(struct intel_fpga_pcie *pcie, | |
95 | pci_dev_t bdf, int offset) | |
96 | { | |
97 | if (IS_ROOT_PORT(pcie, bdf) && PCI_DEV(bdf) == 0 && | |
98 | PCI_FUNC(bdf) == 0 && offset == PCI_BASE_ADDRESS_0) | |
99 | return true; | |
100 | ||
101 | return false; | |
102 | } | |
103 | ||
104 | static inline void cra_writel(struct intel_fpga_pcie *pcie, const u32 value, | |
105 | const u32 reg) | |
106 | { | |
107 | writel(value, pcie->cra_base + reg); | |
108 | } | |
109 | ||
110 | static inline u32 cra_readl(struct intel_fpga_pcie *pcie, const u32 reg) | |
111 | { | |
112 | return readl(pcie->cra_base + reg); | |
113 | } | |
114 | ||
115 | static bool intel_fpga_pcie_link_up(struct intel_fpga_pcie *pcie) | |
116 | { | |
117 | return !!(readw(RP_CFG_ADDR(pcie, RP_CAP_OFFSET + PCI_EXP_LNKSTA)) | |
118 | & PCI_EXP_LNKSTA_DLLLA); | |
119 | } | |
120 | ||
121 | static bool intel_fpga_pcie_addr_valid(struct intel_fpga_pcie *pcie, | |
122 | pci_dev_t bdf) | |
123 | { | |
124 | /* If there is no link, then there is no device */ | |
125 | if (!IS_ROOT_PORT(pcie, bdf) && !intel_fpga_pcie_link_up(pcie)) | |
126 | return false; | |
127 | ||
128 | /* access only one slot on each root port */ | |
129 | if (IS_ROOT_PORT(pcie, bdf) && PCI_DEV(bdf) > 0) | |
130 | return false; | |
131 | ||
132 | if ((PCI_BUS(bdf) == pcie->first_busno + 1) && PCI_DEV(bdf) > 0) | |
133 | return false; | |
134 | ||
135 | return true; | |
136 | } | |
137 | ||
138 | static void tlp_write_tx(struct intel_fpga_pcie *pcie, u32 reg0, u32 ctrl) | |
139 | { | |
140 | cra_writel(pcie, reg0, RP_TX_REG0); | |
141 | cra_writel(pcie, ctrl, RP_TX_CNTRL); | |
142 | } | |
143 | ||
144 | static int tlp_read_packet(struct intel_fpga_pcie *pcie, u32 *value) | |
145 | { | |
146 | int i; | |
147 | u32 ctrl; | |
148 | u32 comp_status; | |
149 | u32 dw[4]; | |
150 | u32 count = 0; | |
151 | ||
152 | for (i = 0; i < TLP_LOOP; i++) { | |
153 | ctrl = cra_readl(pcie, RP_RXCPL_STATUS); | |
154 | if (!(ctrl & RP_RXCPL_SOP)) | |
155 | continue; | |
156 | ||
157 | /* read first DW */ | |
158 | dw[count++] = cra_readl(pcie, RP_RXCPL_REG); | |
159 | ||
160 | /* Poll for EOP */ | |
161 | for (i = 0; i < TLP_LOOP; i++) { | |
162 | ctrl = cra_readl(pcie, RP_RXCPL_STATUS); | |
163 | dw[count++] = cra_readl(pcie, RP_RXCPL_REG); | |
164 | if (ctrl & RP_RXCPL_EOP) { | |
165 | comp_status = TLP_COMP_STATUS(dw[1]); | |
bf9b9813 LFT |
166 | if (comp_status) { |
167 | *value = pci_get_ff(PCI_SIZE_32); | |
168 | return 0; | |
169 | } | |
7c45862f LFT |
170 | |
171 | if (value && | |
172 | TLP_BYTE_COUNT(dw[1]) == sizeof(u32) && | |
173 | count >= 3) | |
174 | *value = dw[3]; | |
175 | ||
176 | return 0; | |
177 | } | |
178 | } | |
179 | ||
180 | udelay(5); | |
181 | } | |
182 | ||
183 | dev_err(pcie->dev, "read TLP packet timed out\n"); | |
184 | return -ENODEV; | |
185 | } | |
186 | ||
187 | static void tlp_write_packet(struct intel_fpga_pcie *pcie, u32 *headers, | |
188 | u32 data) | |
189 | { | |
190 | tlp_write_tx(pcie, headers[0], RP_TX_SOP); | |
191 | ||
192 | tlp_write_tx(pcie, headers[1], 0); | |
193 | ||
194 | tlp_write_tx(pcie, headers[2], 0); | |
195 | ||
196 | tlp_write_tx(pcie, data, RP_TX_EOP); | |
197 | } | |
198 | ||
199 | static int tlp_cfg_dword_read(struct intel_fpga_pcie *pcie, pci_dev_t bdf, | |
200 | int offset, u8 byte_en, u32 *value) | |
201 | { | |
202 | u32 headers[TLP_HDR_SIZE]; | |
203 | u8 busno = PCI_BUS(bdf); | |
204 | ||
205 | headers[0] = TLP_CFGRD_DW0(pcie, busno); | |
206 | headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); | |
207 | headers[2] = TLP_CFG_DW2(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); | |
208 | ||
209 | tlp_write_packet(pcie, headers, 0); | |
210 | ||
211 | return tlp_read_packet(pcie, value); | |
212 | } | |
213 | ||
214 | static int tlp_cfg_dword_write(struct intel_fpga_pcie *pcie, pci_dev_t bdf, | |
215 | int offset, u8 byte_en, u32 value) | |
216 | { | |
217 | u32 headers[TLP_HDR_SIZE]; | |
218 | u8 busno = PCI_BUS(bdf); | |
219 | ||
220 | headers[0] = TLP_CFGWR_DW0(pcie, busno); | |
221 | headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en); | |
222 | headers[2] = TLP_CFG_DW2(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); | |
223 | ||
224 | tlp_write_packet(pcie, headers, value); | |
225 | ||
226 | return tlp_read_packet(pcie, NULL); | |
227 | } | |
228 | ||
c4e72c4a | 229 | int intel_fpga_rp_conf_addr(const struct udevice *bus, pci_dev_t bdf, |
7c45862f LFT |
230 | uint offset, void **paddress) |
231 | { | |
232 | struct intel_fpga_pcie *pcie = dev_get_priv(bus); | |
233 | ||
234 | *paddress = RP_CFG_ADDR(pcie, offset); | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
239 | static int intel_fpga_pcie_rp_rd_conf(struct udevice *bus, pci_dev_t bdf, | |
240 | uint offset, ulong *valuep, | |
241 | enum pci_size_t size) | |
242 | { | |
243 | return pci_generic_mmap_read_config(bus, intel_fpga_rp_conf_addr, | |
244 | bdf, offset, valuep, size); | |
245 | } | |
246 | ||
247 | static int intel_fpga_pcie_rp_wr_conf(struct udevice *bus, pci_dev_t bdf, | |
248 | uint offset, ulong value, | |
249 | enum pci_size_t size) | |
250 | { | |
251 | int ret; | |
252 | struct intel_fpga_pcie *pcie = dev_get_priv(bus); | |
253 | ||
254 | ret = pci_generic_mmap_write_config(bus, intel_fpga_rp_conf_addr, | |
255 | bdf, offset, value, size); | |
256 | if (!ret) { | |
257 | /* Monitor changes to PCI_PRIMARY_BUS register on root port | |
258 | * and update local copy of root bus number accordingly. | |
259 | */ | |
260 | if (offset == PCI_PRIMARY_BUS) | |
261 | pcie->first_busno = (u8)(value); | |
262 | } | |
263 | ||
264 | return ret; | |
265 | } | |
266 | ||
267 | static u8 pcie_get_byte_en(uint offset, enum pci_size_t size) | |
268 | { | |
269 | switch (size) { | |
270 | case PCI_SIZE_8: | |
271 | return 1 << (offset & 3); | |
272 | case PCI_SIZE_16: | |
273 | return 3 << (offset & 3); | |
274 | default: | |
275 | return 0xf; | |
276 | } | |
277 | } | |
278 | ||
279 | static int _pcie_intel_fpga_read_config(struct intel_fpga_pcie *pcie, | |
280 | pci_dev_t bdf, uint offset, | |
281 | ulong *valuep, enum pci_size_t size) | |
282 | { | |
283 | int ret; | |
284 | u32 data; | |
285 | u8 byte_en; | |
286 | ||
287 | /* Uses memory mapped method to read rootport config registers */ | |
288 | if (IS_ROOT_PORT(pcie, bdf)) | |
289 | return intel_fpga_pcie_rp_rd_conf(pcie->bus, bdf, | |
290 | offset, valuep, size); | |
291 | ||
292 | byte_en = pcie_get_byte_en(offset, size); | |
293 | ret = tlp_cfg_dword_read(pcie, bdf, offset & ~DWORD_MASK, | |
294 | byte_en, &data); | |
295 | if (ret) | |
296 | return ret; | |
297 | ||
298 | dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08x)\n", | |
299 | offset, size, data); | |
300 | *valuep = pci_conv_32_to_size(data, offset, size); | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static int _pcie_intel_fpga_write_config(struct intel_fpga_pcie *pcie, | |
306 | pci_dev_t bdf, uint offset, | |
307 | ulong value, enum pci_size_t size) | |
308 | { | |
309 | u32 data; | |
310 | u8 byte_en; | |
311 | ||
312 | dev_dbg(pcie->dev, "PCIE CFG write: (b.d.f)=(%02d.%02d.%02d)\n", | |
313 | PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); | |
314 | dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08lx)\n", | |
315 | offset, size, value); | |
316 | ||
317 | /* Uses memory mapped method to read rootport config registers */ | |
318 | if (IS_ROOT_PORT(pcie, bdf)) | |
319 | return intel_fpga_pcie_rp_wr_conf(pcie->bus, bdf, offset, | |
320 | value, size); | |
321 | ||
322 | byte_en = pcie_get_byte_en(offset, size); | |
323 | data = pci_conv_size_to_32(0, value, offset, size); | |
324 | ||
325 | return tlp_cfg_dword_write(pcie, bdf, offset & ~DWORD_MASK, | |
326 | byte_en, data); | |
327 | } | |
328 | ||
c4e72c4a | 329 | static int pcie_intel_fpga_read_config(const struct udevice *bus, pci_dev_t bdf, |
7c45862f LFT |
330 | uint offset, ulong *valuep, |
331 | enum pci_size_t size) | |
332 | { | |
333 | struct intel_fpga_pcie *pcie = dev_get_priv(bus); | |
334 | ||
335 | dev_dbg(pcie->dev, "PCIE CFG read: (b.d.f)=(%02d.%02d.%02d)\n", | |
336 | PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); | |
337 | ||
338 | if (intel_fpga_pcie_hide_rc_bar(pcie, bdf, offset)) { | |
339 | *valuep = (u32)pci_get_ff(size); | |
340 | return 0; | |
341 | } | |
342 | ||
343 | if (!intel_fpga_pcie_addr_valid(pcie, bdf)) { | |
344 | *valuep = (u32)pci_get_ff(size); | |
345 | return 0; | |
346 | } | |
347 | ||
348 | return _pcie_intel_fpga_read_config(pcie, bdf, offset, valuep, size); | |
349 | } | |
350 | ||
351 | static int pcie_intel_fpga_write_config(struct udevice *bus, pci_dev_t bdf, | |
352 | uint offset, ulong value, | |
353 | enum pci_size_t size) | |
354 | { | |
355 | struct intel_fpga_pcie *pcie = dev_get_priv(bus); | |
356 | ||
357 | if (intel_fpga_pcie_hide_rc_bar(pcie, bdf, offset)) | |
358 | return 0; | |
359 | ||
360 | if (!intel_fpga_pcie_addr_valid(pcie, bdf)) | |
361 | return 0; | |
362 | ||
363 | return _pcie_intel_fpga_write_config(pcie, bdf, offset, value, | |
364 | size); | |
365 | } | |
366 | ||
367 | static int pcie_intel_fpga_probe(struct udevice *dev) | |
368 | { | |
369 | struct intel_fpga_pcie *pcie = dev_get_priv(dev); | |
370 | ||
371 | pcie->bus = pci_get_controller(dev); | |
8b85dfc6 | 372 | pcie->first_busno = dev_seq(dev); |
7c45862f LFT |
373 | |
374 | /* clear all interrupts */ | |
375 | cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS); | |
376 | /* disable all interrupts */ | |
377 | cra_writel(pcie, 0, P2A_INT_ENABLE); | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
d1998a9f | 382 | static int pcie_intel_fpga_of_to_plat(struct udevice *dev) |
7c45862f LFT |
383 | { |
384 | struct intel_fpga_pcie *pcie = dev_get_priv(dev); | |
385 | struct fdt_resource reg_res; | |
386 | int node = dev_of_offset(dev); | |
387 | int ret; | |
388 | ||
389 | DECLARE_GLOBAL_DATA_PTR; | |
390 | ||
391 | ret = fdt_get_named_resource(gd->fdt_blob, node, "reg", "reg-names", | |
392 | "Cra", ®_res); | |
393 | if (ret) { | |
394 | dev_err(dev, "resource \"Cra\" not found\n"); | |
395 | return ret; | |
396 | } | |
397 | ||
398 | pcie->cra_base = map_physmem(reg_res.start, | |
399 | fdt_resource_size(®_res), | |
400 | MAP_NOCACHE); | |
401 | ||
402 | ret = fdt_get_named_resource(gd->fdt_blob, node, "reg", "reg-names", | |
403 | "Hip", ®_res); | |
404 | if (ret) { | |
405 | dev_err(dev, "resource \"Hip\" not found\n"); | |
406 | return ret; | |
407 | } | |
408 | ||
409 | pcie->hip_base = map_physmem(reg_res.start, | |
410 | fdt_resource_size(®_res), | |
411 | MAP_NOCACHE); | |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
416 | static const struct dm_pci_ops pcie_intel_fpga_ops = { | |
417 | .read_config = pcie_intel_fpga_read_config, | |
418 | .write_config = pcie_intel_fpga_write_config, | |
419 | }; | |
420 | ||
421 | static const struct udevice_id pcie_intel_fpga_ids[] = { | |
422 | { .compatible = "altr,pcie-root-port-2.0" }, | |
423 | {}, | |
424 | }; | |
425 | ||
426 | U_BOOT_DRIVER(pcie_intel_fpga) = { | |
427 | .name = "pcie_intel_fpga", | |
428 | .id = UCLASS_PCI, | |
429 | .of_match = pcie_intel_fpga_ids, | |
430 | .ops = &pcie_intel_fpga_ops, | |
d1998a9f | 431 | .of_to_plat = pcie_intel_fpga_of_to_plat, |
7c45862f | 432 | .probe = pcie_intel_fpga_probe, |
41575d8e | 433 | .priv_auto = sizeof(struct intel_fpga_pcie), |
7c45862f | 434 | }; |