blob: e66fb1490a5abdd4e20eb5bb360aac9e5351213f [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));
76 dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
77 lower_32_bits(pci_addr));
78 dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
79 upper_32_bits(pci_addr));
80 dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
81 type);
82 dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
83 PCIE_ATU_ENABLE);
84
85 /*
86 * Make sure ATU enable takes effect before any subsequent config
87 * and I/O accesses.
88 */
89 for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
90 val = dw_pcie_readl_ob_unroll(pci, index,
91 PCIE_ATU_UNR_REGION_CTRL2);
92 if (val & PCIE_ATU_ENABLE)
93 return 0;
94
95 udelay(LINK_WAIT_IATU);
96 }
97 dev_err(pci->dev, "outbound iATU is not being enabled\n");
98
99 return -1;
100}
101
102/**
103 * set_cfg_address() - Configure the PCIe controller config space access
104 *
105 * @pcie: Pointer to the PCI controller state
106 * @d: PCI device to access
107 * @where: Offset in the configuration space
108 *
109 * Configures the PCIe controller to access the configuration space of
110 * a specific PCIe device and returns the address to use for this
111 * access.
112 *
113 * Return: Address that can be used to access the configation space
114 * of the requested device / offset
115 */
116static uintptr_t set_cfg_address(struct pcie_dw *pcie,
117 pci_dev_t d, uint where)
118{
119 int bus = PCI_BUS(d) - pcie->first_busno;
120 uintptr_t va_address;
121 u32 atu_type;
122 int ret;
123
124 /* Use dbi_base for own configuration read and write */
125 if (!bus) {
126 va_address = (uintptr_t)pcie->dbi_base;
127 goto out;
128 }
129
130 if (bus == 1)
131 /*
132 * For local bus whose primary bus number is root bridge,
133 * change TLP Type field to 4.
134 */
135 atu_type = PCIE_ATU_TYPE_CFG0;
136 else
137 /* Otherwise, change TLP Type field to 5. */
138 atu_type = PCIE_ATU_TYPE_CFG1;
139
140 /*
141 * Not accessing root port configuration space?
142 * Region #0 is used for Outbound CFG space access.
143 * Direction = Outbound
144 * Region Index = 0
145 */
146 d = PCI_MASK_BUS(d);
147 d = PCI_ADD_BUS(bus, d);
148 ret = pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
149 atu_type, (u64)pcie->cfg_base,
150 d << 8, pcie->cfg_size);
151 if (ret)
152 return (uintptr_t)ret;
153
154 va_address = (uintptr_t)pcie->cfg_base;
155
156out:
157 va_address += where & ~0x3;
158
159 return va_address;
160}
161
162/**
163 * pcie_dw_addr_valid() - Check for valid bus address
164 *
165 * @d: The PCI device to access
166 * @first_busno: Bus number of the PCIe controller root complex
167 *
168 * Return 1 (true) if the PCI device can be accessed by this controller.
169 *
170 * Return: 1 on valid, 0 on invalid
171 */
172static int pcie_dw_addr_valid(pci_dev_t d, int first_busno)
173{
174 if ((PCI_BUS(d) == first_busno) && (PCI_DEV(d) > 0))
175 return 0;
176 if ((PCI_BUS(d) == first_busno + 1) && (PCI_DEV(d) > 0))
177 return 0;
178
179 return 1;
180}
181
182/**
183 * pcie_dw_read_config() - Read from configuration space
184 *
185 * @bus: Pointer to the PCI bus
186 * @bdf: Identifies the PCIe device to access
187 * @offset: The offset into the device's configuration space
188 * @valuep: A pointer at which to store the read value
189 * @size: Indicates the size of access to perform
190 *
191 * Read a value of size @size from offset @offset within the configuration
192 * space of the device identified by the bus, device & function numbers in @bdf
193 * on the PCI bus @bus.
194 *
195 * Return: 0 on success
196 */
197int pcie_dw_read_config(const struct udevice *bus, pci_dev_t bdf,
198 uint offset, ulong *valuep,
199 enum pci_size_t size)
200{
201 struct pcie_dw *pcie = dev_get_priv(bus);
202 uintptr_t va_address;
203 ulong value;
204
205 dev_dbg(pcie->dev, "PCIE CFG read: bdf=%2x:%2x:%2x ",
206 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
207
208 if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) {
209 debug("- out of range\n");
210 *valuep = pci_get_ff(size);
211 return 0;
212 }
213
214 va_address = set_cfg_address(pcie, bdf, offset);
215
Green Wancb6506a2021-05-19 04:16:15 -0700216 value = readl((void __iomem *)va_address);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100217
218 debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);
219 *valuep = pci_conv_32_to_size(value, offset, size);
220
221 return pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
222 PCIE_ATU_TYPE_IO, pcie->io.phys_start,
223 pcie->io.bus_start, pcie->io.size);
224}
225
226/**
227 * pcie_dw_write_config() - Write to configuration space
228 *
229 * @bus: Pointer to the PCI bus
230 * @bdf: Identifies the PCIe device to access
231 * @offset: The offset into the device's configuration space
232 * @value: The value to write
233 * @size: Indicates the size of access to perform
234 *
235 * Write the value @value of size @size from offset @offset within the
236 * configuration space of the device identified by the bus, device & function
237 * numbers in @bdf on the PCI bus @bus.
238 *
239 * Return: 0 on success
240 */
241int pcie_dw_write_config(struct udevice *bus, pci_dev_t bdf,
242 uint offset, ulong value,
243 enum pci_size_t size)
244{
245 struct pcie_dw *pcie = dev_get_priv(bus);
246 uintptr_t va_address;
247 ulong old;
248
249 dev_dbg(pcie->dev, "PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ",
250 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
251 dev_dbg(pcie->dev, "(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);
252
253 if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) {
254 debug("- out of range\n");
255 return 0;
256 }
257
258 va_address = set_cfg_address(pcie, bdf, offset);
259
Green Wancb6506a2021-05-19 04:16:15 -0700260 old = readl((void __iomem *)va_address);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100261 value = pci_conv_size_to_32(old, value, offset, size);
Green Wancb6506a2021-05-19 04:16:15 -0700262 writel(value, (void __iomem *)va_address);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100263
264 return pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1,
265 PCIE_ATU_TYPE_IO, pcie->io.phys_start,
266 pcie->io.bus_start, pcie->io.size);
267}
268
269/**
270 * pcie_dw_setup_host() - Setup the PCIe controller for RC opertaion
271 *
272 * @pcie: Pointer to the PCI controller state
273 *
274 * Configure the host BARs of the PCIe controller root port so that
275 * PCI(e) devices may access the system memory.
276 */
277void pcie_dw_setup_host(struct pcie_dw *pci)
278{
279 struct udevice *ctlr = pci_get_controller(pci->dev);
280 struct pci_controller *hose = dev_get_uclass_priv(ctlr);
281 u32 ret;
282
283 if (!pci->atu_base)
284 pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
285
286 /* setup RC BARs */
287 writel(PCI_BASE_ADDRESS_MEM_TYPE_64,
288 pci->dbi_base + PCI_BASE_ADDRESS_0);
289 writel(0x0, pci->dbi_base + PCI_BASE_ADDRESS_1);
290
291 /* setup interrupt pins */
292 clrsetbits_le32(pci->dbi_base + PCI_INTERRUPT_LINE,
293 0xff00, 0x100);
294
295 /* setup bus numbers */
296 clrsetbits_le32(pci->dbi_base + PCI_PRIMARY_BUS,
297 0xffffff, 0x00ff0100);
298
299 /* setup command register */
300 clrsetbits_le32(pci->dbi_base + PCI_PRIMARY_BUS,
301 0xffff,
302 PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
303 PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
304
305 /* Enable write permission for the DBI read-only register */
306 dw_pcie_dbi_write_enable(pci, true);
307 /* program correct class for RC */
308 writew(PCI_CLASS_BRIDGE_PCI, pci->dbi_base + PCI_CLASS_DEVICE);
309 /* Better disable write permission right after the update */
310 dw_pcie_dbi_write_enable(pci, false);
311
312 setbits_le32(pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL,
313 PORT_LOGIC_SPEED_CHANGE);
314
315 for (ret = 0; ret < hose->region_count; ret++) {
316 if (hose->regions[ret].flags == PCI_REGION_IO) {
317 pci->io.phys_start = hose->regions[ret].phys_start; /* IO base */
318 pci->io.bus_start = hose->regions[ret].bus_start; /* IO_bus_addr */
319 pci->io.size = hose->regions[ret].size; /* IO size */
320 } else if (hose->regions[ret].flags == PCI_REGION_MEM) {
321 pci->mem.phys_start = hose->regions[ret].phys_start; /* MEM base */
322 pci->mem.bus_start = hose->regions[ret].bus_start; /* MEM_bus_addr */
323 pci->mem.size = hose->regions[ret].size; /* MEM size */
324 } else if (hose->regions[ret].flags == PCI_REGION_PREFETCH) {
325 pci->prefetch.phys_start = hose->regions[ret].phys_start; /* PREFETCH base */
326 pci->prefetch.bus_start = hose->regions[ret].bus_start; /* PREFETCH_bus_addr */
327 pci->prefetch.size = hose->regions[ret].size; /* PREFETCH size */
328 } else if (hose->regions[ret].flags == PCI_REGION_SYS_MEMORY) {
329 pci->cfg_base = (void *)(pci->io.phys_start - pci->io.size);
330 pci->cfg_size = pci->io.size;
331 } else {
332 dev_err(pci->dev, "invalid flags type!\n");
333 }
334 }
335
Green Wancb6506a2021-05-19 04:16:15 -0700336 dev_dbg(pci->dev, "Config space: [0x%llx - 0x%llx, size 0x%llx]\n",
337 (u64)pci->cfg_base, (u64)pci->cfg_base + pci->cfg_size,
338 (u64)pci->cfg_size);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100339
Green Wancb6506a2021-05-19 04:16:15 -0700340 dev_dbg(pci->dev, "IO space: [0x%llx - 0x%llx, size 0x%llx]\n",
341 (u64)pci->io.phys_start, (u64)pci->io.phys_start + pci->io.size,
342 (u64)pci->io.size);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100343
Green Wancb6506a2021-05-19 04:16:15 -0700344 dev_dbg(pci->dev, "IO bus: [0x%llx - 0x%llx, size 0x%llx]\n",
345 (u64)pci->io.bus_start, (u64)pci->io.bus_start + pci->io.size,
346 (u64)pci->io.size);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100347
Green Wancb6506a2021-05-19 04:16:15 -0700348 dev_dbg(pci->dev, "MEM space: [0x%llx - 0x%llx, size 0x%llx]\n",
349 (u64)pci->mem.phys_start,
350 (u64)pci->mem.phys_start + pci->mem.size,
351 (u64)pci->mem.size);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100352
Green Wancb6506a2021-05-19 04:16:15 -0700353 dev_dbg(pci->dev, "MEM bus: [0x%llx - 0x%llx, size 0x%llx]\n",
354 (u64)pci->mem.bus_start,
355 (u64)pci->mem.bus_start + pci->mem.size,
356 (u64)pci->mem.size);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100357
358 if (pci->prefetch.size) {
Green Wancb6506a2021-05-19 04:16:15 -0700359 dev_dbg(pci->dev, "PREFETCH space: [0x%llx - 0x%llx, size 0x%llx]\n",
360 (u64)pci->prefetch.phys_start,
361 (u64)pci->prefetch.phys_start + pci->prefetch.size,
362 (u64)pci->prefetch.size);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100363
Green Wancb6506a2021-05-19 04:16:15 -0700364 dev_dbg(pci->dev, "PREFETCH bus: [0x%llx - 0x%llx, size 0x%llx]\n",
365 (u64)pci->prefetch.bus_start,
366 (u64)pci->prefetch.bus_start + pci->prefetch.size,
367 (u64)pci->prefetch.size);
Neil Armstrongb46caff2021-03-25 15:49:18 +0100368 }
369}