]>
Commit | Line | Data |
---|---|---|
638d705a SG |
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> | |
401d1c4f | 14 | #include <asm/global_data.h> |
638d705a SG |
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 | ||
638d705a SG |
52 | static 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 | ||
74 | static 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 | ||
91 | static 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 | ||
99 | static 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 | ||
bf667d5f T |
107 | 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); | |
638d705a SG |
110 | |
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 | ||
117 | static 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 | ||
bf667d5f T |
125 | 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); | |
638d705a SG |
128 | |
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 | ||
135 | static 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 | ||
638d705a SG |
146 | *valuep = pci_conv_32_to_size(~0UL, offset, size); |
147 | ||
148 | if (octeontx_bdf_invalid(bdf)) | |
149 | return -EPERM; | |
150 | ||
bf667d5f T |
151 | 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); | |
638d705a | 154 | |
bf667d5f | 155 | hdrtype = readb(pcie->cfg.start + address + PCI_HEADER_TYPE); |
638d705a SG |
156 | 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 | ||
165 | static 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 | ||
bf667d5f T |
176 | address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno, |
177 | PCI_DEV(bdf), PCI_FUNC(bdf), 0) << 4; | |
638d705a | 178 | |
bf667d5f | 179 | hdrtype = readb(pcie->cfg.start + address + PCI_HEADER_TYPE); |
638d705a SG |
180 | 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 | ||
bf667d5f | 189 | writel_size(pcie->cfg.start + address + offset, size, value); |
638d705a SG |
190 | |
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 | ||
198 | static 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 | ||
638d705a SG |
206 | *valuep = pci_conv_32_to_size(~0UL, offset, size); |
207 | ||
208 | if (octeontx_bdf_invalid(bdf)) | |
209 | return -EPERM; | |
210 | ||
bf667d5f T |
211 | 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); | |
638d705a SG |
214 | |
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 | ||
222 | static 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 | ||
638d705a SG |
230 | if (octeontx_bdf_invalid(bdf)) |
231 | return -EPERM; | |
232 | ||
bf667d5f T |
233 | 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); | |
638d705a SG |
236 | |
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 | ||
244 | int 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 | ||
269 | int 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 | ||
d1998a9f | 294 | static int pci_octeontx_of_to_plat(struct udevice *dev) |
638d705a SG |
295 | { |
296 | return 0; | |
297 | } | |
298 | ||
299 | static 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 | ||
321 | static const struct dm_pci_ops pci_octeontx_ops = { | |
322 | .read_config = pci_octeontx_read_config, | |
323 | .write_config = pci_octeontx_write_config, | |
324 | }; | |
325 | ||
326 | static 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 | ||
335 | U_BOOT_DRIVER(pci_octeontx) = { | |
336 | .name = "pci_octeontx", | |
337 | .id = UCLASS_PCI, | |
338 | .of_match = pci_octeontx_ids, | |
339 | .ops = &pci_octeontx_ops, | |
d1998a9f | 340 | .of_to_plat = pci_octeontx_of_to_plat, |
638d705a | 341 | .probe = pci_octeontx_probe, |
41575d8e | 342 | .priv_auto = sizeof(struct octeontx_pci), |
638d705a SG |
343 | .flags = DM_FLAG_PRE_RELOC, |
344 | }; |