]>
Commit | Line | Data |
---|---|---|
dfadb946 NA |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (c) 2021 BayLibre, SAS | |
4 | * Author: Neil Armstrong <[email protected]> | |
5 | * | |
6 | * Copyright (c) 2021 Rockchip, Inc. | |
7 | * | |
8 | * Copyright (C) 2018 Texas Instruments, Inc | |
9 | */ | |
10 | ||
dfadb946 NA |
11 | #include <dm.h> |
12 | #include <log.h> | |
13 | #include <pci.h> | |
14 | #include <dm/device_compat.h> | |
15 | #include <asm/io.h> | |
16 | #include <linux/delay.h> | |
17 | #include "pcie_dw_common.h" | |
18 | ||
19 | int pcie_dw_get_link_speed(struct pcie_dw *pci) | |
20 | { | |
21 | return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) & | |
22 | PCIE_LINK_STATUS_SPEED_MASK) >> PCIE_LINK_STATUS_SPEED_OFF; | |
23 | } | |
24 | ||
25 | int pcie_dw_get_link_width(struct pcie_dw *pci) | |
26 | { | |
27 | return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) & | |
28 | PCIE_LINK_STATUS_WIDTH_MASK) >> PCIE_LINK_STATUS_WIDTH_OFF; | |
29 | } | |
30 | ||
31 | static void dw_pcie_writel_ob_unroll(struct pcie_dw *pci, u32 index, u32 reg, | |
32 | u32 val) | |
33 | { | |
34 | u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); | |
35 | void __iomem *base = pci->atu_base; | |
36 | ||
37 | writel(val, base + offset + reg); | |
38 | } | |
39 | ||
40 | static u32 dw_pcie_readl_ob_unroll(struct pcie_dw *pci, u32 index, u32 reg) | |
41 | { | |
42 | u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); | |
43 | void __iomem *base = pci->atu_base; | |
44 | ||
45 | return readl(base + offset + reg); | |
46 | } | |
47 | ||
48 | /** | |
49 | * pcie_dw_prog_outbound_atu_unroll() - Configure ATU for outbound accesses | |
50 | * | |
51 | * @pcie: Pointer to the PCI controller state | |
52 | * @index: ATU region index | |
53 | * @type: ATU accsess type | |
54 | * @cpu_addr: the physical address for the translation entry | |
55 | * @pci_addr: the pcie bus address for the translation entry | |
56 | * @size: the size of the translation entry | |
57 | * | |
58 | * Return: 0 is successful and -1 is failure | |
59 | */ | |
60 | int pcie_dw_prog_outbound_atu_unroll(struct pcie_dw *pci, int index, | |
61 | int type, u64 cpu_addr, | |
62 | u64 pci_addr, u32 size) | |
63 | { | |
64 | u32 retries, val; | |
65 | ||
66 | dev_dbg(pci->dev, "ATU programmed with: index: %d, type: %d, cpu addr: %8llx, pci addr: %8llx, size: %8x\n", | |
67 | index, type, cpu_addr, pci_addr, size); | |
68 | ||
69 | dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, | |
70 | lower_32_bits(cpu_addr)); | |
71 | dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, | |
72 | upper_32_bits(cpu_addr)); | |
73 | dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT, | |
74 | lower_32_bits(cpu_addr + size - 1)); | |
4c56d751 BD |
75 | dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_LIMIT, |
76 | upper_32_bits(cpu_addr + size - 1)); | |
dfadb946 NA |
77 | dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, |
78 | lower_32_bits(pci_addr)); | |
79 | dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, | |
80 | upper_32_bits(pci_addr)); | |
81 | dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, | |
82 | type); | |
83 | dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, | |
84 | PCIE_ATU_ENABLE); | |
85 | ||
86 | /* | |
87 | * Make sure ATU enable takes effect before any subsequent config | |
88 | * and I/O accesses. | |
89 | */ | |
90 | for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { | |
91 | val = dw_pcie_readl_ob_unroll(pci, index, | |
92 | PCIE_ATU_UNR_REGION_CTRL2); | |
93 | if (val & PCIE_ATU_ENABLE) | |
94 | return 0; | |
95 | ||
96 | udelay(LINK_WAIT_IATU); | |
97 | } | |
98 | dev_err(pci->dev, "outbound iATU is not being enabled\n"); | |
99 | ||
100 | return -1; | |
101 | } | |
102 | ||
103 | /** | |
104 | * set_cfg_address() - Configure the PCIe controller config space access | |
105 | * | |
106 | * @pcie: Pointer to the PCI controller state | |
107 | * @d: PCI device to access | |
108 | * @where: Offset in the configuration space | |
109 | * | |
110 | * Configures the PCIe controller to access the configuration space of | |
111 | * a specific PCIe device and returns the address to use for this | |
112 | * access. | |
113 | * | |
114 | * Return: Address that can be used to access the configation space | |
115 | * of the requested device / offset | |
116 | */ | |
117 | static uintptr_t set_cfg_address(struct pcie_dw *pcie, | |
118 | pci_dev_t d, uint where) | |
119 | { | |
120 | int bus = PCI_BUS(d) - pcie->first_busno; | |
121 | uintptr_t va_address; | |
122 | u32 atu_type; | |
123 | int ret; | |
124 | ||
125 | /* Use dbi_base for own configuration read and write */ | |
126 | if (!bus) { | |
127 | va_address = (uintptr_t)pcie->dbi_base; | |
128 | goto out; | |
129 | } | |
130 | ||
131 | if (bus == 1) | |
132 | /* | |
133 | * For local bus whose primary bus number is root bridge, | |
134 | * change TLP Type field to 4. | |
135 | */ | |
136 | atu_type = PCIE_ATU_TYPE_CFG0; | |
137 | else | |
138 | /* Otherwise, change TLP Type field to 5. */ | |
139 | atu_type = PCIE_ATU_TYPE_CFG1; | |
140 | ||
141 | /* | |
142 | * Not accessing root port configuration space? | |
bed7b2f0 | 143 | * Region #1 is used for Outbound CFG space access. |
dfadb946 | 144 | * Direction = Outbound |
bed7b2f0 | 145 | * Region Index = 1 |
dfadb946 NA |
146 | */ |
147 | d = PCI_MASK_BUS(d); | |
148 | d = PCI_ADD_BUS(bus, d); | |
149 | ret = pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, | |
150 | atu_type, (u64)pcie->cfg_base, | |
151 | d << 8, pcie->cfg_size); | |
152 | if (ret) | |
153 | return (uintptr_t)ret; | |
154 | ||
155 | va_address = (uintptr_t)pcie->cfg_base; | |
156 | ||
157 | out: | |
158 | va_address += where & ~0x3; | |
159 | ||
160 | return va_address; | |
161 | } | |
162 | ||
163 | /** | |
164 | * pcie_dw_addr_valid() - Check for valid bus address | |
165 | * | |
166 | * @d: The PCI device to access | |
167 | * @first_busno: Bus number of the PCIe controller root complex | |
168 | * | |
169 | * Return 1 (true) if the PCI device can be accessed by this controller. | |
170 | * | |
171 | * Return: 1 on valid, 0 on invalid | |
172 | */ | |
173 | static int pcie_dw_addr_valid(pci_dev_t d, int first_busno) | |
174 | { | |
175 | if ((PCI_BUS(d) == first_busno) && (PCI_DEV(d) > 0)) | |
176 | return 0; | |
177 | if ((PCI_BUS(d) == first_busno + 1) && (PCI_DEV(d) > 0)) | |
178 | return 0; | |
179 | ||
180 | return 1; | |
181 | } | |
182 | ||
183 | /** | |
184 | * pcie_dw_read_config() - Read from configuration space | |
185 | * | |
186 | * @bus: Pointer to the PCI bus | |
187 | * @bdf: Identifies the PCIe device to access | |
188 | * @offset: The offset into the device's configuration space | |
189 | * @valuep: A pointer at which to store the read value | |
190 | * @size: Indicates the size of access to perform | |
191 | * | |
192 | * Read a value of size @size from offset @offset within the configuration | |
193 | * space of the device identified by the bus, device & function numbers in @bdf | |
194 | * on the PCI bus @bus. | |
195 | * | |
196 | * Return: 0 on success | |
197 | */ | |
198 | int pcie_dw_read_config(const struct udevice *bus, pci_dev_t bdf, | |
199 | uint offset, ulong *valuep, | |
200 | enum pci_size_t size) | |
201 | { | |
202 | struct pcie_dw *pcie = dev_get_priv(bus); | |
203 | uintptr_t va_address; | |
204 | ulong value; | |
205 | ||
206 | dev_dbg(pcie->dev, "PCIE CFG read: bdf=%2x:%2x:%2x ", | |
207 | PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); | |
208 | ||
209 | if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) { | |
210 | debug("- out of range\n"); | |
211 | *valuep = pci_get_ff(size); | |
212 | return 0; | |
213 | } | |
214 | ||
215 | va_address = set_cfg_address(pcie, bdf, offset); | |
216 | ||
d7da718b | 217 | value = readl((void __iomem *)va_address); |
dfadb946 NA |
218 | |
219 | debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); | |
220 | *valuep = pci_conv_32_to_size(value, offset, size); | |
221 | ||
222 | return pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, | |
223 | PCIE_ATU_TYPE_IO, pcie->io.phys_start, | |
224 | pcie->io.bus_start, pcie->io.size); | |
225 | } | |
226 | ||
227 | /** | |
228 | * pcie_dw_write_config() - Write to configuration space | |
229 | * | |
230 | * @bus: Pointer to the PCI bus | |
231 | * @bdf: Identifies the PCIe device to access | |
232 | * @offset: The offset into the device's configuration space | |
233 | * @value: The value to write | |
234 | * @size: Indicates the size of access to perform | |
235 | * | |
236 | * Write the value @value of size @size from offset @offset within the | |
237 | * configuration space of the device identified by the bus, device & function | |
238 | * numbers in @bdf on the PCI bus @bus. | |
239 | * | |
240 | * Return: 0 on success | |
241 | */ | |
242 | int pcie_dw_write_config(struct udevice *bus, pci_dev_t bdf, | |
243 | uint offset, ulong value, | |
244 | enum pci_size_t size) | |
245 | { | |
246 | struct pcie_dw *pcie = dev_get_priv(bus); | |
247 | uintptr_t va_address; | |
248 | ulong old; | |
249 | ||
250 | dev_dbg(pcie->dev, "PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ", | |
251 | PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); | |
252 | dev_dbg(pcie->dev, "(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); | |
253 | ||
254 | if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) { | |
255 | debug("- out of range\n"); | |
256 | return 0; | |
257 | } | |
258 | ||
259 | va_address = set_cfg_address(pcie, bdf, offset); | |
260 | ||
d7da718b | 261 | old = readl((void __iomem *)va_address); |
dfadb946 | 262 | value = pci_conv_size_to_32(old, value, offset, size); |
d7da718b | 263 | writel(value, (void __iomem *)va_address); |
dfadb946 NA |
264 | |
265 | return pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, | |
266 | PCIE_ATU_TYPE_IO, pcie->io.phys_start, | |
267 | pcie->io.bus_start, pcie->io.size); | |
268 | } | |
269 | ||
270 | /** | |
271 | * pcie_dw_setup_host() - Setup the PCIe controller for RC opertaion | |
272 | * | |
273 | * @pcie: Pointer to the PCI controller state | |
274 | * | |
275 | * Configure the host BARs of the PCIe controller root port so that | |
276 | * PCI(e) devices may access the system memory. | |
277 | */ | |
278 | void pcie_dw_setup_host(struct pcie_dw *pci) | |
279 | { | |
280 | struct udevice *ctlr = pci_get_controller(pci->dev); | |
281 | struct pci_controller *hose = dev_get_uclass_priv(ctlr); | |
282 | u32 ret; | |
283 | ||
284 | if (!pci->atu_base) | |
285 | pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; | |
286 | ||
287 | /* setup RC BARs */ | |
288 | writel(PCI_BASE_ADDRESS_MEM_TYPE_64, | |
289 | pci->dbi_base + PCI_BASE_ADDRESS_0); | |
290 | writel(0x0, pci->dbi_base + PCI_BASE_ADDRESS_1); | |
291 | ||
292 | /* setup interrupt pins */ | |
293 | clrsetbits_le32(pci->dbi_base + PCI_INTERRUPT_LINE, | |
294 | 0xff00, 0x100); | |
295 | ||
296 | /* setup bus numbers */ | |
297 | clrsetbits_le32(pci->dbi_base + PCI_PRIMARY_BUS, | |
298 | 0xffffff, 0x00ff0100); | |
299 | ||
300 | /* setup command register */ | |
301 | clrsetbits_le32(pci->dbi_base + PCI_PRIMARY_BUS, | |
302 | 0xffff, | |
303 | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | | |
304 | PCI_COMMAND_MASTER | PCI_COMMAND_SERR); | |
305 | ||
306 | /* Enable write permission for the DBI read-only register */ | |
307 | dw_pcie_dbi_write_enable(pci, true); | |
308 | /* program correct class for RC */ | |
309 | writew(PCI_CLASS_BRIDGE_PCI, pci->dbi_base + PCI_CLASS_DEVICE); | |
310 | /* Better disable write permission right after the update */ | |
311 | dw_pcie_dbi_write_enable(pci, false); | |
312 | ||
313 | setbits_le32(pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL, | |
314 | PORT_LOGIC_SPEED_CHANGE); | |
315 | ||
316 | for (ret = 0; ret < hose->region_count; ret++) { | |
317 | if (hose->regions[ret].flags == PCI_REGION_IO) { | |
318 | pci->io.phys_start = hose->regions[ret].phys_start; /* IO base */ | |
319 | pci->io.bus_start = hose->regions[ret].bus_start; /* IO_bus_addr */ | |
320 | pci->io.size = hose->regions[ret].size; /* IO size */ | |
321 | } else if (hose->regions[ret].flags == PCI_REGION_MEM) { | |
322 | pci->mem.phys_start = hose->regions[ret].phys_start; /* MEM base */ | |
323 | pci->mem.bus_start = hose->regions[ret].bus_start; /* MEM_bus_addr */ | |
324 | pci->mem.size = hose->regions[ret].size; /* MEM size */ | |
325 | } else if (hose->regions[ret].flags == PCI_REGION_PREFETCH) { | |
326 | pci->prefetch.phys_start = hose->regions[ret].phys_start; /* PREFETCH base */ | |
327 | pci->prefetch.bus_start = hose->regions[ret].bus_start; /* PREFETCH_bus_addr */ | |
328 | pci->prefetch.size = hose->regions[ret].size; /* PREFETCH size */ | |
329 | } else if (hose->regions[ret].flags == PCI_REGION_SYS_MEMORY) { | |
bed7b2f0 JK |
330 | if (!pci->cfg_base) { |
331 | pci->cfg_base = (void *)(pci->io.phys_start - pci->io.size); | |
332 | pci->cfg_size = pci->io.size; | |
333 | } | |
dfadb946 NA |
334 | } else { |
335 | dev_err(pci->dev, "invalid flags type!\n"); | |
336 | } | |
337 | } | |
338 | ||
d7da718b GW |
339 | dev_dbg(pci->dev, "Config space: [0x%llx - 0x%llx, size 0x%llx]\n", |
340 | (u64)pci->cfg_base, (u64)pci->cfg_base + pci->cfg_size, | |
341 | (u64)pci->cfg_size); | |
dfadb946 | 342 | |
d7da718b GW |
343 | dev_dbg(pci->dev, "IO space: [0x%llx - 0x%llx, size 0x%llx]\n", |
344 | (u64)pci->io.phys_start, (u64)pci->io.phys_start + pci->io.size, | |
345 | (u64)pci->io.size); | |
dfadb946 | 346 | |
d7da718b GW |
347 | dev_dbg(pci->dev, "IO bus: [0x%llx - 0x%llx, size 0x%llx]\n", |
348 | (u64)pci->io.bus_start, (u64)pci->io.bus_start + pci->io.size, | |
349 | (u64)pci->io.size); | |
dfadb946 | 350 | |
d7da718b GW |
351 | dev_dbg(pci->dev, "MEM space: [0x%llx - 0x%llx, size 0x%llx]\n", |
352 | (u64)pci->mem.phys_start, | |
353 | (u64)pci->mem.phys_start + pci->mem.size, | |
354 | (u64)pci->mem.size); | |
dfadb946 | 355 | |
d7da718b GW |
356 | dev_dbg(pci->dev, "MEM bus: [0x%llx - 0x%llx, size 0x%llx]\n", |
357 | (u64)pci->mem.bus_start, | |
358 | (u64)pci->mem.bus_start + pci->mem.size, | |
359 | (u64)pci->mem.size); | |
dfadb946 NA |
360 | |
361 | if (pci->prefetch.size) { | |
d7da718b GW |
362 | dev_dbg(pci->dev, "PREFETCH space: [0x%llx - 0x%llx, size 0x%llx]\n", |
363 | (u64)pci->prefetch.phys_start, | |
364 | (u64)pci->prefetch.phys_start + pci->prefetch.size, | |
365 | (u64)pci->prefetch.size); | |
366 | ||
367 | dev_dbg(pci->dev, "PREFETCH bus: [0x%llx - 0x%llx, size 0x%llx]\n", | |
368 | (u64)pci->prefetch.bus_start, | |
369 | (u64)pci->prefetch.bus_start + pci->prefetch.size, | |
370 | (u64)pci->prefetch.size); | |
dfadb946 NA |
371 | } |
372 | } |