blob: d211d22226eb14b6a543ab5a97fec4d0fbf2c776 [file] [log] [blame]
Suneel Garapati638d7052019-10-19 17:28:01 -07001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018 Marvell International Ltd.
4 *
5 * https://spdx.org/licenses
6 */
7
8#include <dm.h>
9#include <errno.h>
10#include <fdtdec.h>
11#include <log.h>
12#include <malloc.h>
13#include <pci.h>
14
15#include <asm/io.h>
16
17#include <linux/ioport.h>
18
19DECLARE_GLOBAL_DATA_PTR;
20
21/*
22 * This driver supports multiple types of operations / host bridges / busses:
23 *
24 * OTX_ECAM: Octeon TX & TX2 ECAM (Enhanced Configuration Access Mechanism)
25 * Used to access the internal on-chip devices which are connected
26 * to internal buses
27 * OTX_PEM: Octeon TX PEM (PCI Express MAC)
28 * Used to access the external (off-chip) PCI devices
29 * OTX2_PEM: Octeon TX2 PEM (PCI Express MAC)
30 * Used to access the external (off-chip) PCI devices
31 */
32enum {
33 OTX_ECAM,
34 OTX_PEM,
35 OTX2_PEM,
36};
37
38/**
39 * struct octeontx_pci - Driver private data
40 * @type: Device type matched via compatible (e.g. OTX_ECAM etc)
41 * @cfg: Config resource
42 * @bus: Bus resource
43 */
44struct octeontx_pci {
45 unsigned int type;
46
47 struct resource cfg;
48 struct resource bus;
49};
50
51static uintptr_t octeontx_cfg_addr(struct octeontx_pci *pcie,
52 int bus_offs, int shift_offs,
53 pci_dev_t bdf, uint offset)
54{
55 u32 bus, dev, func;
56 uintptr_t address;
57
58 bus = PCI_BUS(bdf) + bus_offs;
59 dev = PCI_DEV(bdf);
60 func = PCI_FUNC(bdf);
61
62 address = (bus << (20 + shift_offs)) |
63 (dev << (15 + shift_offs)) |
64 (func << (12 + shift_offs)) | offset;
65 address += pcie->cfg.start;
66
67 return address;
68}
69
70static ulong readl_size(uintptr_t addr, enum pci_size_t size)
71{
72 ulong val;
73
74 switch (size) {
75 case PCI_SIZE_8:
76 val = readb(addr);
77 break;
78 case PCI_SIZE_16:
79 val = readw(addr);
80 break;
81 case PCI_SIZE_32:
82 val = readl(addr);
83 break;
84 default:
85 printf("Invalid size\n");
86 return -EINVAL;
87 };
88
89 return val;
90}
91
92static void writel_size(uintptr_t addr, enum pci_size_t size, ulong valuep)
93{
94 switch (size) {
95 case PCI_SIZE_8:
96 writeb(valuep, addr);
97 break;
98 case PCI_SIZE_16:
99 writew(valuep, addr);
100 break;
101 case PCI_SIZE_32:
102 writel(valuep, addr);
103 break;
104 default:
105 printf("Invalid size\n");
106 };
107}
108
109static bool octeontx_bdf_invalid(pci_dev_t bdf)
110{
111 if (PCI_BUS(bdf) == 1 && PCI_DEV(bdf) > 0)
112 return true;
113
114 return false;
115}
116
117static int octeontx_ecam_read_config(const struct udevice *bus, pci_dev_t bdf,
118 uint offset, ulong *valuep,
119 enum pci_size_t size)
120{
121 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
122 struct pci_controller *hose = dev_get_uclass_priv(bus);
123 uintptr_t address;
124
125 address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
126 0, bdf, offset);
127 *valuep = readl_size(address, size);
128
129 debug("%02x.%02x.%02x: u%d %x -> %lx\n",
130 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep);
131
132 return 0;
133}
134
135static int octeontx_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
136 uint offset, ulong value,
137 enum pci_size_t size)
138{
139 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
140 struct pci_controller *hose = dev_get_uclass_priv(bus);
141 uintptr_t address;
142
143 address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
144 0, bdf, offset);
145 writel_size(address, size, value);
146
147 debug("%02x.%02x.%02x: u%d %x <- %lx\n",
148 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, value);
149
150 return 0;
151}
152
153static int octeontx_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
154 uint offset, ulong *valuep,
155 enum pci_size_t size)
156{
157 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
158 struct pci_controller *hose = dev_get_uclass_priv(bus);
159 uintptr_t address;
160 u8 hdrtype;
161 u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
162 u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
163
164 address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4,
165 bdf, 0);
166
167 *valuep = pci_conv_32_to_size(~0UL, offset, size);
168
169 if (octeontx_bdf_invalid(bdf))
170 return -EPERM;
171
172 *valuep = readl_size(address + offset, size);
173
174 hdrtype = readb(address + PCI_HEADER_TYPE);
175 if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
176 offset >= PCI_PRIMARY_BUS &&
177 offset <= PCI_SUBORDINATE_BUS &&
178 *valuep != pci_conv_32_to_size(~0UL, offset, size))
179 *valuep -= pci_conv_32_to_size(bus_offs, offset, size);
180
181 return 0;
182}
183
184static int octeontx_pem_write_config(struct udevice *bus, pci_dev_t bdf,
185 uint offset, ulong value,
186 enum pci_size_t size)
187{
188 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
189 struct pci_controller *hose = dev_get_uclass_priv(bus);
190 uintptr_t address;
191 u8 hdrtype;
192 u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
193 u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
194
195 address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4, bdf, 0);
196
197 hdrtype = readb(address + PCI_HEADER_TYPE);
198 if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
199 offset >= PCI_PRIMARY_BUS &&
200 offset <= PCI_SUBORDINATE_BUS &&
201 value != pci_conv_32_to_size(~0UL, offset, size))
202 value += pci_conv_32_to_size(bus_offs, offset, size);
203
204 if (octeontx_bdf_invalid(bdf))
205 return -EPERM;
206
207 writel_size(address + offset, size, value);
208
209 debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
210 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
211 address, value);
212
213 return 0;
214}
215
216static int octeontx2_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
217 uint offset, ulong *valuep,
218 enum pci_size_t size)
219{
220 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
221 struct pci_controller *hose = dev_get_uclass_priv(bus);
222 uintptr_t address;
223
224 address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
225 bdf, 0);
226
227 *valuep = pci_conv_32_to_size(~0UL, offset, size);
228
229 if (octeontx_bdf_invalid(bdf))
230 return -EPERM;
231
232 *valuep = readl_size(address + offset, size);
233
234 debug("%02x.%02x.%02x: u%d %x (%lx) -> %lx\n",
235 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
236 address, *valuep);
237
238 return 0;
239}
240
241static int octeontx2_pem_write_config(struct udevice *bus, pci_dev_t bdf,
242 uint offset, ulong value,
243 enum pci_size_t size)
244{
245 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
246 struct pci_controller *hose = dev_get_uclass_priv(bus);
247 uintptr_t address;
248
249 address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
250 bdf, 0);
251
252 if (octeontx_bdf_invalid(bdf))
253 return -EPERM;
254
255 writel_size(address + offset, size, value);
256
257 debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
258 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
259 address, value);
260
261 return 0;
262}
263
264int pci_octeontx_read_config(const struct udevice *bus, pci_dev_t bdf,
265 uint offset, ulong *valuep,
266 enum pci_size_t size)
267{
268 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
269 int ret = -EIO;
270
271 switch (pcie->type) {
272 case OTX_ECAM:
273 ret = octeontx_ecam_read_config(bus, bdf, offset, valuep,
274 size);
275 break;
276 case OTX_PEM:
277 ret = octeontx_pem_read_config(bus, bdf, offset, valuep,
278 size);
279 break;
280 case OTX2_PEM:
281 ret = octeontx2_pem_read_config(bus, bdf, offset, valuep,
282 size);
283 break;
284 }
285
286 return ret;
287}
288
289int pci_octeontx_write_config(struct udevice *bus, pci_dev_t bdf,
290 uint offset, ulong value,
291 enum pci_size_t size)
292{
293 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
294 int ret = -EIO;
295
296 switch (pcie->type) {
297 case OTX_ECAM:
298 ret = octeontx_ecam_write_config(bus, bdf, offset, value,
299 size);
300 break;
301 case OTX_PEM:
302 ret = octeontx_pem_write_config(bus, bdf, offset, value,
303 size);
304 break;
305 case OTX2_PEM:
306 ret = octeontx2_pem_write_config(bus, bdf, offset, value,
307 size);
308 break;
309 }
310
311 return ret;
312}
313
314static int pci_octeontx_ofdata_to_platdata(struct udevice *dev)
315{
316 return 0;
317}
318
319static int pci_octeontx_probe(struct udevice *dev)
320{
321 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(dev);
322 int err;
323
324 pcie->type = dev_get_driver_data(dev);
325
326 err = dev_read_resource(dev, 0, &pcie->cfg);
327 if (err) {
328 debug("Error reading resource: %s\n", fdt_strerror(err));
329 return err;
330 }
331
332 err = dev_read_pci_bus_range(dev, &pcie->bus);
333 if (err) {
334 debug("Error reading resource: %s\n", fdt_strerror(err));
335 return err;
336 }
337
338 return 0;
339}
340
341static const struct dm_pci_ops pci_octeontx_ops = {
342 .read_config = pci_octeontx_read_config,
343 .write_config = pci_octeontx_write_config,
344};
345
346static const struct udevice_id pci_octeontx_ids[] = {
347 { .compatible = "cavium,pci-host-thunder-ecam", .data = OTX_ECAM },
348 { .compatible = "cavium,pci-host-octeontx-ecam", .data = OTX_ECAM },
349 { .compatible = "pci-host-ecam-generic", .data = OTX_ECAM },
350 { .compatible = "cavium,pci-host-thunder-pem", .data = OTX_PEM },
351 { .compatible = "marvell,pci-host-octeontx2-pem", .data = OTX2_PEM },
352 { }
353};
354
355U_BOOT_DRIVER(pci_octeontx) = {
356 .name = "pci_octeontx",
357 .id = UCLASS_PCI,
358 .of_match = pci_octeontx_ids,
359 .ops = &pci_octeontx_ops,
360 .ofdata_to_platdata = pci_octeontx_ofdata_to_platdata,
361 .probe = pci_octeontx_probe,
Simon Glass41575d82020-12-03 16:55:17 -0700362 .priv_auto = sizeof(struct octeontx_pci),
Suneel Garapati638d7052019-10-19 17:28:01 -0700363 .flags = DM_FLAG_PRE_RELOC,
364};