📄 pci.c
字号:
}
struct pci_bus far * pci_add_new_bus(struct pci_bus far * parent, struct pci_dev far *dev,
long busnr)
{
struct pci_bus far * child;
int i;
/*
* Allocate a new bus, and inherit stuff from the parent..
*/
child = pci_alloc_bus();
list_add_tail(&child->node, &parent->children);
child->self = dev;
dev->subordinate = child;
child->parent = parent;
child->ops = parent->ops;
child->sysdata = parent->sysdata;
/*
* Set up the primary, secondary and subordinate
* bus numbers.
*/
child->number = child->secondary = busnr;
child->primary = parent->secondary;
child->subordinate = 0xff;
/* Set up default resource pointers.. */
for (i = 0; i < 4; i++)
child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES + i];
return child;
}
/*
* If it's a bridge, configure it and scan the bus behind it.
* For CardBus bridges, we don't scan behind as the devices will
* be handled by the bridge driver itself.
*
* We need to process bridges in two passes -- first we scan those
* already configured by the BIOS and after we are done with all of
* them, we proceed to assigning numbers to the remaining buses in
* order to avoid overlaps between old and new bus numbers.
*/
long pci_scan_bridge(struct pci_bus far * bus, struct pci_dev far * dev, long max,
long pass)
{
unsigned long buses;
unsigned short cr;
struct pci_bus far * child;
int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
#ifdef DEBUG_VERSION
safe_printf("Scanning behind PCI bridge %s, config %06x, pass %d\n",
dev->slot_name, buses & 0xffffff, pass);
#endif
if ((buses & 0xffff00) && !pcibios_assign_all_busses())
{
/*
* Bus already configured by firmware, process it in the first
* pass and just note the configuration.
*/
if (pass)
return max;
child = pci_add_new_bus(bus, dev, 0);
child->primary = buses & 0xFF;
child->secondary = (buses >> 8) & 0xFF;
child->subordinate = (buses >> 16) & 0xFF;
child->number = child->secondary;
if (!is_cardbus)
{
unsigned long cmax = pci_do_scan_bus(child);
if (cmax > max)
max = cmax;
}
else
{
unsigned long cmax = child->subordinate;
if (cmax > max)
max = cmax;
}
}
else
{
/*
* We need to assign a number to this bus which we always
* do in the second pass. We also keep all address decoders
* on the bridge disabled during scanning. FIXME: Why?
*/
if (!pass)
return max;
pci_read_config_word(dev, PCI_COMMAND, &cr);
pci_write_config_word(dev, PCI_COMMAND, 0x0000);
pci_write_config_word(dev, PCI_STATUS, 0xffff);
child = pci_add_new_bus(bus, dev, ++max);
buses = (buses & 0xff000000L)
| ((unsigned long)(child->primary))
| ((unsigned long)(child->secondary) * 256L)
| ((unsigned long)(child->subordinate) * 65536L);
/*
* We need to blast all three values with a single write.
*/
pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
if (!is_cardbus)
{
/* Now we can scan all subordinate buses... */
max = pci_do_scan_bus(child);
}
else
{
/*
* For CardBus bridges, we leave 4 bus numbers
* as cards with a PCI-to-PCI bridge can be
* inserted later.
*/
max += 3;
}
/*
* Set the subordinate bus number to its real value.
*/
child->subordinate = max;
pci_write_config_byte(dev,PCI_SUBORDINATE_BUS,(u8)max);
pci_write_config_word(dev,PCI_COMMAND,cr);
}
sprintf(child->name, (is_cardbus ? "PCI CardBus #%02x" : "PCI Bus #%02x"),
child->number);
return max;
}
unsigned long pci_do_scan_bus(struct pci_bus far *bus)
{
unsigned long devfn, max, pass;
struct list_head *ln;
struct pci_dev far * dev;
struct pci_dev dev0;
#ifdef DEBUG_VERSION
safe_printf("Scanning bus %02x\n", bus->number);
#endif
max = bus->secondary;
/* Create a device template */
memset(&dev0, 0, sizeof(struct pci_dev));
dev0.bus = bus;
dev0.sysdata = bus->sysdata;
/* Go find them, Rover! */
for (devfn = 0; devfn < 0x100; devfn += 8)
{
dev0.devfn = devfn;
pci_scan_slot(&dev0);
}
/*
* After performing arch-dependent fixup of the bus, look behind
* all PCI-to-PCI bridges on this bus.
*/
#ifdef DEBUG_VERSION
safe_printf("Fixups for bus %02x\n", bus->number);
#endif
pcibios_fixup_bus(bus);
for (pass=0; pass < 2; pass++)
for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next)
{
dev = pci_dev_b(ln);
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE
|| dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
max = pci_scan_bridge(bus, dev, max, pass);
}
/*
* We've scanned the bus and so we know all about what's on
* the other side of any bridges that may be on this bus plus
* any devices.
*
* Return how far we've got finding sub-buses.
*/
#ifdef DEBUG_VERSION
safe_printf("Bus scan for %02x returning with max=%02x\n", bus->number, max);
#endif
return max;
}
struct pci_bus far * pci_scan_bus(long bus, struct pci_ops *ops, void *sysdata)
{
struct pci_bus far * b = pci_alloc_primary_bus(bus);
if (b)
{
b->sysdata = sysdata;
b->ops = ops;
b->subordinate = pci_do_scan_bus(b);
}
return b;
}
/**
* pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
* @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices. If a PCI device is
* found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its
* device structure is returned. Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device on the global list.
*/
struct pci_dev far * pci_find_subsys(unsigned long vendor, unsigned long device,
unsigned long ss_vendor, unsigned long ss_device,
const struct pci_dev far * from)
{
struct list_head *n = from ? from->global_list.next : pci_devices.next;
while (n != &pci_devices) {
struct pci_dev *dev = pci_dev_g(n);
if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
(device == PCI_ANY_ID || dev->device == device) &&
(ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
(ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
return dev;
n = n->next;
}
return NULL;
}
/**
* pci_find_device - begin or continue searching for a PCI device by vendor/device id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices. If a PCI device is
* found with a matching @vendor and @device, a pointer to its device structure is
* returned. Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device on the global list.
*/
struct pci_dev far * pci_find_device(unsigned long vendor, unsigned long device,
const struct pci_dev far * from)
{
return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
}
/**
* pci_find_parent_resource - return resource region of parent bus of given region
* @dev: PCI device structure contains resources to be searched
* @res: child resource record for which parent is sought
*
* For given resource region of given device, return the resource
* region of parent bus the given region is contained in or where
* it should be allocated from.
*/
struct resource * far pci_find_parent_resource(const struct pci_dev far * dev,
struct resource far * res)
{
const struct pci_bus far * bus = dev->bus;
int i;
struct resource far * best = NULL;
for(i=0; i<4; i++)
{
struct resource far * r = bus->resource[i];
if (!r)
continue;
if (res->start && !(res->start >= r->start && res->end <= r->end))
continue; /* Not contained */
if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM))
continue; /* Wrong type */
if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH))
return r; /* Exact match */
if ((res->flags & IORESOURCE_PREFETCH) && !(r->flags & IORESOURCE_PREFETCH))
best = r; /* Approximating prefetchable by non-prefetchable */
}
return best;
}
long pci_claim_resource(struct pci_dev far * dev, long resource)
{
struct resource far * res = &dev->resource[resource];
struct resource far * root = pci_find_parent_resource(dev, res);
long err;
struct resource far * tmp;
err = -EINVAL;
if (root != NULL)
{
tmp = request_resource(root,res);
if(!tmp)
{
err = -EINVAL;
#ifdef DEBUG_VERSION
safe_printf("PCI: Address space collision on "
"region %d of device %s [%lx:%lx]\n",
(unsigned short)resource,dev->name,
res->start,res->end);
#endif
}
else
{
err = 0L;
}
}
else
{
#ifdef DEBUG_VERSION
safe_printf("PCI: No parent found for region %d "
"of device %s\n", resource, dev->name);
#endif
}
return err;
}
/**
* pci_find_slot - locate PCI device from a given PCI slot
* @bus: number of PCI bus on which desired PCI device resides
* @devfn: encodes number of PCI slot in which the desired PCI
* device resides and the logical device number within that slot
* in case of multi-function devices.
*
* Given a PCI bus and slot/function number, the desired PCI device
* is located in system global list of PCI devices. If the device
* is found, a pointer to its data structure is returned. If no
* device is found, %NULL is returned.
*/
struct pci_dev far * pci_find_slot(unsigned long bus, unsigned long devfn)
{
struct pci_dev far * dev;
pci_for_each_dev(dev)
{
if (dev->bus->number == bus && dev->devfn == devfn)
return dev;
}
return NULL;
}
struct irq_routing_table far * pcibios_get_irq_routing_table(void)
{
struct irq_routing_options opt;
struct irq_routing_options far * popt;
struct irq_routing_table far * rt = NULL;
unsigned res, map;
unsigned seg1;
void far * page;
if (!pci_bios_present)
return NULL;
page = (char far *)malloc((unsigned short)PAGE_SIZE);
if (!page)
return NULL;
opt.table = (struct irq_info far *) page;
opt.size = PAGE_SIZE;
opt.segment = 0xf000;
seg1 = opt.segment;
popt = &(opt);
#ifdef DEBUG_VERSION
safe_printf("PCI: Fetching IRQ routing table... \n");
#endif
_asm push ds
_asm push es
_asm push di
_asm mov ax,seg1
_asm mov ds,ax
_asm mov ah,PCI_FUNCTION_ID
_asm mov al,GET_IRQ_ROUTING_OPTIONS
_asm xor bx,bx
_asm les di,popt
_asm int 1ah
_asm pop di
_asm pop es
_asm pop ds
_asm mov res,ax
_asm jc return_null
_asm cmp ah,SUCCESSFUL
_asm jnz return_null
_asm mov map,bx
_asm jmp get_ok
return_null:
#ifdef DEBUG_VERSION
safe_printf("PCI: Error %02x when fetching IRQ routing table.\n", (res >> 8) & 0xff);
#endif
free(page);
return NULL;
get_ok:
#ifdef DEBUG_VERSION
safe_printf("OK ret=%d, size=%d, map=%x\n", res, opt.size, map);
#endif
rt = (struct irq_routing_table far * )malloc(
sizeof(struct irq_routing_table) + opt.size);
if(rt)
{
memset(rt, 0, sizeof(struct irq_routing_table));
rt->size = opt.size + sizeof(struct irq_routing_table);
rt->exclusive_irqs = map;
memcpy(rt->slots,page,opt.size);
#ifdef DEBUG_VERSION
safe_printf("PCI: Using BIOS Interrupt Routing Table\n");
#endif
}
free(page);
return rt;
}
/*
* Discover remaining PCI buses in case there are peer host bridges.
* We use the number of last PCI bus provided by the PCI BIOS.
*/
void pcibios_fixup_peer_bridges(void)
{
long n;
struct pci_bus bus;
struct pci_dev dev;
u16 l;
if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff)
return;
#ifdef DEBUG_VERSION
safe_printf("PCI: Peer bridge fixup\n");
#endif
for (n=0; n <= pcibios_last_bus; n++)
{
if (pci_bus_exists(&pci_root_buses, n))
continue;
bus.number = n;
bus.ops = pci_root_ops;
dev.bus = &bus;
for(dev.devfn=0; dev.devfn<256; dev.devfn += 8)
if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) &&
l != 0x0000 && l != 0xffff)
{
#ifdef DEBUG_VERSION
safe_printf("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l);
safe_printf("PCI: Discovered peer bus %02x\n", n);
#endif
pci_scan_bus(n, pci_root_ops, NULL);
break;
}
}
}
/* Return the conflict entry if you can't request it */
struct resource far * __request_resource(struct resource far * root,
struct resource far * new)
{
unsigned long start = new->start;
unsigned long end = new->end;
struct resource far * tmp;
struct resource far **p;
if (end < start)
return root;
if (start < root->start)
return root;
if (end > root->end)
return root;
p = &root->child;
for (;;)
{
tmp = *p;
if (!tmp || tmp->start > end)
{
new->sibling = tmp;
*p = new;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -