]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
aab6724c SG |
2 | /* |
3 | * Copyright (c) 2014 Google, Inc | |
4 | * | |
5 | * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> | |
6 | * Andreas Heppel <[email protected]> | |
7 | * | |
8 | * (C) Copyright 2002, 2003 | |
9 | * Wolfgang Denk, DENX Software Engineering, [email protected]. | |
aab6724c SG |
10 | */ |
11 | ||
aec241df | 12 | #include <dm.h> |
7b51b576 | 13 | #include <env.h> |
aab6724c SG |
14 | #include <errno.h> |
15 | #include <pci.h> | |
16 | #include <asm/io.h> | |
17 | ||
18 | const char *pci_class_str(u8 class) | |
19 | { | |
20 | switch (class) { | |
21 | case PCI_CLASS_NOT_DEFINED: | |
22 | return "Build before PCI Rev2.0"; | |
23 | break; | |
24 | case PCI_BASE_CLASS_STORAGE: | |
25 | return "Mass storage controller"; | |
26 | break; | |
27 | case PCI_BASE_CLASS_NETWORK: | |
28 | return "Network controller"; | |
29 | break; | |
30 | case PCI_BASE_CLASS_DISPLAY: | |
31 | return "Display controller"; | |
32 | break; | |
33 | case PCI_BASE_CLASS_MULTIMEDIA: | |
34 | return "Multimedia device"; | |
35 | break; | |
36 | case PCI_BASE_CLASS_MEMORY: | |
37 | return "Memory controller"; | |
38 | break; | |
39 | case PCI_BASE_CLASS_BRIDGE: | |
40 | return "Bridge device"; | |
41 | break; | |
42 | case PCI_BASE_CLASS_COMMUNICATION: | |
43 | return "Simple comm. controller"; | |
44 | break; | |
45 | case PCI_BASE_CLASS_SYSTEM: | |
46 | return "Base system peripheral"; | |
47 | break; | |
48 | case PCI_BASE_CLASS_INPUT: | |
49 | return "Input device"; | |
50 | break; | |
51 | case PCI_BASE_CLASS_DOCKING: | |
52 | return "Docking station"; | |
53 | break; | |
54 | case PCI_BASE_CLASS_PROCESSOR: | |
55 | return "Processor"; | |
56 | break; | |
57 | case PCI_BASE_CLASS_SERIAL: | |
58 | return "Serial bus controller"; | |
59 | break; | |
60 | case PCI_BASE_CLASS_INTELLIGENT: | |
61 | return "Intelligent controller"; | |
62 | break; | |
63 | case PCI_BASE_CLASS_SATELLITE: | |
64 | return "Satellite controller"; | |
65 | break; | |
66 | case PCI_BASE_CLASS_CRYPT: | |
67 | return "Cryptographic device"; | |
68 | break; | |
69 | case PCI_BASE_CLASS_SIGNAL_PROCESSING: | |
70 | return "DSP"; | |
71 | break; | |
72 | case PCI_CLASS_OTHERS: | |
73 | return "Does not fit any class"; | |
74 | break; | |
75 | default: | |
76 | return "???"; | |
77 | break; | |
78 | }; | |
79 | } | |
80 | ||
aab6724c SG |
81 | __weak int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) |
82 | { | |
83 | /* | |
84 | * Check if pci device should be skipped in configuration | |
85 | */ | |
86 | if (dev == PCI_BDF(hose->first_busno, 0, 0)) { | |
87 | #if defined(CONFIG_PCI_CONFIG_HOST_BRIDGE) /* don't skip host bridge */ | |
88 | /* | |
89 | * Only skip configuration if "pciconfighost" is not set | |
90 | */ | |
00caae6d | 91 | if (env_get("pciconfighost") == NULL) |
aab6724c SG |
92 | return 1; |
93 | #else | |
94 | return 1; | |
95 | #endif | |
96 | } | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
26221dc3 | 101 | #if defined(CONFIG_DM_PCI_COMPAT) |
aab6724c SG |
102 | /* Get a virtual address associated with a BAR region */ |
103 | void *pci_map_bar(pci_dev_t pdev, int bar, int flags) | |
104 | { | |
105 | pci_addr_t pci_bus_addr; | |
106 | u32 bar_response; | |
107 | ||
108 | /* read BAR address */ | |
109 | pci_read_config_dword(pdev, bar, &bar_response); | |
110 | pci_bus_addr = (pci_addr_t)(bar_response & ~0xf); | |
111 | ||
112 | /* | |
113 | * Pass "0" as the length argument to pci_bus_to_virt. The arg | |
114 | * isn't actualy used on any platform because u-boot assumes a static | |
115 | * linear mapping. In the future, this could read the BAR size | |
116 | * and pass that as the size if needed. | |
117 | */ | |
118 | return pci_bus_to_virt(pdev, pci_bus_addr, flags, 0, MAP_NOCACHE); | |
119 | } | |
120 | ||
121 | void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum, | |
122 | u32 addr_and_ctrl) | |
123 | { | |
124 | int bar; | |
125 | ||
126 | bar = PCI_BASE_ADDRESS_0 + barnum * 4; | |
127 | pci_hose_write_config_dword(hose, dev, bar, addr_and_ctrl); | |
128 | } | |
129 | ||
130 | u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum) | |
131 | { | |
132 | u32 addr; | |
133 | int bar; | |
134 | ||
135 | bar = PCI_BASE_ADDRESS_0 + barnum * 4; | |
136 | pci_hose_read_config_dword(hose, dev, bar, &addr); | |
137 | if (addr & PCI_BASE_ADDRESS_SPACE_IO) | |
138 | return addr & PCI_BASE_ADDRESS_IO_MASK; | |
139 | else | |
140 | return addr & PCI_BASE_ADDRESS_MEM_MASK; | |
141 | } | |
142 | ||
143 | int __pci_hose_bus_to_phys(struct pci_controller *hose, | |
238fe16c BM |
144 | pci_addr_t bus_addr, |
145 | unsigned long flags, | |
146 | unsigned long skip_mask, | |
147 | phys_addr_t *pa) | |
aab6724c SG |
148 | { |
149 | struct pci_region *res; | |
150 | int i; | |
151 | ||
152 | for (i = 0; i < hose->region_count; i++) { | |
153 | res = &hose->regions[i]; | |
154 | ||
155 | if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0) | |
156 | continue; | |
157 | ||
158 | if (res->flags & skip_mask) | |
159 | continue; | |
160 | ||
161 | if (bus_addr >= res->bus_start && | |
162 | (bus_addr - res->bus_start) < res->size) { | |
163 | *pa = (bus_addr - res->bus_start + res->phys_start); | |
164 | return 0; | |
165 | } | |
166 | } | |
167 | ||
168 | return 1; | |
169 | } | |
170 | ||
171 | phys_addr_t pci_hose_bus_to_phys(struct pci_controller *hose, | |
172 | pci_addr_t bus_addr, | |
173 | unsigned long flags) | |
174 | { | |
175 | phys_addr_t phys_addr = 0; | |
176 | int ret; | |
177 | ||
178 | if (!hose) { | |
179 | puts("pci_hose_bus_to_phys: invalid hose\n"); | |
180 | return phys_addr; | |
181 | } | |
182 | ||
183 | /* | |
184 | * if PCI_REGION_MEM is set we do a two pass search with preference | |
185 | * on matches that don't have PCI_REGION_SYS_MEMORY set | |
186 | */ | |
35262850 | 187 | if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) { |
aab6724c SG |
188 | ret = __pci_hose_bus_to_phys(hose, bus_addr, |
189 | flags, PCI_REGION_SYS_MEMORY, &phys_addr); | |
190 | if (!ret) | |
191 | return phys_addr; | |
192 | } | |
193 | ||
194 | ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr); | |
195 | ||
196 | if (ret) | |
197 | puts("pci_hose_bus_to_phys: invalid physical address\n"); | |
198 | ||
199 | return phys_addr; | |
200 | } | |
201 | ||
238fe16c BM |
202 | int __pci_hose_phys_to_bus(struct pci_controller *hose, |
203 | phys_addr_t phys_addr, | |
204 | unsigned long flags, | |
205 | unsigned long skip_mask, | |
206 | pci_addr_t *ba) | |
207 | { | |
208 | struct pci_region *res; | |
209 | pci_addr_t bus_addr; | |
210 | int i; | |
211 | ||
212 | for (i = 0; i < hose->region_count; i++) { | |
213 | res = &hose->regions[i]; | |
214 | ||
215 | if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0) | |
216 | continue; | |
217 | ||
218 | if (res->flags & skip_mask) | |
219 | continue; | |
220 | ||
221 | bus_addr = phys_addr - res->phys_start + res->bus_start; | |
222 | ||
223 | if (bus_addr >= res->bus_start && | |
b4bd6554 | 224 | (bus_addr - res->bus_start) < res->size) { |
238fe16c BM |
225 | *ba = bus_addr; |
226 | return 0; | |
227 | } | |
228 | } | |
229 | ||
230 | return 1; | |
231 | } | |
232 | ||
fcf45692 ML |
233 | /* |
234 | * pci_hose_phys_to_bus(): Convert physical address to bus address | |
235 | * @hose: PCI hose of the root PCI controller | |
236 | * @phys_addr: physical address to convert | |
237 | * @flags: flags of pci regions | |
185f812c | 238 | * Return: bus address if OK, 0 on error |
fcf45692 | 239 | */ |
238fe16c BM |
240 | pci_addr_t pci_hose_phys_to_bus(struct pci_controller *hose, |
241 | phys_addr_t phys_addr, | |
242 | unsigned long flags) | |
243 | { | |
244 | pci_addr_t bus_addr = 0; | |
245 | int ret; | |
246 | ||
247 | if (!hose) { | |
248 | puts("pci_hose_phys_to_bus: invalid hose\n"); | |
249 | return bus_addr; | |
250 | } | |
251 | ||
252 | /* | |
253 | * if PCI_REGION_MEM is set we do a two pass search with preference | |
254 | * on matches that don't have PCI_REGION_SYS_MEMORY set | |
255 | */ | |
35262850 | 256 | if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) { |
238fe16c BM |
257 | ret = __pci_hose_phys_to_bus(hose, phys_addr, |
258 | flags, PCI_REGION_SYS_MEMORY, &bus_addr); | |
259 | if (!ret) | |
260 | return bus_addr; | |
261 | } | |
262 | ||
263 | ret = __pci_hose_phys_to_bus(hose, phys_addr, flags, 0, &bus_addr); | |
264 | ||
265 | if (ret) | |
266 | puts("pci_hose_phys_to_bus: invalid physical address\n"); | |
267 | ||
268 | return bus_addr; | |
269 | } | |
270 | ||
aab6724c SG |
271 | pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index) |
272 | { | |
273 | struct pci_device_id ids[2] = { {}, {0, 0} }; | |
274 | ||
275 | ids[0].vendor = vendor; | |
276 | ids[0].device = device; | |
277 | ||
278 | return pci_find_devices(ids, index); | |
279 | } | |
280 | ||
281 | pci_dev_t pci_hose_find_devices(struct pci_controller *hose, int busnum, | |
282 | struct pci_device_id *ids, int *indexp) | |
283 | { | |
284 | int found_multi = 0; | |
285 | u16 vendor, device; | |
286 | u8 header_type; | |
287 | pci_dev_t bdf; | |
288 | int i; | |
289 | ||
290 | for (bdf = PCI_BDF(busnum, 0, 0); | |
291 | bdf < PCI_BDF(busnum + 1, 0, 0); | |
292 | bdf += PCI_BDF(0, 0, 1)) { | |
293 | if (pci_skip_dev(hose, bdf)) | |
294 | continue; | |
295 | ||
296 | if (!PCI_FUNC(bdf)) { | |
297 | pci_read_config_byte(bdf, PCI_HEADER_TYPE, | |
298 | &header_type); | |
299 | found_multi = header_type & 0x80; | |
300 | } else { | |
301 | if (!found_multi) | |
302 | continue; | |
303 | } | |
304 | ||
305 | pci_read_config_word(bdf, PCI_VENDOR_ID, &vendor); | |
306 | pci_read_config_word(bdf, PCI_DEVICE_ID, &device); | |
307 | ||
308 | for (i = 0; ids[i].vendor != 0; i++) { | |
309 | if (vendor == ids[i].vendor && | |
310 | device == ids[i].device) { | |
311 | if ((*indexp) <= 0) | |
312 | return bdf; | |
313 | ||
314 | (*indexp)--; | |
315 | } | |
316 | } | |
317 | } | |
318 | ||
319 | return -1; | |
320 | } | |
170366c1 SG |
321 | |
322 | pci_dev_t pci_find_class(uint find_class, int index) | |
323 | { | |
324 | int bus; | |
325 | int devnum; | |
326 | pci_dev_t bdf; | |
327 | uint32_t class; | |
328 | ||
329 | for (bus = 0; bus <= pci_last_busno(); bus++) { | |
330 | for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES - 1; devnum++) { | |
331 | pci_read_config_dword(PCI_BDF(bus, devnum, 0), | |
332 | PCI_CLASS_REVISION, &class); | |
333 | if (class >> 16 == 0xffff) | |
334 | continue; | |
335 | ||
336 | for (bdf = PCI_BDF(bus, devnum, 0); | |
337 | bdf <= PCI_BDF(bus, devnum, | |
338 | PCI_MAX_PCI_FUNCTIONS - 1); | |
339 | bdf += PCI_BDF(0, 0, 1)) { | |
340 | pci_read_config_dword(bdf, PCI_CLASS_REVISION, | |
341 | &class); | |
342 | class >>= 8; | |
343 | ||
344 | if (class != find_class) | |
345 | continue; | |
346 | /* | |
347 | * Decrement the index. We want to return the | |
348 | * correct device, so index is 0 for the first | |
349 | * matching device, 1 for the second, etc. | |
350 | */ | |
351 | if (index) { | |
352 | index--; | |
353 | continue; | |
354 | } | |
355 | /* Return index'th controller. */ | |
356 | return bdf; | |
357 | } | |
358 | } | |
359 | } | |
360 | ||
361 | return -ENODEV; | |
362 | } | |
26221dc3 | 363 | #endif /* CONFIG_DM_PCI_COMPAT */ |