blob: 875cf7f7115d5a1a3ae5cd1a292e7fff518091bb [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>
Simon Glass401d1c42020-10-30 21:38:53 -060014#include <asm/global_data.h>
Suneel Garapati638d7052019-10-19 17:28:01 -070015
16#include <asm/io.h>
17
18#include <linux/ioport.h>
19
20DECLARE_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 */
33enum {
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 */
45struct octeontx_pci {
46 unsigned int type;
47
48 struct resource cfg;
49 struct resource bus;
50};
51
Suneel Garapati638d7052019-10-19 17:28:01 -070052static ulong readl_size(uintptr_t addr, enum pci_size_t size)
53{
54 ulong val;
55
56 switch (size) {
57 case PCI_SIZE_8:
58 val = readb(addr);
59 break;
60 case PCI_SIZE_16:
61 val = readw(addr);
62 break;
63 case PCI_SIZE_32:
64 val = readl(addr);
65 break;
66 default:
67 printf("Invalid size\n");
68 return -EINVAL;
69 };
70
71 return val;
72}
73
74static void writel_size(uintptr_t addr, enum pci_size_t size, ulong valuep)
75{
76 switch (size) {
77 case PCI_SIZE_8:
78 writeb(valuep, addr);
79 break;
80 case PCI_SIZE_16:
81 writew(valuep, addr);
82 break;
83 case PCI_SIZE_32:
84 writel(valuep, addr);
85 break;
86 default:
87 printf("Invalid size\n");
88 };
89}
90
91static bool octeontx_bdf_invalid(pci_dev_t bdf)
92{
93 if (PCI_BUS(bdf) == 1 && PCI_DEV(bdf) > 0)
94 return true;
95
96 return false;
97}
98
99static int octeontx_ecam_read_config(const struct udevice *bus, pci_dev_t bdf,
100 uint offset, ulong *valuep,
101 enum pci_size_t size)
102{
103 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
104 struct pci_controller *hose = dev_get_uclass_priv(bus);
105 uintptr_t address;
106
Pali Rohárbf667d52021-11-24 18:00:33 +0100107 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + pcie->bus.start - hose->first_busno,
108 PCI_DEV(bdf), PCI_FUNC(bdf), offset);
109 *valuep = readl_size(pcie->cfg.start + address, size);
Suneel Garapati638d7052019-10-19 17:28:01 -0700110
111 debug("%02x.%02x.%02x: u%d %x -> %lx\n",
112 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep);
113
114 return 0;
115}
116
117static int octeontx_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
118 uint offset, ulong value,
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
Pali Rohárbf667d52021-11-24 18:00:33 +0100125 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + pcie->bus.start - hose->first_busno,
126 PCI_DEV(bdf), PCI_FUNC(bdf), offset);
127 writel_size(pcie->cfg.start + address, size, value);
Suneel Garapati638d7052019-10-19 17:28:01 -0700128
129 debug("%02x.%02x.%02x: u%d %x <- %lx\n",
130 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, value);
131
132 return 0;
133}
134
135static int octeontx_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
136 uint offset, ulong *valuep,
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 u8 hdrtype;
143 u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
144 u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
145
Suneel Garapati638d7052019-10-19 17:28:01 -0700146 *valuep = pci_conv_32_to_size(~0UL, offset, size);
147
148 if (octeontx_bdf_invalid(bdf))
149 return -EPERM;
150
Pali Rohárbf667d52021-11-24 18:00:33 +0100151 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno,
152 PCI_DEV(bdf), PCI_FUNC(bdf), 0) << 4;
153 *valuep = readl_size(pcie->cfg.start + address + offset, size);
Suneel Garapati638d7052019-10-19 17:28:01 -0700154
Pali Rohárbf667d52021-11-24 18:00:33 +0100155 hdrtype = readb(pcie->cfg.start + address + PCI_HEADER_TYPE);
Suneel Garapati638d7052019-10-19 17:28:01 -0700156 if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
157 offset >= PCI_PRIMARY_BUS &&
158 offset <= PCI_SUBORDINATE_BUS &&
159 *valuep != pci_conv_32_to_size(~0UL, offset, size))
160 *valuep -= pci_conv_32_to_size(bus_offs, offset, size);
161
162 return 0;
163}
164
165static int octeontx_pem_write_config(struct udevice *bus, pci_dev_t bdf,
166 uint offset, ulong value,
167 enum pci_size_t size)
168{
169 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
170 struct pci_controller *hose = dev_get_uclass_priv(bus);
171 uintptr_t address;
172 u8 hdrtype;
173 u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
174 u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
175
Pali Rohárbf667d52021-11-24 18:00:33 +0100176 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno,
177 PCI_DEV(bdf), PCI_FUNC(bdf), 0) << 4;
Suneel Garapati638d7052019-10-19 17:28:01 -0700178
Pali Rohárbf667d52021-11-24 18:00:33 +0100179 hdrtype = readb(pcie->cfg.start + address + PCI_HEADER_TYPE);
Suneel Garapati638d7052019-10-19 17:28:01 -0700180 if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
181 offset >= PCI_PRIMARY_BUS &&
182 offset <= PCI_SUBORDINATE_BUS &&
183 value != pci_conv_32_to_size(~0UL, offset, size))
184 value += pci_conv_32_to_size(bus_offs, offset, size);
185
186 if (octeontx_bdf_invalid(bdf))
187 return -EPERM;
188
Pali Rohárbf667d52021-11-24 18:00:33 +0100189 writel_size(pcie->cfg.start + address + offset, size, value);
Suneel Garapati638d7052019-10-19 17:28:01 -0700190
191 debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
192 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
193 address, value);
194
195 return 0;
196}
197
198static int octeontx2_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
199 uint offset, ulong *valuep,
200 enum pci_size_t size)
201{
202 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
203 struct pci_controller *hose = dev_get_uclass_priv(bus);
204 uintptr_t address;
205
Suneel Garapati638d7052019-10-19 17:28:01 -0700206 *valuep = pci_conv_32_to_size(~0UL, offset, size);
207
208 if (octeontx_bdf_invalid(bdf))
209 return -EPERM;
210
Pali Rohárbf667d52021-11-24 18:00:33 +0100211 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno,
212 PCI_DEV(bdf), PCI_FUNC(bdf), offset);
213 *valuep = readl_size(pcie->cfg.start + address, size);
Suneel Garapati638d7052019-10-19 17:28:01 -0700214
215 debug("%02x.%02x.%02x: u%d %x (%lx) -> %lx\n",
216 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
217 address, *valuep);
218
219 return 0;
220}
221
222static int octeontx2_pem_write_config(struct udevice *bus, pci_dev_t bdf,
223 uint offset, ulong value,
224 enum pci_size_t size)
225{
226 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
227 struct pci_controller *hose = dev_get_uclass_priv(bus);
228 uintptr_t address;
229
Suneel Garapati638d7052019-10-19 17:28:01 -0700230 if (octeontx_bdf_invalid(bdf))
231 return -EPERM;
232
Pali Rohárbf667d52021-11-24 18:00:33 +0100233 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno,
234 PCI_DEV(bdf), PCI_FUNC(bdf), offset);
235 writel_size(pcie->cfg.start + address, size, value);
Suneel Garapati638d7052019-10-19 17:28:01 -0700236
237 debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
238 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
239 address, value);
240
241 return 0;
242}
243
244int pci_octeontx_read_config(const struct udevice *bus, pci_dev_t bdf,
245 uint offset, ulong *valuep,
246 enum pci_size_t size)
247{
248 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
249 int ret = -EIO;
250
251 switch (pcie->type) {
252 case OTX_ECAM:
253 ret = octeontx_ecam_read_config(bus, bdf, offset, valuep,
254 size);
255 break;
256 case OTX_PEM:
257 ret = octeontx_pem_read_config(bus, bdf, offset, valuep,
258 size);
259 break;
260 case OTX2_PEM:
261 ret = octeontx2_pem_read_config(bus, bdf, offset, valuep,
262 size);
263 break;
264 }
265
266 return ret;
267}
268
269int pci_octeontx_write_config(struct udevice *bus, pci_dev_t bdf,
270 uint offset, ulong value,
271 enum pci_size_t size)
272{
273 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
274 int ret = -EIO;
275
276 switch (pcie->type) {
277 case OTX_ECAM:
278 ret = octeontx_ecam_write_config(bus, bdf, offset, value,
279 size);
280 break;
281 case OTX_PEM:
282 ret = octeontx_pem_write_config(bus, bdf, offset, value,
283 size);
284 break;
285 case OTX2_PEM:
286 ret = octeontx2_pem_write_config(bus, bdf, offset, value,
287 size);
288 break;
289 }
290
291 return ret;
292}
293
Simon Glassd1998a92020-12-03 16:55:21 -0700294static int pci_octeontx_of_to_plat(struct udevice *dev)
Suneel Garapati638d7052019-10-19 17:28:01 -0700295{
296 return 0;
297}
298
299static int pci_octeontx_probe(struct udevice *dev)
300{
301 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(dev);
302 int err;
303
304 pcie->type = dev_get_driver_data(dev);
305
306 err = dev_read_resource(dev, 0, &pcie->cfg);
307 if (err) {
308 debug("Error reading resource: %s\n", fdt_strerror(err));
309 return err;
310 }
311
312 err = dev_read_pci_bus_range(dev, &pcie->bus);
313 if (err) {
314 debug("Error reading resource: %s\n", fdt_strerror(err));
315 return err;
316 }
317
318 return 0;
319}
320
321static const struct dm_pci_ops pci_octeontx_ops = {
322 .read_config = pci_octeontx_read_config,
323 .write_config = pci_octeontx_write_config,
324};
325
326static const struct udevice_id pci_octeontx_ids[] = {
327 { .compatible = "cavium,pci-host-thunder-ecam", .data = OTX_ECAM },
328 { .compatible = "cavium,pci-host-octeontx-ecam", .data = OTX_ECAM },
329 { .compatible = "pci-host-ecam-generic", .data = OTX_ECAM },
330 { .compatible = "cavium,pci-host-thunder-pem", .data = OTX_PEM },
331 { .compatible = "marvell,pci-host-octeontx2-pem", .data = OTX2_PEM },
332 { }
333};
334
335U_BOOT_DRIVER(pci_octeontx) = {
336 .name = "pci_octeontx",
337 .id = UCLASS_PCI,
338 .of_match = pci_octeontx_ids,
339 .ops = &pci_octeontx_ops,
Simon Glassd1998a92020-12-03 16:55:21 -0700340 .of_to_plat = pci_octeontx_of_to_plat,
Suneel Garapati638d7052019-10-19 17:28:01 -0700341 .probe = pci_octeontx_probe,
Simon Glass41575d82020-12-03 16:55:17 -0700342 .priv_auto = sizeof(struct octeontx_pci),
Suneel Garapati638d7052019-10-19 17:28:01 -0700343 .flags = DM_FLAG_PRE_RELOC,
344};