mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-03 15:53:54 -06:00
Merge branch 'pci' into for_anthony
This commit is contained in:
commit
b907b69dd7
26 changed files with 2606 additions and 276 deletions
322
hw/pci.c
322
hw/pci.c
|
@ -23,6 +23,10 @@
|
|||
*/
|
||||
#include "hw.h"
|
||||
#include "pci.h"
|
||||
#include "pci_bridge.h"
|
||||
#include "pci_internals.h"
|
||||
#include "msix.h"
|
||||
#include "msi.h"
|
||||
#include "monitor.h"
|
||||
#include "net.h"
|
||||
#include "sysemu.h"
|
||||
|
@ -37,31 +41,10 @@
|
|||
# define PCI_DPRINTF(format, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
struct PCIBus {
|
||||
BusState qbus;
|
||||
int devfn_min;
|
||||
pci_set_irq_fn set_irq;
|
||||
pci_map_irq_fn map_irq;
|
||||
pci_hotplug_fn hotplug;
|
||||
DeviceState *hotplug_qdev;
|
||||
void *irq_opaque;
|
||||
PCIDevice *devices[256];
|
||||
PCIDevice *parent_dev;
|
||||
target_phys_addr_t mem_base;
|
||||
|
||||
QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */
|
||||
QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */
|
||||
|
||||
/* The bus IRQ state is the logical OR of the connected devices.
|
||||
Keep a count of the number of devices with raised IRQs. */
|
||||
int nirq;
|
||||
int *irq_count;
|
||||
};
|
||||
|
||||
static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
|
||||
static char *pcibus_get_dev_path(DeviceState *dev);
|
||||
|
||||
static struct BusInfo pci_bus_info = {
|
||||
struct BusInfo pci_bus_info = {
|
||||
.name = "PCI",
|
||||
.size = sizeof(PCIBus),
|
||||
.print_dev = pcibus_dev_print,
|
||||
|
@ -157,9 +140,9 @@ static void pci_device_reset(PCIDevice *dev)
|
|||
dev->irq_state = 0;
|
||||
pci_update_irq_status(dev);
|
||||
/* Clear all writeable bits */
|
||||
pci_set_word(dev->config + PCI_COMMAND,
|
||||
pci_get_word(dev->config + PCI_COMMAND) &
|
||||
~pci_get_word(dev->wmask + PCI_COMMAND));
|
||||
pci_word_test_and_clear_mask(dev->config + PCI_COMMAND,
|
||||
pci_get_word(dev->wmask + PCI_COMMAND) |
|
||||
pci_get_word(dev->w1cmask + PCI_COMMAND));
|
||||
dev->config[PCI_CACHE_LINE_SIZE] = 0x0;
|
||||
dev->config[PCI_INTERRUPT_LINE] = 0x0;
|
||||
for (r = 0; r < PCI_NUM_REGIONS; ++r) {
|
||||
|
@ -293,26 +276,6 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name,
|
|||
return bus;
|
||||
}
|
||||
|
||||
static void pci_register_secondary_bus(PCIBus *parent,
|
||||
PCIBus *bus,
|
||||
PCIDevice *dev,
|
||||
pci_map_irq_fn map_irq,
|
||||
const char *name)
|
||||
{
|
||||
qbus_create_inplace(&bus->qbus, &pci_bus_info, &dev->qdev, name);
|
||||
bus->map_irq = map_irq;
|
||||
bus->parent_dev = dev;
|
||||
|
||||
QLIST_INIT(&bus->child);
|
||||
QLIST_INSERT_HEAD(&parent->child, bus, sibling);
|
||||
}
|
||||
|
||||
static void pci_unregister_secondary_bus(PCIBus *bus)
|
||||
{
|
||||
assert(QLIST_EMPTY(&bus->child));
|
||||
QLIST_REMOVE(bus, sibling);
|
||||
}
|
||||
|
||||
int pci_bus_num(PCIBus *s)
|
||||
{
|
||||
if (!s->parent_dev)
|
||||
|
@ -331,7 +294,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
|
|||
|
||||
qemu_get_buffer(f, config, size);
|
||||
for (i = 0; i < size; ++i) {
|
||||
if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i]) {
|
||||
if ((config[i] ^ s->config[i]) &
|
||||
s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) {
|
||||
qemu_free(config);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -464,15 +428,18 @@ static void pci_set_default_subsystem_id(PCIDevice *pci_dev)
|
|||
}
|
||||
|
||||
/*
|
||||
* Parse [[<domain>:]<bus>:]<slot>, return -1 on error
|
||||
* Parse [[<domain>:]<bus>:]<slot>, return -1 on error if funcp == NULL
|
||||
* [[<domain>:]<bus>:]<slot>.<func>, return -1 on error
|
||||
*/
|
||||
static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp)
|
||||
int pci_parse_devaddr(const char *addr, int *domp, int *busp,
|
||||
unsigned int *slotp, unsigned int *funcp)
|
||||
{
|
||||
const char *p;
|
||||
char *e;
|
||||
unsigned long val;
|
||||
unsigned long dom = 0, bus = 0;
|
||||
unsigned slot = 0;
|
||||
unsigned int slot = 0;
|
||||
unsigned int func = 0;
|
||||
|
||||
p = addr;
|
||||
val = strtoul(p, &e, 16);
|
||||
|
@ -494,11 +461,24 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s
|
|||
}
|
||||
}
|
||||
|
||||
if (dom > 0xffff || bus > 0xff || val > 0x1f)
|
||||
return -1;
|
||||
|
||||
slot = val;
|
||||
|
||||
if (funcp != NULL) {
|
||||
if (*e != '.')
|
||||
return -1;
|
||||
|
||||
p = e + 1;
|
||||
val = strtoul(p, &e, 16);
|
||||
if (e == p)
|
||||
return -1;
|
||||
|
||||
func = val;
|
||||
}
|
||||
|
||||
/* if funcp == NULL func is 0 */
|
||||
if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7)
|
||||
return -1;
|
||||
|
||||
if (*e)
|
||||
return -1;
|
||||
|
||||
|
@ -509,6 +489,8 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s
|
|||
*domp = dom;
|
||||
*busp = bus;
|
||||
*slotp = slot;
|
||||
if (funcp != NULL)
|
||||
*funcp = func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -519,7 +501,7 @@ int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
|
|||
if (!strncmp(addr, "pci_addr=", 9)) {
|
||||
addr += 9;
|
||||
}
|
||||
if (pci_parse_devaddr(addr, domp, busp, slotp)) {
|
||||
if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) {
|
||||
monitor_printf(mon, "Invalid pci address\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -536,7 +518,7 @@ PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
|
|||
return pci_find_bus(pci_find_root_bus(0), 0);
|
||||
}
|
||||
|
||||
if (pci_parse_devaddr(devaddr, &dom, &bus, &slot) < 0) {
|
||||
if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -649,6 +631,7 @@ static void pci_config_alloc(PCIDevice *pci_dev)
|
|||
pci_dev->config = qemu_mallocz(config_size);
|
||||
pci_dev->cmask = qemu_mallocz(config_size);
|
||||
pci_dev->wmask = qemu_mallocz(config_size);
|
||||
pci_dev->w1cmask = qemu_mallocz(config_size);
|
||||
pci_dev->used = qemu_mallocz(config_size);
|
||||
}
|
||||
|
||||
|
@ -657,6 +640,7 @@ static void pci_config_free(PCIDevice *pci_dev)
|
|||
qemu_free(pci_dev->config);
|
||||
qemu_free(pci_dev->cmask);
|
||||
qemu_free(pci_dev->wmask);
|
||||
qemu_free(pci_dev->w1cmask);
|
||||
qemu_free(pci_dev->used);
|
||||
}
|
||||
|
||||
|
@ -780,16 +764,15 @@ static int pci_unregister_device(DeviceState *dev)
|
|||
}
|
||||
|
||||
void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||
pcibus_t size, int type,
|
||||
pcibus_t size, uint8_t type,
|
||||
PCIMapIORegionFunc *map_func)
|
||||
{
|
||||
PCIIORegion *r;
|
||||
uint32_t addr;
|
||||
pcibus_t wmask;
|
||||
|
||||
if ((unsigned int)region_num >= PCI_NUM_REGIONS)
|
||||
return;
|
||||
uint64_t wmask;
|
||||
|
||||
assert(region_num >= 0);
|
||||
assert(region_num < PCI_NUM_REGIONS);
|
||||
if (size & (size-1)) {
|
||||
fprintf(stderr, "ERROR: PCI region size must be pow2 "
|
||||
"type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size);
|
||||
|
@ -820,75 +803,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
|||
}
|
||||
}
|
||||
|
||||
static uint32_t pci_config_get_io_base(PCIDevice *d,
|
||||
uint32_t base, uint32_t base_upper16)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8;
|
||||
if (d->config[base] & PCI_IO_RANGE_TYPE_32) {
|
||||
val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static pcibus_t pci_config_get_memory_base(PCIDevice *d, uint32_t base)
|
||||
{
|
||||
return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK)
|
||||
<< 16;
|
||||
}
|
||||
|
||||
static pcibus_t pci_config_get_pref_base(PCIDevice *d,
|
||||
uint32_t base, uint32_t upper)
|
||||
{
|
||||
pcibus_t tmp;
|
||||
pcibus_t val;
|
||||
|
||||
tmp = (pcibus_t)pci_get_word(d->config + base);
|
||||
val = (tmp & PCI_PREF_RANGE_MASK) << 16;
|
||||
if (tmp & PCI_PREF_RANGE_TYPE_64) {
|
||||
val |= (pcibus_t)pci_get_long(d->config + upper) << 32;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static pcibus_t pci_bridge_get_base(PCIDevice *bridge, uint8_t type)
|
||||
{
|
||||
pcibus_t base;
|
||||
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
base = pci_config_get_io_base(bridge,
|
||||
PCI_IO_BASE, PCI_IO_BASE_UPPER16);
|
||||
} else {
|
||||
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
|
||||
base = pci_config_get_pref_base(
|
||||
bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32);
|
||||
} else {
|
||||
base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE);
|
||||
}
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
static pcibus_t pci_bridge_get_limit(PCIDevice *bridge, uint8_t type)
|
||||
{
|
||||
pcibus_t limit;
|
||||
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
limit = pci_config_get_io_base(bridge,
|
||||
PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16);
|
||||
limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */
|
||||
} else {
|
||||
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
|
||||
limit = pci_config_get_pref_base(
|
||||
bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32);
|
||||
} else {
|
||||
limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT);
|
||||
}
|
||||
limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size,
|
||||
uint8_t type)
|
||||
{
|
||||
|
@ -1089,7 +1003,10 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
|
|||
|
||||
for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) {
|
||||
uint8_t wmask = d->wmask[addr + i];
|
||||
uint8_t w1cmask = d->w1cmask[addr + i];
|
||||
assert(!(wmask & w1cmask));
|
||||
d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
|
||||
d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
|
||||
}
|
||||
if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
|
||||
ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
|
||||
|
@ -1121,6 +1038,23 @@ static void pci_set_irq(void *opaque, int irq_num, int level)
|
|||
pci_change_irq_level(pci_dev, irq_num, change);
|
||||
}
|
||||
|
||||
bool pci_msi_enabled(PCIDevice *dev)
|
||||
{
|
||||
return msix_enabled(dev) || msi_enabled(dev);
|
||||
}
|
||||
|
||||
void pci_msi_notify(PCIDevice *dev, unsigned int vector)
|
||||
{
|
||||
if (msix_enabled(dev)) {
|
||||
msix_notify(dev, vector);
|
||||
} else if (msi_enabled(dev)) {
|
||||
msi_notify(dev, vector);
|
||||
} else {
|
||||
/* MSI/MSI-X must be enabled */
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* monitor info on PCI */
|
||||
|
||||
|
@ -1534,20 +1468,12 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
|
|||
return res;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
PCIDevice dev;
|
||||
PCIBus bus;
|
||||
uint32_t vid;
|
||||
uint32_t did;
|
||||
} PCIBridge;
|
||||
|
||||
|
||||
static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d)
|
||||
{
|
||||
pci_update_mappings(d);
|
||||
}
|
||||
|
||||
static void pci_bridge_update_mappings(PCIBus *b)
|
||||
void pci_bridge_update_mappings(PCIBus *b)
|
||||
{
|
||||
PCIBus *child;
|
||||
|
||||
|
@ -1558,23 +1484,6 @@ static void pci_bridge_update_mappings(PCIBus *b)
|
|||
}
|
||||
}
|
||||
|
||||
static void pci_bridge_write_config(PCIDevice *d,
|
||||
uint32_t address, uint32_t val, int len)
|
||||
{
|
||||
pci_default_write_config(d, address, val, len);
|
||||
|
||||
if (/* io base/limit */
|
||||
ranges_overlap(address, len, PCI_IO_BASE, 2) ||
|
||||
|
||||
/* memory base/limit, prefetchable base/limit and
|
||||
io base/limit upper 16 */
|
||||
ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) {
|
||||
PCIBridge *s = container_of(d, PCIBridge, dev);
|
||||
PCIBus *secondary_bus = &s->bus;
|
||||
pci_bridge_update_mappings(secondary_bus);
|
||||
}
|
||||
}
|
||||
|
||||
PCIBus *pci_find_bus(PCIBus *bus, int bus_num)
|
||||
{
|
||||
PCIBus *sec;
|
||||
|
@ -1618,54 +1527,6 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function)
|
|||
return bus->devices[PCI_DEVFN(slot, function)];
|
||||
}
|
||||
|
||||
static int pci_bridge_initfn(PCIDevice *dev)
|
||||
{
|
||||
PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev);
|
||||
|
||||
pci_config_set_vendor_id(s->dev.config, s->vid);
|
||||
pci_config_set_device_id(s->dev.config, s->did);
|
||||
|
||||
pci_set_word(dev->config + PCI_STATUS,
|
||||
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
|
||||
pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI);
|
||||
dev->config[PCI_HEADER_TYPE] =
|
||||
(dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
|
||||
PCI_HEADER_TYPE_BRIDGE;
|
||||
pci_set_word(dev->config + PCI_SEC_STATUS,
|
||||
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_bridge_exitfn(PCIDevice *pci_dev)
|
||||
{
|
||||
PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev);
|
||||
PCIBus *bus = &s->bus;
|
||||
pci_unregister_secondary_bus(bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction,
|
||||
uint16_t vid, uint16_t did,
|
||||
pci_map_irq_fn map_irq, const char *name)
|
||||
{
|
||||
PCIDevice *dev;
|
||||
PCIBridge *s;
|
||||
|
||||
dev = pci_create_multifunction(bus, devfn, multifunction, "pci-bridge");
|
||||
qdev_prop_set_uint32(&dev->qdev, "vendorid", vid);
|
||||
qdev_prop_set_uint32(&dev->qdev, "deviceid", did);
|
||||
qdev_init_nofail(&dev->qdev);
|
||||
|
||||
s = DO_UPCAST(PCIBridge, dev, dev);
|
||||
pci_register_secondary_bus(bus, &s->bus, &s->dev, map_irq, name);
|
||||
return &s->bus;
|
||||
}
|
||||
|
||||
PCIDevice *pci_bridge_get_device(PCIBus *bus)
|
||||
{
|
||||
return bus->parent_dev;
|
||||
}
|
||||
|
||||
static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
|
||||
{
|
||||
PCIDevice *pci_dev = (PCIDevice *)qdev;
|
||||
|
@ -1696,7 +1557,8 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
|
|||
pci_dev->romfile = qemu_strdup(info->romfile);
|
||||
pci_add_option_rom(pci_dev);
|
||||
|
||||
if (qdev->hotplugged) {
|
||||
if (bus->hotplug) {
|
||||
/* lower layer must check qdev->hotplugged */
|
||||
rc = bus->hotplug(bus->hotplug_qdev, pci_dev, 1);
|
||||
if (rc != 0) {
|
||||
int r = pci_unregister_device(&pci_dev->qdev);
|
||||
|
@ -1864,11 +1726,25 @@ static void pci_del_option_rom(PCIDevice *pdev)
|
|||
pdev->rom_offset = 0;
|
||||
}
|
||||
|
||||
/* Reserve space and add capability to the linked list in pci config space */
|
||||
int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
|
||||
uint8_t offset, uint8_t size)
|
||||
/*
|
||||
* if !offset
|
||||
* Reserve space and add capability to the linked list in pci config space
|
||||
*
|
||||
* if offset = 0,
|
||||
* Find and reserve space and add capability to the linked list
|
||||
* in pci config space */
|
||||
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
|
||||
uint8_t offset, uint8_t size)
|
||||
{
|
||||
uint8_t *config = pdev->config + offset;
|
||||
uint8_t *config;
|
||||
if (!offset) {
|
||||
offset = pci_find_space(pdev, size);
|
||||
if (!offset) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
}
|
||||
|
||||
config = pdev->config + offset;
|
||||
config[PCI_CAP_LIST_ID] = cap_id;
|
||||
config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST];
|
||||
pdev->config[PCI_CAPABILITY_LIST] = offset;
|
||||
|
@ -1881,17 +1757,6 @@ int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
|
|||
return offset;
|
||||
}
|
||||
|
||||
/* Find and reserve space and add capability to the linked list
|
||||
* in pci config space */
|
||||
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
|
||||
{
|
||||
uint8_t offset = pci_find_space(pdev, size);
|
||||
if (!offset) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
return pci_add_capability_at_offset(pdev, cap_id, offset, size);
|
||||
}
|
||||
|
||||
/* Unlink capability from the pci config space. */
|
||||
void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
|
||||
{
|
||||
|
@ -1901,6 +1766,7 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
|
|||
pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT];
|
||||
/* Make capability writeable again */
|
||||
memset(pdev->wmask + offset, 0xff, size);
|
||||
memset(pdev->w1cmask + offset, 0, size);
|
||||
/* Clear cmask as device-specific registers can't be checked */
|
||||
memset(pdev->cmask + offset, 0, size);
|
||||
memset(pdev->used + offset, 0, size);
|
||||
|
@ -1971,23 +1837,3 @@ static char *pcibus_get_dev_path(DeviceState *dev)
|
|||
return strdup(path);
|
||||
}
|
||||
|
||||
static PCIDeviceInfo bridge_info = {
|
||||
.qdev.name = "pci-bridge",
|
||||
.qdev.size = sizeof(PCIBridge),
|
||||
.init = pci_bridge_initfn,
|
||||
.exit = pci_bridge_exitfn,
|
||||
.config_write = pci_bridge_write_config,
|
||||
.is_bridge = 1,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0),
|
||||
DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
}
|
||||
};
|
||||
|
||||
static void pci_register_devices(void)
|
||||
{
|
||||
pci_qdev_register(&bridge_info);
|
||||
}
|
||||
|
||||
device_init(pci_register_devices)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue