📄 pci.c
字号:
#include <stdio.h>
#include <stdlib.h>
#include "typedef.h"
#include "low.h"
#include "list.h"
#include "pci.h"
#ifdef CONFIG_PM
extern short pm_active;
#endif
extern unsigned long pcibios_max_latency;
extern struct list_head pci_root_buses;
extern struct list_head pci_devices;
extern struct list_head pci_drivers;
int pci_bus_exists(const struct list_head *list, long nr)
{
const struct list_head *l;
for(l=list->next; l != list; l = l->next) {
const struct pci_bus *b = pci_bus_b(l);
if (b->number == nr || pci_bus_exists(&b->children, nr))
return 1;
}
return 0;
}
struct pci_bus far * pci_alloc_bus(void)
{
struct pci_bus far *b;
b = (struct pci_bus far *)malloc(sizeof(struct pci_bus));
if (b)
{
memset(b, 0, sizeof(struct pci_bus));
INIT_LIST_HEAD(&b->children);
INIT_LIST_HEAD(&b->devices);
}
return b;
}
struct pci_bus far * pci_alloc_primary_bus(long bus)
{
struct pci_bus far *b;
if (pci_bus_exists(&pci_root_buses, bus))
{
/* If we already got to this bus through a different bridge, ignore it */
#ifdef DEBUG_VERSION
safe_printf("PCI: Bus %02x already known\n", bus);
#endif
return NULL;
}
b = pci_alloc_bus();
list_add_tail(&b->node, &pci_root_buses);
b->number = b->secondary = bus;
b->resource[0] = &ioport_resource;
b->resource[1] = &iomem_resource;
return b;
}
/*
* Read interrupt line and base address registers.
* The architecture-dependent code can tweak these, of course.
*/
void pci_read_irq(struct pci_dev far *dev)
{
u8 irq;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
if (irq)
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
dev->irq = irq;
}
/*
* Find the extent of a PCI decode..
*/
u32 pci_size(u32 base, unsigned long mask)
{
u32 size = mask & base; /* Find the significant bits */
size = size & ~(size-1); /* Get the lowest of them to find the decode size */
return size - 1; /* extent = size - 1 */
}
unsigned long pci_calc_resource_flags(unsigned long flags)
{
if (flags & PCI_BASE_ADDRESS_SPACE_IO)
return IORESOURCE_IO;
if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
return IORESOURCE_MEM | IORESOURCE_PREFETCH;
return IORESOURCE_MEM;
}
void pci_read_bases(struct pci_dev far *dev, unsigned long howmany, long rom)
{
unsigned long pos, reg, next;
u32 l, sz;
struct resource *res;
for(pos=0; pos< howmany; pos = next)
{
next = pos+1;
res = &dev->resource[pos];
res->name = dev->name;
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
pci_read_config_dword(dev, reg, &l);
pci_write_config_dword(dev, reg, ~0L);
pci_read_config_dword(dev, reg, &sz);
pci_write_config_dword(dev, reg, l);
if (!sz || sz == 0xffffffff)
continue;
if (l == 0xffffffff)
l = 0;
if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY)
{
res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
sz = pci_size(sz, PCI_BASE_ADDRESS_MEM_MASK);
}
else
{
res->start = l & PCI_BASE_ADDRESS_IO_MASK;
sz = pci_size(sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff);
}
res->end = res->start + (unsigned long) sz;
res->flags |= (l & 0xf) | pci_calc_resource_flags(l);
if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK))
== (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64))
{
pci_read_config_dword(dev, reg+4, &l);
next++;
if (l)
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Unable to handle 64-bit address for device %s\n",
dev->slot_name);
#endif
res->start = 0;
res->flags = 0;
continue;
}
}
}
if (rom)
{
dev->rom_base_reg = rom;
res = &dev->resource[PCI_ROM_RESOURCE];
pci_read_config_dword(dev, rom, &l);
pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE);
pci_read_config_dword(dev, rom, &sz);
pci_write_config_dword(dev, rom, l);
if (l == 0xffffffff)
l = 0;
if (sz && sz != 0xffffffff)
{
res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
res->start = l & PCI_ROM_ADDRESS_MASK;
sz = pci_size(sz, PCI_ROM_ADDRESS_MASK);
res->end = res->start + (unsigned long) sz;
}
res->name = dev->name;
}
}
long pci_setup_device(struct pci_dev far * dev)
{
u32 class;
sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
sprintf(dev->name, "PCI device %04x:%04x", dev->vendor, dev->device);
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
class >>= 8; /* upper 3 bytes */
dev->class = class;
class >>= 8;
#ifdef DEBUG_VERSION
safe_printf("Found %02x:%02x.%d [%04x/%04x] %06x %02x\n", dev->bus->number,
PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn),dev->vendor,
dev->device,class,dev->hdr_type);
#endif
/* "Unknown power state" */
dev->current_state = 4;
switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
break;
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
if (class != PCI_CLASS_BRIDGE_PCI)
goto bad;
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
if (class != PCI_CLASS_BRIDGE_CARDBUS)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 1, 0);
pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
break;
default: /* unknown header */
#ifdef DEBUG_VERSION
safe_printf("PCI: device %s has unknown header type %02x, ignoring.\n",
dev->slot_name, dev->hdr_type);
#endif
return -1;
bad:
#ifdef DEBUG_VERSION
safe_printf("PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n",
dev->slot_name, class, dev->hdr_type);
#endif
dev->class = PCI_CLASS_NOT_DEFINED;
}
return 0;
}
/*
* Read the config data for a PCI device, sanity-check it
* and fill in the dev structure...
*/
struct pci_dev far * pci_scan_device(struct pci_dev far *temp)
{
struct pci_dev far *dev;
u32 l;
if (pci_read_config_dword(temp, PCI_VENDOR_ID, &l))
return NULL;
/* some broken boards return 0 or ~0 if a slot is empty: */
if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000)
return NULL;
dev = (struct pci_dev far *)malloc(sizeof(struct pci_dev));
if (!dev)
return NULL;
memcpy(dev, temp, sizeof(struct pci_dev));
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
/*Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
set this higher, assuming the system even supports it. */
dev->dma_mask.Low = 0xffffffff;
if (pci_setup_device(dev) < 0)
{
free(dev);
dev = NULL;
}
return dev;
}
struct pci_dev far * pci_scan_slot(struct pci_dev far *temp)
{
struct pci_bus far *bus = temp->bus;
struct pci_dev far *dev;
struct pci_dev far *first_dev = NULL;
long func = 0;
long is_multi = 0;
u8 hdr_type;
for (func = 0; func < 8; func++, temp->devfn++)
{
if (func && !is_multi) /* not a multi-function device */
continue;
if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
continue;
temp->hdr_type = hdr_type & 0x7f;
dev = pci_scan_device(temp);
if (!dev)
continue;
/*pci_name_device(dev);*/
if (!func)
{
is_multi = hdr_type & 0x80;
first_dev = dev;
}
/*
* Link the device to both the global PCI device chain and
* the per-bus list of devices.
*/
list_add_tail(&dev->global_list, &pci_devices);
list_add_tail(&dev->bus_list, &bus->devices);
/* Fix up broken headers */
pci_fixup_device(PCI_FIXUP_HEADER, dev);
}
return first_dev;
}
/*
* Several buggy motherboards address only 16 devices and mirror
* them to next 16 IDs. We try to detect this `feature' on all
* primary buses (those containing host bridges as they are
* expected to be unique) and remove the ghost devices.
*/
void pcibios_fixup_ghosts(struct pci_bus far * b)
{
struct list_head *ln, *mn;
struct pci_dev far * d;
struct pci_dev far * e;
long mirror = PCI_DEVFN(16,0);
long seen_host_bridge = 0;
long i;
#ifdef DEBUG_VERSION
safe_printf("PCI: Scanning for ghost devices on bus %lx\n", b->number);
#endif
for (ln=b->devices.next; ln != &b->devices; ln=ln->next)
{
d = pci_dev_b(ln);
if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
seen_host_bridge++;
for (mn=ln->next; mn != &b->devices; mn=mn->next)
{
e = pci_dev_b(mn);
if (e->devfn != d->devfn + mirror ||
e->vendor != d->vendor ||
e->device != d->device ||
e->class != d->class)
continue;
for(i=0; i<PCI_NUM_RESOURCES; i++)
if (e->resource[i].start != d->resource[i].start ||
e->resource[i].end != d->resource[i].end ||
e->resource[i].flags != d->resource[i].flags)
continue;
break;
}
if (mn == &b->devices)
return;
}
if (!seen_host_bridge)
return;
#ifdef DEBUG_VERSION
safe_printf("PCI: Ignoring ghost devices on bus %02x\n", b->number);
#endif
ln = &b->devices;
while (ln->next != &b->devices)
{
d = pci_dev_b(ln->next);
if (d->devfn >= mirror)
{
list_del(&d->global_list);
list_del(&d->bus_list);
free(d);
}
else
ln = ln->next;
}
}
void pci_read_bridge_bases(struct pci_bus far * child)
{
struct pci_dev far * dev = child->self;
u8 io_base_lo, io_limit_lo;
u16 mem_base_lo, mem_limit_lo;
unsigned long base, limit;
struct resource far * res;
int i;
if (!dev) /* It's a host bus, nothing to read */
return;
for(i=0; i<3; i++)
child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
res = child->resource[0];
pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
base = ((unsigned long)(io_base_lo & PCI_IO_RANGE_MASK)) * 256L ;
limit = ((unsigned long)(io_limit_lo & PCI_IO_RANGE_MASK)) * 256L;
if ((base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32)
{
u16 io_base_hi, io_limit_hi;
pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);
pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);
base |= ((unsigned long)(io_base_hi) * 65536L);
limit |= ((unsigned long)(io_limit_hi) * 65536L);
}
if (base && base <= limit)
{
res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
res->start = base;
res->end = limit + 0xfff;
res->name = child->name;
}
else
{
/*
* Ugh. We don't know enough about this bridge. Just assume
* that it's entirely transparent.
*/
#ifdef DEBUG_VERSION
safe_printf("Unknown bridge resource %d: assuming transparent\n",0);
#endif
child->resource[0] = child->parent->resource[0];
}
res = child->resource[1];
pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
base = ((unsigned long)(mem_base_lo & PCI_MEMORY_RANGE_MASK)) * 65536L;
limit = ((unsigned long)(mem_limit_lo & PCI_MEMORY_RANGE_MASK)) * 65536L;
if (base && base <= limit)
{
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
res->start = base;
res->end = limit + 0xfffff;
res->name = child->name;
}
else
{
/* See comment above. Same thing */
#ifdef DEBUG_VERSION
safe_printf("Unknown bridge resource %d: assuming transparent\n",1);
#endif
child->resource[1] = child->parent->resource[1];
}
res = child->resource[2];
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
base = ((unsigned long)(mem_base_lo & PCI_PREF_RANGE_MASK)) * 65536L;
limit = ((unsigned long)(mem_limit_lo & PCI_PREF_RANGE_MASK)) * 65536L;
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64)
{
u32 mem_base_hi, mem_limit_hi;
pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi);
pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi);
if (mem_base_hi || mem_limit_hi)
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Unable to handle 64-bit address space for %s\n",child->name);
#endif
return;
}
}
if (base && base <= limit)
{
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH;
res->start = base;
res->end = limit + 0xfffff;
res->name = child->name;
}
else
{
/* See comments above */
#ifdef DEBUG_VERSION
safe_printf("Unknown bridge resource %d: assuming transparent\n",2);
#endif
child->resource[2] = child->parent->resource[2];
}
}
void pcibios_fixup_bus(struct pci_bus far * b)
{
pcibios_fixup_ghosts(b);
pci_read_bridge_bases(b);
}
unsigned long pcibios_assign_all_busses(void)
{
return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -