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