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