📄 pci.c
字号:
new->parent = root;
return NULL;
}
p = &tmp->sibling;
if (tmp->end < start)
continue;
return tmp;
}
}
struct resource far * request_resource(struct resource far * root,
struct resource far * new)
{
struct resource far * tmp;
local_irq_save(0);
tmp = __request_resource(root,new);
local_irq_restore(0);
return tmp;
}
long __release_resource(struct resource far * old)
{
struct resource far *tmp;
struct resource far **p;
p = &old->parent->child;
for (;;)
{
tmp = *p;
if (!tmp)
break;
if (tmp == old)
{
*p = tmp->sibling;
old->parent = NULL;
return 0;
}
p = &tmp->sibling;
}
return -EINVAL;
}
long release_resource(struct resource far * old)
{
long tmp;
local_irq_save(0);
tmp = __release_resource(old);
local_irq_restore(0);
return tmp;
}
/*
* Handle resources of PCI devices. If the world were perfect, we could
* just allocate all the resource regions and do nothing more. It isn't.
* On the other hand, we cannot just re-allocate all devices, as it would
* require us to know lots of host bridge internals. So we attempt to
* keep as much of the original configuration as possible, but tweak it
* when it's found to be wrong.
*
* Known BIOS problems we have to work around:
* - I/O or memory regions not configured
* - regions configured, but not enabled in the command register
* - bogus I/O addresses above 64K used
* - expansion ROMs left enabled (this may sound harmless, but given
* the fact the PCI specs explicitly allow address decoders to be
* shared between expansion ROMs and other resource regions, it's
* at least dangerous)
*
* Our solution:
* (1) Allocate resources for all buses behind PCI-to-PCI bridges.
* This gives us fixed barriers on where we can allocate.
* (2) Allocate resources for all enabled devices. If there is
* a collision, just mark the resource as unallocated. Also
* disable expansion ROMs during this step.
* (3) Try to allocate resources for disabled devices. If the
* resources were assigned correctly, everything goes well,
* if they weren't, they won't disturb allocation of other
* resources.
* (4) Assign new addresses to resources which were either
* not configured at all or misconfigured. If explicitly
* requested by the user, configure expansion ROM address
* as well.
*/
void pcibios_allocate_bus_resources(struct list_head *bus_list)
{
struct list_head far * ln;
struct pci_bus far * bus;
struct pci_dev far * dev;
int idx;
struct resource far * r;
struct resource far * pr;
/* Depth-First Search on bus tree */
for (ln=bus_list->next; ln != bus_list; ln=ln->next) {
bus = pci_bus_b(ln);
if ((dev = bus->self))
{
for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++)
{
r = &dev->resource[idx];
if (!r->start)
continue;
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0)
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Cannot allocate resource region %d of bridge %s\n",
idx, dev->slot_name);
#endif
r->start = 0L;
r->end = 0L;
}
}
}
pcibios_allocate_bus_resources(&bus->children);
}
}
void pcibios_allocate_resources(long pass)
{
struct pci_dev far * dev;
long idx, disabled;
u16 command;
struct resource far * r;
struct resource far * pr;
pci_for_each_dev(dev)
{
pci_read_config_word(dev, PCI_COMMAND, &command);
for(idx = 0; idx < 6; idx++)
{
r = &dev->resource[idx];
if (r->parent) /* Already allocated */
continue;
if (!r->start) /* Address not assigned at all */
continue;
if (r->flags & IORESOURCE_IO)
disabled = !(command & PCI_COMMAND_IO);
else
disabled = !(command & PCI_COMMAND_MEMORY);
if (pass == disabled)
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n",
r->start, r->end, r->flags, disabled, pass);
#endif
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0)
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Cannot allocate resource region %d of device %s\n", (unsigned short)idx,
dev->slot_name);
#endif
/* We'll assign a new address later */
r->end -= r->start;
r->start = 0;
}
}
}
if (!pass)
{
r = &dev->resource[PCI_ROM_RESOURCE];
if (r->flags & PCI_ROM_ADDRESS_ENABLE)
{
/* Turn the ROM off, leave the resource region, but keep it unregistered. */
u32 reg;
#ifdef DEBUG_VERSION
safe_printf("PCI: Switching off ROM of %s\n", dev->slot_name);
#endif
r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
pci_read_config_dword(dev, dev->rom_base_reg, ®);
pci_write_config_dword(dev, dev->rom_base_reg,
reg & ~PCI_ROM_ADDRESS_ENABLE);
}
}
}
}
/*
* Find empty slot in the resource tree given range and alignment.
*/
long find_resource(struct resource far * root, struct resource far * new,
unsigned long size,unsigned long min, unsigned long max,
unsigned long align,
void (*alignf)(void far *, struct resource far *, unsigned long),
void far * alignf_data)
{
unsigned long temp;
struct resource far * this = root->child;
new->start = root->start;
for(;;)
{
if (this)
new->end = this->start;
else
new->end = root->end;
if (new->start < min)
new->start = min;
if (new->end > max)
new->end = max;
new->start = (new->start + align - 1) & ~(align - 1);
if (alignf)
alignf(alignf_data, new, size);
if (new->start < new->end && new->end - new->start + 1 >= size)
{
temp = new->end;
new->end = new->start + size - 1;
return 0;
}
if (!this)
break;
new->start = this->end + 1;
this = this->sibling;
}
return -EBUSY;
}
/*
* Allocate empty slot in the resource tree given range and alignment.
*/
long allocate_resource(struct resource far * root, struct resource far * new,
unsigned long size,unsigned long min, unsigned long max,
unsigned long align,
void (*alignf)(void *, struct resource *, unsigned long),
void far * alignf_data)
{
long err;
local_irq_save(0);
err = find_resource(root, new, size, min, max, align, alignf, alignf_data);
local_irq_restore(0);
if (err >= 0 && request_resource(root, new))
err = -EBUSY;
return err;
}
void pcibios_update_resource(struct pci_dev far * dev, struct resource far * root,
struct resource far * res, long resource)
{
u32 new, check;
long reg;
new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
if (resource < 6)
{
reg = PCI_BASE_ADDRESS_0 + 4 * resource;
}
else if (resource == PCI_ROM_RESOURCE)
{
res->flags |= PCI_ROM_ADDRESS_ENABLE;
new |= PCI_ROM_ADDRESS_ENABLE;
reg = dev->rom_base_reg;
}
else
{
/* Somebody might have asked allocation of a non-standard resource */
return;
}
pci_write_config_dword(dev, reg, new);
pci_read_config_dword(dev, reg, &check);
if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO)
? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK))
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Error (%08lx != %08lx) while updating region %s %d \n",
new,check,dev->slot_name,(unsigned short)resource);
#endif
}
}
/*
* We need to avoid collisions with `mirrored' VGA ports
* and other strange ISA hardware, so we always want the
* addresses to be allocated in the 0x000-0x0ff region
* modulo 0x400.
*
* Why? Because some silly external IO cards only decode
* the low 10 bits of the IO address. The 0x00-0xff region
* is reserved for motherboard devices that decode all 16
* bits, so it's ok to allocate at, say, 0x2800-0x28ff,
* but we want to try to avoid allocating at 0x2900-0x2bff
* which might have be mirrored at 0x0100-0x03ff..
*/
void pcibios_align_resource(void far * data, struct resource far * res,
unsigned long size)
{
if (res->flags & IORESOURCE_IO)
{
unsigned long start = res->start;
if (start & 0x300)
{
start = (start + 0x3ff) & ~0x3ff;
res->start = start;
}
}
}
/*
* Given the PCI bus a device resides on, try to
* find an acceptable resource allocation for a
* specific device resource..
*/
long pci_assign_bus_resource(const struct pci_bus far * bus,struct pci_dev far * dev,
struct resource far * res,unsigned long size,unsigned long min,
unsigned long type_mask,long resno)
{
long i;
unsigned long j;
type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
for (i = 0 ; i < 4; i++)
{
struct resource *r = bus->resource[i];
if (!r)
continue;
/* type_mask must match */
if ((res->flags ^ r->flags) & type_mask)
continue;
/* We cannot allocate a non-prefetching resource from a pre-fetching area */
if ((r->flags & IORESOURCE_PREFETCH) && !(res->flags & IORESOURCE_PREFETCH))
continue;
/* Ok, try it out.. */
if (allocate_resource(r, res, size, min, -1, size, pcibios_align_resource, dev)
< 0)
continue;
/* Update PCI config space. */
pcibios_update_resource(dev, r, res, resno);
return 0;
}
return -EBUSY;
}
long pci_assign_resource(struct pci_dev far * dev, long i)
{
const struct pci_bus far * bus = dev->bus;
struct resource far * res = dev->resource + i;
unsigned long size, min;
char far * name;
size = res->end - res->start + 1;
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
/* First, try exact prefetching match.. */
if (pci_assign_bus_resource(bus, dev, res, size, min, IORESOURCE_PREFETCH, i) < 0)
{
/*
* That failed.
*
* But a prefetching area can handle a non-prefetching
* window (it will just not perform as well).
*/
if (!(res->flags & IORESOURCE_PREFETCH) ||
pci_assign_bus_resource(bus, dev, res, size, min, 0, i) < 0)
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Failed to allocate resource %d(%lx-%lx) for %s\n",
(unsigned short)i, res->start, res->end, dev->slot_name);
#endif
return -EBUSY;
}
}
name = (char far *)(&(dev->name));
#ifdef DEBUG_VERSION
safe_printf(" got res[%lx:%lx] for resource %d of %s\n", res->start,
res->end, (unsigned short)i,
name);
#endif
return 0;
}
void pcibios_assign_resources(void)
{
struct pci_dev far * dev;
long idx;
struct resource far * r;
pci_for_each_dev(dev)
{
long class = dev->class >> 8;
/* Don't touch classless devices and host bridges */
if (!class || class == PCI_CLASS_BRIDGE_HOST)
continue;
for(idx=0; idx<6; idx++)
{
r = &dev->resource[idx];
/*
* Don't touch IDE controllers and I/O ports of video cards!
*/
if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) ||
(class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO)))
continue;
/*
* We shall assign a new address to this resource, either because
* the BIOS forgot to do so or because we have decided the old
* address was unusable for some reason.
*/
if (!r->start && r->end)
pci_assign_resource(dev, idx);
}
if (pci_probe & PCI_ASSIGN_ROMS)
{
r = &dev->resource[PCI_ROM_RESOURCE];
r->end -= r->start;
r->start = 0;
if (r->end)
pci_assign_resource(dev, PCI_ROM_RESOURCE);
}
}
}
void pcibios_resource_survey(void)
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Allocating resources\n");
#endif
pcibios_allocate_bus_resources(&pci_root_buses);
pcibios_allocate_resources(0);
pcibios_allocate_resources(1);
pcibios_assign_resources();
}
/*
* Sort the device list according to PCI BIOS. Nasty hack, but since some
* fool forgot to define the `correct' device order in the PCI BIOS specs
* and we want to be (possibly bug-to-bug ;-]) compatible with older kernels
* which used BIOS ordering, we are bound to do this...
*/
void pcibios_sort(void)
{
LIST_HEAD(sorted_devices);
struct list_head *ln;
struct pci_dev far * dev;
struct pci_dev far * d;
long idx, found;
unsigned char bus, devfn;
#ifdef DEBUG_VERSION
safe_printf("PCI: Sorting device list...\n");
#endif
while (!list_empty(&pci_devices))
{
ln = pci_devices.next;
dev = pci_dev_g(ln);
idx = found = 0;
while (pci_bios_find_device(dev->vendor,dev->device,(unsigned short)idx,&bus,
&devfn) == (long)SUCCESSFUL)
{
idx++;
for (ln= pci_devices.next; ln != &pci_devices; ln=ln->next)
{
d = pci_dev_g(ln);
if (d->bus->number == bus && d->devfn == devfn)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -