1 |
dpavlin |
1 |
/* |
2 |
dpavlin |
7 |
* Cisco router simulation platform. |
3 |
dpavlin |
1 |
* Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) |
4 |
|
|
* |
5 |
|
|
* PCI devices. |
6 |
|
|
* |
7 |
|
|
* Very interesting docs: |
8 |
|
|
* http://www.science.unitn.it/~fiorella/guidelinux/tlk/node72.html |
9 |
|
|
* http://www.science.unitn.it/~fiorella/guidelinux/tlk/node76.html |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include <stdio.h> |
13 |
|
|
#include <stdlib.h> |
14 |
|
|
#include <string.h> |
15 |
|
|
#include <unistd.h> |
16 |
|
|
#include <sys/types.h> |
17 |
|
|
|
18 |
dpavlin |
7 |
#include "cpu.h" |
19 |
|
|
#include "vm.h" |
20 |
dpavlin |
1 |
#include "dynamips.h" |
21 |
|
|
#include "memory.h" |
22 |
|
|
#include "device.h" |
23 |
|
|
|
24 |
|
|
#define DEBUG_PCI 1 |
25 |
|
|
|
26 |
|
|
#define GET_PCI_ADDR(offset,mask) ((pci_bus->pci_addr >> offset) & mask) |
27 |
|
|
|
28 |
|
|
/* Trigger a PCI device IRQ */ |
29 |
|
|
void pci_dev_trigger_irq(vm_instance_t *vm,struct pci_device *dev) |
30 |
|
|
{ |
31 |
|
|
if (dev->irq != -1) |
32 |
|
|
vm_set_irq(vm,dev->irq); |
33 |
|
|
} |
34 |
|
|
|
35 |
|
|
/* Clear a PCI device IRQ */ |
36 |
|
|
void pci_dev_clear_irq(vm_instance_t *vm,struct pci_device *dev) |
37 |
|
|
{ |
38 |
|
|
if (dev->irq != -1) |
39 |
|
|
vm_clear_irq(vm,dev->irq); |
40 |
|
|
} |
41 |
|
|
|
42 |
|
|
/* Swapping function */ |
43 |
|
|
static inline m_uint32_t pci_swap(m_uint32_t val,int swap) |
44 |
|
|
{ |
45 |
|
|
return((swap) ? swap32(val) : val); |
46 |
|
|
} |
47 |
|
|
|
48 |
|
|
/* PCI bus lookup */ |
49 |
|
|
struct pci_bus *pci_bus_lookup(struct pci_bus *pci_bus_root,int bus) |
50 |
|
|
{ |
51 |
|
|
struct pci_bus *next_bus,*cur_bus = pci_bus_root; |
52 |
|
|
struct pci_bridge *bridge; |
53 |
|
|
|
54 |
|
|
while(cur_bus != NULL) { |
55 |
|
|
if (cur_bus->bus == bus) |
56 |
|
|
return cur_bus; |
57 |
|
|
|
58 |
|
|
/* Try busses behind PCI bridges */ |
59 |
|
|
next_bus = NULL; |
60 |
|
|
|
61 |
|
|
for(bridge=cur_bus->bridge_list;bridge;bridge=bridge->next) { |
62 |
|
|
/* |
63 |
|
|
* Specific case: final bridge with no checking of secondary |
64 |
|
|
* bus number. Dynamically programming. |
65 |
|
|
*/ |
66 |
|
|
if (bridge->skip_bus_check) { |
67 |
|
|
pci_bridge_set_bus_info(bridge,cur_bus->bus,bus,bus); |
68 |
|
|
bridge->skip_bus_check = FALSE; |
69 |
|
|
return bridge->pci_bus; |
70 |
|
|
} |
71 |
|
|
|
72 |
|
|
if ((bus >= bridge->sec_bus) && (bus <= bridge->sub_bus)) { |
73 |
|
|
next_bus = bridge->pci_bus; |
74 |
|
|
break; |
75 |
|
|
} |
76 |
|
|
} |
77 |
|
|
|
78 |
|
|
cur_bus = next_bus; |
79 |
|
|
} |
80 |
|
|
|
81 |
|
|
return NULL; |
82 |
|
|
} |
83 |
|
|
|
84 |
|
|
/* PCI device local lookup */ |
85 |
|
|
struct pci_device *pci_dev_lookup_local(struct pci_bus *pci_bus, |
86 |
|
|
int device,int function) |
87 |
|
|
{ |
88 |
|
|
struct pci_device *dev; |
89 |
|
|
|
90 |
|
|
for(dev=pci_bus->dev_list;dev;dev=dev->next) |
91 |
|
|
if ((dev->device == device) && (dev->function == function)) |
92 |
|
|
return dev; |
93 |
|
|
|
94 |
|
|
return NULL; |
95 |
|
|
} |
96 |
|
|
|
97 |
|
|
/* PCI Device lookup */ |
98 |
|
|
struct pci_device *pci_dev_lookup(struct pci_bus *pci_bus_root, |
99 |
|
|
int bus,int device,int function) |
100 |
|
|
{ |
101 |
|
|
struct pci_bus *req_bus; |
102 |
|
|
|
103 |
|
|
/* Find, try to find the request bus */ |
104 |
|
|
if (!(req_bus = pci_bus_lookup(pci_bus_root,bus))) |
105 |
|
|
return NULL; |
106 |
|
|
|
107 |
|
|
/* Walk through devices present on this bus */ |
108 |
|
|
return pci_dev_lookup_local(req_bus,device,function); |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
/* Handle the address register access */ |
112 |
dpavlin |
7 |
void pci_dev_addr_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus, |
113 |
dpavlin |
1 |
u_int op_type,int swap,m_uint64_t *data) |
114 |
|
|
{ |
115 |
|
|
if (op_type == MTS_WRITE) |
116 |
|
|
pci_bus->pci_addr = pci_swap(*data,swap); |
117 |
|
|
else |
118 |
|
|
*data = pci_swap(pci_bus->pci_addr,swap); |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
/* |
122 |
|
|
* Handle the data register access. |
123 |
|
|
* |
124 |
|
|
* The address of requested register is first written at address 0xcf8 |
125 |
|
|
* (with pci_dev_addr_handler). |
126 |
|
|
* |
127 |
|
|
* The data is read/written at address 0xcfc. |
128 |
|
|
*/ |
129 |
dpavlin |
7 |
void pci_dev_data_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus, |
130 |
dpavlin |
1 |
u_int op_type,int swap,m_uint64_t *data) |
131 |
|
|
{ |
132 |
|
|
struct pci_device *dev; |
133 |
|
|
int bus,device,function,reg; |
134 |
|
|
|
135 |
|
|
if (op_type == MTS_READ) |
136 |
dpavlin |
7 |
*data = 0x0; |
137 |
dpavlin |
1 |
|
138 |
|
|
/* |
139 |
|
|
* http://www.mega-tokyo.com/osfaq2/index.php/PciSectionOfPentiumVme |
140 |
|
|
* |
141 |
|
|
* 31 : Enable Bit |
142 |
|
|
* 30 - 24 : Reserved |
143 |
|
|
* 23 - 16 : Bus Number |
144 |
|
|
* 15 - 11 : Device Number |
145 |
|
|
* 10 - 8 : Function Number |
146 |
|
|
* 7 - 2 : Register Number |
147 |
|
|
* 1 - 0 : always 00 |
148 |
|
|
*/ |
149 |
|
|
bus = GET_PCI_ADDR(16,0xff); |
150 |
|
|
device = GET_PCI_ADDR(11,0x1f); |
151 |
|
|
function = GET_PCI_ADDR(8,0x7); |
152 |
|
|
reg = GET_PCI_ADDR(0,0xff); |
153 |
|
|
|
154 |
|
|
/* Find the corresponding PCI device */ |
155 |
|
|
dev = pci_dev_lookup(pci_bus,bus,device,function); |
156 |
|
|
|
157 |
|
|
#if DEBUG_PCI |
158 |
|
|
if (op_type == MTS_READ) { |
159 |
|
|
cpu_log(cpu,"PCI","read request at pc=0x%llx: " |
160 |
|
|
"bus=%d,device=%d,function=%d,reg=0x%2.2x\n", |
161 |
dpavlin |
7 |
cpu_get_pc(cpu), bus, device, function, reg); |
162 |
dpavlin |
1 |
} else { |
163 |
|
|
cpu_log(cpu,"PCI","write request (data=0x%8.8x) at pc=0x%llx: " |
164 |
|
|
"bus=%d,device=%d,function=%d,reg=0x%2.2x\n", |
165 |
dpavlin |
7 |
pci_swap(*data,swap), cpu_get_pc(cpu), |
166 |
|
|
bus, device, function, reg); |
167 |
dpavlin |
1 |
} |
168 |
|
|
#endif |
169 |
|
|
|
170 |
|
|
if (!dev) { |
171 |
|
|
if (op_type == MTS_READ) { |
172 |
|
|
cpu_log(cpu,"PCI","read request for unknown device at pc=0x%llx " |
173 |
|
|
"(bus=%d,device=%d,function=%d,reg=0x%2.2x).\n", |
174 |
dpavlin |
7 |
cpu_get_pc(cpu), bus, device, function, reg); |
175 |
dpavlin |
1 |
} else { |
176 |
|
|
cpu_log(cpu,"PCI","write request (data=0x%8.8x) for unknown device " |
177 |
|
|
"at pc=0x%llx (bus=%d,device=%d,function=%d,reg=0x%2.2x).\n", |
178 |
dpavlin |
7 |
pci_swap(*data,swap), cpu_get_pc(cpu), |
179 |
|
|
bus, device, function, reg); |
180 |
dpavlin |
1 |
} |
181 |
|
|
|
182 |
|
|
/* Returns an invalid device ID */ |
183 |
|
|
if ((op_type == MTS_READ) && (reg == PCI_REG_ID)) |
184 |
|
|
*data = 0xffffffff; |
185 |
|
|
} else { |
186 |
|
|
if (op_type == MTS_WRITE) { |
187 |
|
|
if (dev->write_register != NULL) |
188 |
|
|
dev->write_register(cpu,dev,reg,pci_swap(*data,swap)); |
189 |
|
|
} else { |
190 |
|
|
if (reg == PCI_REG_ID) |
191 |
|
|
*data = pci_swap((dev->product_id << 16) | dev->vendor_id,swap); |
192 |
|
|
else { |
193 |
|
|
if (dev->read_register != NULL) |
194 |
|
|
*data = pci_swap(dev->read_register(cpu,dev,reg),swap); |
195 |
|
|
} |
196 |
|
|
} |
197 |
|
|
} |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
/* Add a PCI bridge */ |
201 |
|
|
struct pci_bridge *pci_bridge_add(struct pci_bus *pci_bus) |
202 |
|
|
{ |
203 |
|
|
struct pci_bridge *bridge; |
204 |
|
|
|
205 |
|
|
if (!pci_bus) |
206 |
|
|
return NULL; |
207 |
|
|
|
208 |
|
|
if (!(bridge = malloc(sizeof(*bridge)))) { |
209 |
|
|
fprintf(stderr,"pci_bridge_add: unable to create new PCI bridge.\n"); |
210 |
|
|
return NULL; |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
memset(bridge,0,sizeof(*bridge)); |
214 |
|
|
bridge->pri_bus = pci_bus->bus; |
215 |
|
|
bridge->sec_bus = -1; |
216 |
|
|
bridge->sub_bus = -1; |
217 |
|
|
bridge->pci_bus = NULL; |
218 |
|
|
|
219 |
|
|
/* Insert the bridge in the double-linked list */ |
220 |
|
|
bridge->next = pci_bus->bridge_list; |
221 |
|
|
bridge->pprev = &pci_bus->bridge_list; |
222 |
|
|
|
223 |
|
|
if (pci_bus->bridge_list != NULL) |
224 |
|
|
pci_bus->bridge_list->pprev = &bridge->next; |
225 |
|
|
|
226 |
|
|
pci_bus->bridge_list = bridge; |
227 |
|
|
return bridge; |
228 |
|
|
} |
229 |
|
|
|
230 |
|
|
/* Remove a PCI bridge from the double-linked list */ |
231 |
|
|
static inline void pci_bridge_remove_from_list(struct pci_bridge *bridge) |
232 |
|
|
{ |
233 |
|
|
if (bridge->next) |
234 |
|
|
bridge->next->pprev = bridge->pprev; |
235 |
|
|
|
236 |
|
|
if (bridge->pprev) |
237 |
|
|
*(bridge->pprev) = bridge->next; |
238 |
|
|
} |
239 |
|
|
|
240 |
|
|
/* Remove a PCI bridge */ |
241 |
|
|
void pci_bridge_remove(struct pci_bridge *bridge) |
242 |
|
|
{ |
243 |
|
|
if (bridge != NULL) { |
244 |
|
|
pci_bridge_remove_from_list(bridge); |
245 |
|
|
free(bridge); |
246 |
|
|
} |
247 |
|
|
} |
248 |
|
|
|
249 |
|
|
/* Map secondary bus to a PCI bridge */ |
250 |
|
|
void pci_bridge_map_bus(struct pci_bridge *bridge,struct pci_bus *pci_bus) |
251 |
|
|
{ |
252 |
|
|
if (bridge != NULL) { |
253 |
|
|
bridge->pci_bus = pci_bus; |
254 |
|
|
|
255 |
|
|
if (bridge->pci_bus != NULL) |
256 |
|
|
bridge->pci_bus->bus = bridge->sec_bus; |
257 |
|
|
} |
258 |
|
|
} |
259 |
|
|
|
260 |
|
|
/* Set PCI bridge bus info */ |
261 |
|
|
void pci_bridge_set_bus_info(struct pci_bridge *bridge, |
262 |
|
|
int pri_bus,int sec_bus,int sub_bus) |
263 |
|
|
{ |
264 |
|
|
if (bridge != NULL) { |
265 |
|
|
bridge->pri_bus = pri_bus; |
266 |
|
|
bridge->sec_bus = sec_bus; |
267 |
|
|
bridge->sub_bus = sub_bus; |
268 |
|
|
|
269 |
|
|
if (bridge->pci_bus != NULL) |
270 |
|
|
bridge->pci_bus->bus = bridge->sec_bus; |
271 |
|
|
} |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
/* Add a PCI device */ |
275 |
|
|
struct pci_device * |
276 |
|
|
pci_dev_add(struct pci_bus *pci_bus,char *name, |
277 |
|
|
u_int vendor_id,u_int product_id, |
278 |
|
|
int device,int function,int irq, |
279 |
|
|
void *priv_data,pci_init_t init, |
280 |
|
|
pci_reg_read_t read_register, |
281 |
|
|
pci_reg_write_t write_register) |
282 |
|
|
{ |
283 |
|
|
struct pci_device *dev; |
284 |
|
|
|
285 |
|
|
if (!pci_bus) |
286 |
|
|
return NULL; |
287 |
|
|
|
288 |
|
|
if ((dev = pci_dev_lookup_local(pci_bus,device,function)) != NULL) { |
289 |
|
|
fprintf(stderr,"pci_dev_add: bus %s, device %d, function %d already " |
290 |
|
|
"registered (device '%s').\n", |
291 |
|
|
pci_bus->name,device,function,dev->name); |
292 |
|
|
return NULL; |
293 |
|
|
} |
294 |
|
|
|
295 |
|
|
/* we can create safely the new device */ |
296 |
|
|
if (!(dev = malloc(sizeof(*dev)))) { |
297 |
|
|
fprintf(stderr,"pci_dev_add: unable to create new PCI device.\n"); |
298 |
|
|
return NULL; |
299 |
|
|
} |
300 |
|
|
|
301 |
|
|
memset(dev,0,sizeof(*dev)); |
302 |
|
|
dev->name = name; |
303 |
|
|
dev->vendor_id = vendor_id; |
304 |
|
|
dev->product_id = product_id; |
305 |
|
|
dev->pci_bus = pci_bus; |
306 |
|
|
dev->device = device; |
307 |
|
|
dev->function = function; |
308 |
|
|
dev->irq = irq; |
309 |
|
|
dev->priv_data = priv_data; |
310 |
|
|
dev->init = init; |
311 |
|
|
dev->read_register = read_register; |
312 |
|
|
dev->write_register = write_register; |
313 |
|
|
|
314 |
|
|
/* Insert the device in the double-linked list */ |
315 |
|
|
dev->next = pci_bus->dev_list; |
316 |
|
|
dev->pprev = &pci_bus->dev_list; |
317 |
|
|
|
318 |
|
|
if (pci_bus->dev_list != NULL) |
319 |
|
|
pci_bus->dev_list->pprev = &dev->next; |
320 |
|
|
|
321 |
|
|
pci_bus->dev_list = dev; |
322 |
|
|
|
323 |
|
|
if (init) init(dev); |
324 |
|
|
return dev; |
325 |
|
|
} |
326 |
|
|
|
327 |
|
|
/* Add a basic PCI device that just returns a Vendor/Product ID */ |
328 |
|
|
struct pci_device * |
329 |
|
|
pci_dev_add_basic(struct pci_bus *pci_bus, |
330 |
|
|
char *name,u_int vendor_id,u_int product_id, |
331 |
|
|
int device,int function) |
332 |
|
|
{ |
333 |
|
|
return(pci_dev_add(pci_bus,name,vendor_id,product_id, |
334 |
|
|
device,function,-1,NULL, |
335 |
|
|
NULL,NULL,NULL)); |
336 |
|
|
} |
337 |
|
|
|
338 |
|
|
/* Remove a device from the double-linked list */ |
339 |
|
|
static inline void pci_dev_remove_from_list(struct pci_device *dev) |
340 |
|
|
{ |
341 |
|
|
if (dev->next) |
342 |
|
|
dev->next->pprev = dev->pprev; |
343 |
|
|
|
344 |
|
|
if (dev->pprev) |
345 |
|
|
*(dev->pprev) = dev->next; |
346 |
|
|
} |
347 |
|
|
|
348 |
|
|
/* Remove a PCI device */ |
349 |
|
|
void pci_dev_remove(struct pci_device *dev) |
350 |
|
|
{ |
351 |
|
|
if (dev != NULL) { |
352 |
|
|
pci_dev_remove_from_list(dev); |
353 |
|
|
free(dev); |
354 |
|
|
} |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
/* Remove a PCI device given its ID (bus,device,function) */ |
358 |
|
|
int pci_dev_remove_by_id(struct pci_bus *pci_bus, |
359 |
|
|
int bus,int device,int function) |
360 |
|
|
{ |
361 |
|
|
struct pci_device *dev; |
362 |
|
|
|
363 |
|
|
if (!(dev = pci_dev_lookup(pci_bus,bus,device,function))) |
364 |
|
|
return(-1); |
365 |
|
|
|
366 |
|
|
pci_dev_remove(dev); |
367 |
|
|
return(0); |
368 |
|
|
} |
369 |
|
|
|
370 |
|
|
/* Remove a PCI device given its name */ |
371 |
|
|
int pci_dev_remove_by_name(struct pci_bus *pci_bus,char *name) |
372 |
|
|
{ |
373 |
|
|
struct pci_device *dev,*next; |
374 |
|
|
int count = 0; |
375 |
|
|
|
376 |
|
|
for(dev=pci_bus->dev_list;dev;dev=next) { |
377 |
|
|
next = dev->next; |
378 |
|
|
|
379 |
|
|
if (!strcmp(dev->name,name)) { |
380 |
|
|
pci_dev_remove(dev); |
381 |
|
|
count++; |
382 |
|
|
} |
383 |
|
|
} |
384 |
|
|
|
385 |
|
|
return(count); |
386 |
|
|
} |
387 |
|
|
|
388 |
|
|
/* Create a PCI bus */ |
389 |
|
|
struct pci_bus *pci_bus_create(char *name,int bus) |
390 |
|
|
{ |
391 |
|
|
struct pci_bus *d; |
392 |
|
|
|
393 |
|
|
if (!(d = malloc(sizeof(*d)))) { |
394 |
|
|
fprintf(stderr,"pci_bus_create: unable to create PCI info.\n"); |
395 |
|
|
return NULL; |
396 |
|
|
} |
397 |
|
|
|
398 |
|
|
memset(d,0,sizeof(*d)); |
399 |
|
|
d->name = strdup(name); |
400 |
|
|
d->bus = bus; |
401 |
|
|
return d; |
402 |
|
|
} |
403 |
|
|
|
404 |
|
|
/* Delete a PCI bus */ |
405 |
|
|
void pci_bus_remove(struct pci_bus *pci_bus) |
406 |
|
|
{ |
407 |
|
|
struct pci_device *dev,*next; |
408 |
|
|
struct pci_bridge *bridge,*next_bridge; |
409 |
|
|
|
410 |
|
|
if (pci_bus) { |
411 |
|
|
/* Remove all devices */ |
412 |
|
|
for(dev=pci_bus->dev_list;dev;dev=next) { |
413 |
|
|
next = dev->next; |
414 |
|
|
free(dev); |
415 |
|
|
} |
416 |
|
|
|
417 |
|
|
/* Remove all bridges */ |
418 |
|
|
for(bridge=pci_bus->bridge_list;bridge;bridge=next_bridge) { |
419 |
|
|
next_bridge = bridge->next; |
420 |
|
|
free(bridge); |
421 |
|
|
} |
422 |
|
|
|
423 |
|
|
/* Free the structure itself */ |
424 |
|
|
free(pci_bus->name); |
425 |
|
|
free(pci_bus); |
426 |
|
|
} |
427 |
|
|
} |
428 |
|
|
|
429 |
|
|
/* Read a configuration register of a PCI bridge */ |
430 |
dpavlin |
7 |
static m_uint32_t pci_bridge_read_reg(cpu_gen_t *cpu,struct pci_device *dev, |
431 |
dpavlin |
1 |
int reg) |
432 |
|
|
{ |
433 |
|
|
struct pci_bridge *bridge = dev->priv_data; |
434 |
|
|
m_uint32_t val = 0; |
435 |
|
|
|
436 |
|
|
switch(reg) { |
437 |
|
|
case 0x18: |
438 |
|
|
return(bridge->cfg_reg_bus); |
439 |
|
|
default: |
440 |
|
|
if (bridge->fallback_read != NULL) |
441 |
|
|
val = bridge->fallback_read(cpu,dev,reg); |
442 |
|
|
|
443 |
|
|
/* Returns appropriate PCI bridge class code if nothing defined */ |
444 |
|
|
if ((reg == 0x08) && !val) |
445 |
|
|
val = 0x06040000; |
446 |
|
|
|
447 |
|
|
return(val); |
448 |
|
|
} |
449 |
|
|
} |
450 |
|
|
|
451 |
|
|
/* Write a configuration register of a PCI bridge */ |
452 |
dpavlin |
7 |
static void pci_bridge_write_reg(cpu_gen_t *cpu,struct pci_device *dev, |
453 |
dpavlin |
1 |
int reg,m_uint32_t value) |
454 |
|
|
{ |
455 |
|
|
struct pci_bridge *bridge = dev->priv_data; |
456 |
|
|
u_int pri_bus,sec_bus,sub_bus; |
457 |
|
|
|
458 |
|
|
switch(reg) { |
459 |
|
|
case 0x18: |
460 |
|
|
bridge->cfg_reg_bus = value; |
461 |
|
|
sub_bus = (value >> 16) & 0xFF; |
462 |
|
|
sec_bus = (value >> 8) & 0xFF; |
463 |
|
|
pri_bus = value & 0xFF; |
464 |
|
|
|
465 |
|
|
/* Modify the PCI bridge settings */ |
466 |
|
|
vm_log(cpu->vm,"PCI", |
467 |
|
|
"PCI bridge %d,%d,%d -> pri: %2.2u, sec: %2.2u, sub: %2.2u\n", |
468 |
|
|
dev->pci_bus->bus,dev->device,dev->function, |
469 |
|
|
pri_bus,sec_bus,sub_bus); |
470 |
|
|
|
471 |
|
|
pci_bridge_set_bus_info(bridge,pri_bus,sec_bus,sub_bus); |
472 |
|
|
break; |
473 |
|
|
|
474 |
|
|
default: |
475 |
|
|
if (bridge->fallback_write != NULL) |
476 |
|
|
bridge->fallback_write(cpu,dev,reg,value); |
477 |
|
|
} |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
/* Create a PCI bridge device */ |
481 |
|
|
struct pci_device *pci_bridge_create_dev(struct pci_bus *pci_bus,char *name, |
482 |
|
|
u_int vendor_id,u_int product_id, |
483 |
|
|
int device,int function, |
484 |
|
|
struct pci_bus *sec_bus, |
485 |
|
|
pci_reg_read_t fallback_read, |
486 |
|
|
pci_reg_write_t fallback_write) |
487 |
|
|
{ |
488 |
|
|
struct pci_bridge *bridge; |
489 |
|
|
struct pci_device *dev; |
490 |
|
|
|
491 |
|
|
/* Create the PCI bridge structure */ |
492 |
|
|
if (!(bridge = pci_bridge_add(pci_bus))) |
493 |
|
|
return NULL; |
494 |
|
|
|
495 |
|
|
/* Create the PCI device corresponding to the bridge */ |
496 |
|
|
dev = pci_dev_add(pci_bus,name,vendor_id,product_id,device,function,-1, |
497 |
|
|
bridge,NULL,pci_bridge_read_reg,pci_bridge_write_reg); |
498 |
|
|
|
499 |
|
|
if (!dev) |
500 |
|
|
goto err_pci_dev; |
501 |
|
|
|
502 |
|
|
/* Keep the associated PCI device for this bridge */ |
503 |
|
|
bridge->pci_dev = dev; |
504 |
|
|
|
505 |
|
|
/* Set the fallback functions */ |
506 |
|
|
bridge->fallback_read = fallback_read; |
507 |
|
|
bridge->fallback_write = fallback_write; |
508 |
|
|
|
509 |
|
|
/* Map the secondary bus (disabled at startup) */ |
510 |
|
|
pci_bridge_map_bus(bridge,sec_bus); |
511 |
|
|
return dev; |
512 |
|
|
|
513 |
|
|
err_pci_dev: |
514 |
|
|
pci_bridge_remove(bridge); |
515 |
|
|
return NULL; |
516 |
|
|
} |
517 |
|
|
|
518 |
|
|
/* Show PCI device list of the specified bus */ |
519 |
|
|
static void pci_bus_show_dev_list(struct pci_bus *pci_bus) |
520 |
|
|
{ |
521 |
|
|
struct pci_device *dev; |
522 |
|
|
struct pci_bridge *bridge; |
523 |
|
|
char bus_id[32]; |
524 |
|
|
|
525 |
|
|
if (!pci_bus) |
526 |
|
|
return; |
527 |
|
|
|
528 |
|
|
if (pci_bus->bus != -1) { |
529 |
|
|
snprintf(bus_id,sizeof(bus_id),"%2d",pci_bus->bus); |
530 |
|
|
} else { |
531 |
|
|
strcpy(bus_id,"XX"); |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
for(dev=pci_bus->dev_list;dev;dev=dev->next) { |
535 |
|
|
printf(" %-18s: ID %4.4x:%4.4x, Bus %s, Dev. %2d, Func. %2d", |
536 |
|
|
dev->name,dev->vendor_id,dev->product_id, |
537 |
|
|
bus_id,dev->device,dev->function); |
538 |
|
|
|
539 |
|
|
if (dev->irq != -1) |
540 |
|
|
printf(", IRQ: %d\n",dev->irq); |
541 |
|
|
else |
542 |
|
|
printf("\n"); |
543 |
|
|
} |
544 |
|
|
|
545 |
|
|
for(bridge=pci_bus->bridge_list;bridge;bridge=bridge->next) |
546 |
|
|
pci_bus_show_dev_list(bridge->pci_bus); |
547 |
|
|
} |
548 |
|
|
|
549 |
|
|
/* Show PCI device list */ |
550 |
|
|
void pci_dev_show_list(struct pci_bus *pci_bus) |
551 |
|
|
{ |
552 |
|
|
if (!pci_bus) |
553 |
|
|
return; |
554 |
|
|
|
555 |
|
|
printf("PCI Bus \"%s\" Device list:\n",pci_bus->name); |
556 |
|
|
pci_bus_show_dev_list(pci_bus); |
557 |
|
|
printf("\n"); |
558 |
|
|
} |