Merge tag 'u-boot-imx-20200825' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
[platform/kernel/u-boot.git] / drivers / pci / pci_octeontx.c
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
15 #include <asm/io.h>
16
17 #include <linux/ioport.h>
18
19 DECLARE_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  */
32 enum {
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  */
44 struct octeontx_pci {
45         unsigned int type;
46
47         struct resource cfg;
48         struct resource bus;
49 };
50
51 static 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
70 static 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
92 static 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
109 static 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
117 static 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
135 static 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
153 static 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
184 static 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
216 static 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
241 static 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
264 int 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
289 int 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
314 static int pci_octeontx_ofdata_to_platdata(struct udevice *dev)
315 {
316         return 0;
317 }
318
319 static 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
341 static const struct dm_pci_ops pci_octeontx_ops = {
342         .read_config    = pci_octeontx_read_config,
343         .write_config   = pci_octeontx_write_config,
344 };
345
346 static 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
355 U_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,
362         .priv_auto_alloc_size = sizeof(struct octeontx_pci),
363         .flags = DM_FLAG_PRE_RELOC,
364 };