pci: added pcie mechanism for config space access.

* pci-acpi.cpp is based on the bootloader bios_ia32/acpi.cpp. The ACPI module
has already a dependency on the PCI module. Using pci-acpi.cpp eases the simple
task of finding the PCIe base address to map the config space.
* pci_read_config and pci_write_config in pci_controller.h were using an uint8
for offsets in the config space. Switched to uint16 to enable access to the extended
config space (0x100 and upper). Added a check for these offsets in
pci_mech[1|2]_[read|write]_config() for x86 and other platforms as these mechanisms
don't support a priori the extended config space.
This commit is contained in:
Jérôme Duval 2013-06-22 19:13:39 +02:00
parent bc7a518375
commit ce353e5d6e
10 changed files with 464 additions and 26 deletions

View File

@ -82,9 +82,9 @@ static int m68k_atari_enable_config(struct m68k_atari_fake_host_bridge *bridge,
uint8 bus, uint8 slot, uint8 function, uint8 offset);
static status_t m68k_atari_read_pci_config(void *cookie, uint8 bus, uint8 device,
uint8 function, uint8 offset, uint8 size, uint32 *value);
uint8 function, uint16 offset, uint8 size, uint32 *value);
static status_t m68k_atari_write_pci_config(void *cookie, uint8 bus,
uint8 device, uint8 function, uint8 offset, uint8 size,
uint8 device, uint8 function, uint16 offset, uint8 size,
uint32 value);
static status_t m68k_atari_get_max_bus_devices(void *cookie, int32 *count);
static status_t m68k_atari_read_pci_irq(void *cookie, uint8 bus, uint8 device,
@ -103,7 +103,7 @@ static pci_controller sM68kAtariPCIController = {
static status_t
m68k_atari_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 *value)
uint16 offset, uint8 size, uint32 *value)
{
struct fake_pci_device *devices = (struct fake_pci_device *)cookie;
struct fake_pci_device *dev;
@ -183,7 +183,7 @@ m68k_atari_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function
static status_t
m68k_atari_write_pci_config(void *cookie, uint8 bus, uint8 device,
uint8 function, uint8 offset, uint8 size, uint32 value)
uint8 function, uint16 offset, uint8 size, uint32 value)
{
#if 0
if (m68k_atari_enable_config(bridge, bus, device, function, offset)) {

View File

@ -80,13 +80,16 @@ struct grackle_host_bridge {
static status_t
grackle_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 *value)
uint16 offset, uint8 size, uint32 *value)
{
grackle_host_bridge *bridge = (grackle_host_bridge*)cookie;
TRACE("grackle_read_pci_config(bus=%u, dev=%u, func=%u, offset=%u, "
"size=%u)\n", (int)bus, (int)device, (int)function, (int)offset,
(int)size);
if (offset > 0xff)
return B_BAD_VALUE;
out32rb(bridge->address_registers, (1 << 31)
| (bus << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8)
| (offset & 0xfc));
@ -115,13 +118,16 @@ grackle_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
static status_t
grackle_write_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 value)
uint16 offset, uint8 size, uint32 value)
{
grackle_host_bridge *bridge = (grackle_host_bridge*)cookie;
TRACE("grackle_write_pci_config(bus=%u, dev=%u, func=%u, offset=%u, "
"size=%u, value=%lu)\n", (int)bus, (int)device, (int)function,
(int)offset, (int)size, value);
if (offset > 0xff)
return B_BAD_VALUE;
out32rb(bridge->address_registers, (1 << 31)
| (bus << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8)
| (offset & 0xfc));

View File

@ -74,9 +74,9 @@ static int uninorth_enable_config(struct uninorth_host_bridge *bridge,
uint8 bus, uint8 slot, uint8 function, uint8 offset);
static status_t uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device,
uint8 function, uint8 offset, uint8 size, uint32 *value);
uint8 function, uint16 offset, uint8 size, uint32 *value);
static status_t uninorth_write_pci_config(void *cookie, uint8 bus,
uint8 device, uint8 function, uint8 offset, uint8 size,
uint8 device, uint8 function, uint16 offset, uint8 size,
uint32 value);
static status_t uninorth_get_max_bus_devices(void *cookie, int32 *count);
static status_t uninorth_read_pci_irq(void *cookie, uint8 bus, uint8 device,
@ -95,10 +95,13 @@ static pci_controller sUniNorthPCIController = {
static status_t
uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 *value)
uint16 offset, uint8 size, uint32 *value)
{
uninorth_host_bridge *bridge = (uninorth_host_bridge*)cookie;
if (offset > 0xff)
return B_BAD_VALUE;
addr_t caoff = bridge->data_registers + (offset & 0x07);
if (uninorth_enable_config(bridge, bus, device, function, offset) != 0) {
@ -124,10 +127,13 @@ uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
static status_t uninorth_write_pci_config(void *cookie, uint8 bus, uint8 device,
uint8 function, uint8 offset, uint8 size, uint32 value)
uint8 function, uint16 offset, uint8 size, uint32 value)
{
uninorth_host_bridge *bridge = (uninorth_host_bridge*)cookie;
if (offset > 0xff)
return B_BAD_VALUE;
addr_t caoff = bridge->data_registers + (offset & 0x07);
if (uninorth_enable_config(bridge, bus, device, function, offset)) {

View File

@ -4,7 +4,12 @@ SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) ] ;
UsePrivateHeaders kernel [ FDirName kernel arch x86 ] [ FDirName kernel util ] ;
SubDirHdrs $(HAIKU_TOP) src add-ons kernel bus_managers acpi acpica include ;
SubDirHdrs $(HAIKU_TOP) src add-ons kernel bus_managers acpi acpica include
platform ;
KernelStaticLibrary pci_arch_bus_manager :
pci_acpi.cpp
pci_arch_info.cpp
pci_arch_module.cpp
pci_bios.cpp

View File

@ -0,0 +1,270 @@
/*
* Copyright 2011, Rene Gollent, rene@gollent.com.
* Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
* Copyright 2007, Michael Lotz, mmlr@mlotz.ch
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include "pci_acpi.h"
#include <string.h>
#include <KernelExport.h>
#include <arch/x86/arch_acpi.h>
//#define TRACE_ACPI
#ifdef TRACE_ACPI
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
static struct scan_spots_struct acpi_scan_spots[] = {
{ 0x0, 0x1000, 0x1000 },
{ 0x9f000, 0x10000, 0x1000 },
{ 0xe0000, 0x110000, 0x20000 },
{ 0xfd000, 0xfe000, 0x1000},
{ 0, 0, 0 }
};
static acpi_descriptor_header* sAcpiRsdt; // System Description Table
static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table
static int32 sNumEntries = -1;
static status_t
acpi_validate_rsdp(acpi_rsdp* rsdp)
{
const char* data = (const char*)rsdp;
unsigned char checksum = 0;
for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++)
checksum += data[i];
if ((checksum & 0xff) != 0) {
TRACE(("acpi: rsdp failed basic checksum\n"));
return B_BAD_DATA;
}
// for ACPI 2.0+ we need to also validate the extended checksum
if (rsdp->revision > 0) {
for (uint32 i = sizeof(acpi_rsdp_legacy);
i < sizeof(acpi_rsdp_extended); i++) {
checksum += data[i];
}
if ((checksum & 0xff) != 0) {
TRACE(("acpi: rsdp failed extended checksum\n"));
return B_BAD_DATA;
}
}
return B_OK;
}
static status_t
acpi_validate_rsdt(acpi_descriptor_header* rsdt)
{
const char* data = (const char*)rsdt;
unsigned char checksum = 0;
for (uint32 i = 0; i < rsdt->length; i++)
checksum += data[i];
return checksum == 0 ? B_OK : B_BAD_DATA;
}
static status_t
acpi_check_rsdt(acpi_rsdp* rsdp)
{
if (acpi_validate_rsdp(rsdp) != B_OK)
return B_BAD_DATA;
bool usingXsdt = false;
TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n",
rsdp, rsdp->oem_id, rsdp->revision));
TRACE(("acpi: rsdp points to rsdt at 0x%lx\n", rsdp->rsdt_address));
uint32 length = 0;
acpi_descriptor_header* rsdt = NULL;
area_id rsdtArea = -1;
if (rsdp->revision > 0) {
length = rsdp->xsdt_length;
rsdtArea = map_physical_memory("rsdt acpi",
(uint32)rsdp->xsdt_address, rsdp->xsdt_length, B_ANY_KERNEL_ADDRESS,
B_KERNEL_READ_AREA, (void **)&rsdt);
if (rsdt != NULL
&& strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) {
delete_area(rsdtArea);
rsdt = NULL;
TRACE(("acpi: invalid extended system description table\n"));
} else
usingXsdt = true;
}
// if we're ACPI v1 or we fail to map the XSDT for some reason,
// attempt to use the RSDT instead.
if (rsdt == NULL) {
// map and validate the root system description table
rsdtArea = map_physical_memory("rsdt acpi",
rsdp->rsdt_address, sizeof(acpi_descriptor_header),
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&rsdt);
if (rsdt != NULL
&& strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
delete_area(rsdtArea);
rsdt = NULL;
TRACE(("acpi: invalid root system description table\n"));
return B_ERROR;
}
length = rsdt->length;
// Map the whole table, not just the header
TRACE(("acpi: rsdt length: %lu\n", length));
delete_area(rsdtArea);
rsdtArea = map_physical_memory("rsdt acpi",
rsdp->rsdt_address, length, B_ANY_KERNEL_ADDRESS,
B_KERNEL_READ_AREA, (void **)&rsdt);
}
if (rsdt != NULL) {
if (acpi_validate_rsdt(rsdt) != B_OK) {
TRACE(("acpi: rsdt failed checksum validation\n"));
delete_area(rsdtArea);
return B_ERROR;
} else {
if (usingXsdt)
sAcpiXsdt = rsdt;
else
sAcpiRsdt = rsdt;
TRACE(("acpi: found valid %s at %p\n",
usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE,
rsdt));
}
} else
return B_ERROR;
return B_OK;
}
template<typename PointerType>
acpi_descriptor_header*
acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt)
{
if (acpiSdt == NULL)
return NULL;
if (sNumEntries == -1) {
// if using the xsdt, our entries are 64 bits wide.
sNumEntries = (acpiSdt->length
- sizeof(acpi_descriptor_header))
/ sizeof(PointerType);
}
if (sNumEntries <= 0) {
TRACE(("acpi: root system description table is empty\n"));
return NULL;
}
TRACE(("acpi: searching %ld entries for table '%.4s'\n", sNumEntries,
signature));
PointerType* pointer = (PointerType*)((uint8*)acpiSdt
+ sizeof(acpi_descriptor_header));
acpi_descriptor_header* header = NULL;
area_id headerArea = -1;
for (int32 j = 0; j < sNumEntries; j++, pointer++) {
headerArea = map_physical_memory("acpi header", (uint32)*pointer,
sizeof(acpi_descriptor_header), B_ANY_KERNEL_ADDRESS,
B_KERNEL_READ_AREA, (void **)&header);
if (header == NULL
|| strncmp(header->signature, signature, 4) != 0) {
// not interesting for us
TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n",
signature, header != NULL ? header->signature : "null"));
if (header != NULL) {
delete_area(headerArea);
header = NULL;
}
continue;
}
TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer));
break;
}
if (header == NULL)
return NULL;
// Map the whole table, not just the header
uint32 length = header->length;
delete_area(headerArea);
headerArea = map_physical_memory("acpi table",
(uint32)*pointer, length, B_ANY_KERNEL_ADDRESS,
B_KERNEL_READ_AREA, (void **)&header);
return header;
}
void*
acpi_find_table(const char* signature)
{
if (sAcpiRsdt != NULL)
return acpi_find_table_generic<uint32>(signature, sAcpiRsdt);
else if (sAcpiXsdt != NULL)
return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
return NULL;
}
void
acpi_init()
{
// Try to find the ACPI RSDP.
for (int32 i = 0; acpi_scan_spots[i].length > 0; i++) {
acpi_rsdp* rsdp = NULL;
TRACE(("acpi_init: entry base 0x%lx, limit 0x%lx\n",
acpi_scan_spots[i].start, acpi_scan_spots[i].stop));
char* start = NULL;
area_id rsdpArea = map_physical_memory("acpi rsdp",
acpi_scan_spots[i].start, acpi_scan_spots[i].length,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&start);
if (rsdpArea < B_OK) {
TRACE(("acpi_init: couldn't map %s\n", strerror(rsdpArea)));
break;
}
for (char *pointer = start;
(addr_t)pointer < (addr_t)start + acpi_scan_spots[i].length;
pointer += 16) {
if (strncmp(pointer, ACPI_RSDP_SIGNATURE, 8) == 0) {
TRACE(("acpi_init: found ACPI RSDP signature at %p\n",
pointer));
rsdp = (acpi_rsdp*)pointer;
}
}
if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) {
delete_area(rsdpArea);
break;
}
delete_area(rsdpArea);
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef PCI_ACPI_H
#define PCI_ACPI_H
#include <SupportDefs.h>
#ifdef __cplusplus
extern "C" {
#endif
struct scan_spots_struct {
uint32 start;
uint32 stop;
uint32 length;
};
void *acpi_find_table(const char *signature);
void acpi_init(void);
#ifdef __cplusplus
}
#endif
#endif /* PCI_ACPI_H */

View File

@ -17,7 +17,7 @@ pci_bios_init(void)
status_t
pci_bios_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 *value)
uint16 offset, uint8 size, uint32 *value)
{
return B_ERROR;
}
@ -25,7 +25,7 @@ pci_bios_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
status_t
pci_bios_write_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 value)
uint16 offset, uint8 size, uint32 value)
{
return B_ERROR;
}

View File

@ -7,11 +7,11 @@ status_t pci_bios_init(void);
status_t pci_bios_read_config(void *cookie,
uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 *value);
uint16 offset, uint8 size, uint32 *value);
status_t pci_bios_write_config(void *cookie,
uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 value);
uint16 offset, uint8 size, uint32 value);
status_t pci_bios_get_max_bus_devices(void *cookie, int32 *count);

View File

@ -3,14 +3,20 @@
* Distributed under the terms of the MIT License.
*/
#include <KernelExport.h>
#include <driver_settings.h>
#include <string.h>
#include "pci_irq.h"
#include "pci_bios.h"
#include "pci_private.h"
#include "pci_controller.h"
#include "pci_acpi.h"
#include "arch_cpu.h"
#include "pci_bios.h"
#include "pci_controller.h"
#include "pci_irq.h"
#include "pci_private.h"
#include "acpi.h"
#define PCI_MECH1_REQ_PORT 0xCF8
#define PCI_MECH1_DATA_PORT 0xCFC
@ -38,11 +44,14 @@ spinlock sConfigLock = B_SPINLOCK_INITIALIZER;
static status_t
pci_mech1_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 *value)
uint16 offset, uint8 size, uint32 *value)
{
cpu_status cpu;
status_t status = B_OK;
if (offset > 0xff)
return B_BAD_VALUE;
PCI_LOCK_CONFIG(cpu);
out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT);
switch (size) {
@ -67,11 +76,14 @@ pci_mech1_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
static status_t
pci_mech1_write_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 value)
uint16 offset, uint8 size, uint32 value)
{
cpu_status cpu;
status_t status = B_OK;
if (offset > 0xff)
return B_BAD_VALUE;
PCI_LOCK_CONFIG(cpu);
out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT);
switch (size) {
@ -104,11 +116,14 @@ pci_mech1_get_max_bus_devices(void *cookie, int32 *count)
static status_t
pci_mech2_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 *value)
uint16 offset, uint8 size, uint32 *value)
{
cpu_status cpu;
status_t status = B_OK;
if (offset > 0xff)
return B_BAD_VALUE;
PCI_LOCK_CONFIG(cpu);
out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT);
out8(bus, PCI_MECH2_FORWARD_PORT);
@ -135,11 +150,14 @@ pci_mech2_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
static status_t
pci_mech2_write_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 value)
uint16 offset, uint8 size, uint32 value)
{
cpu_status cpu;
status_t status = B_OK;
if (offset > 0xff)
return B_BAD_VALUE;
PCI_LOCK_CONFIG(cpu);
out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT);
out8(bus, PCI_MECH2_FORWARD_PORT);
@ -172,6 +190,71 @@ pci_mech2_get_max_bus_devices(void *cookie, int32 *count)
}
addr_t sPCIeBase = 0;
#define PCIE_VADDR(base, bus, slot, func, reg) ((base) + \
((((bus) & 0xff) << 20) | (((slot) & 0x1f) << 15) | \
(((func) & 0x7) << 12) | ((reg) & 0xfff)))
static status_t
pci_mechpcie_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32 *value)
{
status_t status = B_OK;
addr_t address = PCIE_VADDR(sPCIeBase, bus, device, function, offset);
switch (size) {
case 1:
*value = *(uint8*)address;
break;
case 2:
*value = *(uint16*)address;
break;
case 4:
*value = *(uint32*)address;
break;
default:
status = B_ERROR;
break;
}
return status;
}
static status_t
pci_mechpcie_write_config(void *cookie, uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32 value)
{
status_t status = B_OK;
addr_t address = PCIE_VADDR(sPCIeBase, bus, device, function, offset);
switch (size) {
case 1:
*(uint8*)address = value;
break;
case 2:
*(uint16*)address = value;
break;
case 4:
*(uint32*)address = value;
break;
default:
status = B_ERROR;
break;
}
return status;
}
static status_t
pci_mechpcie_get_max_bus_devices(void *cookie, int32 *count)
{
*count = 32;
return B_OK;
}
void *
pci_ram_address(const void *physical_address_in_system_memory)
{
@ -197,6 +280,15 @@ pci_controller pci_controller_x86_mech2 =
pci_x86_irq_write,
};
pci_controller pci_controller_x86_mechpcie =
{
pci_mechpcie_read_config,
pci_mechpcie_write_config,
pci_mechpcie_get_max_bus_devices,
pci_x86_irq_read,
pci_x86_irq_write,
};
pci_controller pci_controller_x86_bios =
{
pci_bios_read_config,
@ -212,6 +304,7 @@ pci_controller_init(void)
{
bool search_mech1 = true;
bool search_mech2 = true;
bool search_mechpcie = true;
bool search_bios = true;
void *config = NULL;
status_t status;
@ -225,11 +318,13 @@ pci_controller_init(void)
const char *mech = get_driver_parameter(config, "mechanism",
NULL, NULL);
if (mech) {
search_mech1 = search_mech2 = search_bios = false;
search_mech1 = search_mech2 = search_mechpcie = search_bios = false;
if (strcmp(mech, "1") == 0)
search_mech1 = true;
else if (strcmp(mech, "2") == 0)
search_mech2 = true;
else if (strcmp(mech, "pcie") == 0)
search_mechpcie = true;
else if (strcmp(mech, "bios") == 0)
search_bios = true;
else
@ -240,10 +335,39 @@ pci_controller_init(void)
// TODO: check safemode "don't call the BIOS" setting and unset search_bios!
// PCI configuration mechanism 1 is the preferred one.
// PCI configuration mechanism PCIe is the preferred one.
// If it doesn't work, try mechanism 1.
// If it doesn't work, try mechanism 2.
// Finally, try to fallback to PCI BIOS
if (search_mechpcie) {
acpi_init();
struct acpi_table_mcfg* mcfg =
(struct acpi_table_mcfg*)acpi_find_table("MCFG");
if (mcfg != NULL) {
struct acpi_mcfg_allocation* end = (struct acpi_mcfg_allocation*)
((char*)mcfg + mcfg->Header.Length);
struct acpi_mcfg_allocation* alloc = (struct acpi_mcfg_allocation*)
(mcfg + 1);
for (; alloc < end; alloc++) {
dprintf("PCI: mechanism addr: %" B_PRIx64 ", seg: %x, start: "
"%x, end: %x\n", alloc->Address, alloc->PciSegment,
alloc->StartBusNumber, alloc->EndBusNumber);
if (alloc->PciSegment == 0) {
area_id mcfgArea = map_physical_memory("acpi mcfg",
alloc->Address, (alloc->EndBusNumber + 1) << 20,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA
| B_KERNEL_WRITE_AREA, (void **)&sPCIeBase);
if (mcfgArea < 0)
break;
dprintf("PCI: mechanism pcie controller found\n");
return pci_controller_add(&pci_controller_x86_mechpcie,
NULL);
}
}
}
}
if (search_mech1) {
// check for mechanism 1
out32(0x80000000, PCI_MECH1_REQ_PORT);

View File

@ -13,12 +13,12 @@ typedef struct pci_controller
// read PCI config space
status_t (*read_pci_config)(void *cookie,
uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 *value);
uint16 offset, uint8 size, uint32 *value);
// write PCI config space
status_t (*write_pci_config)(void *cookie,
uint8 bus, uint8 device, uint8 function,
uint8 offset, uint8 size, uint32 value);
uint16 offset, uint8 size, uint32 value);
status_t (*get_max_bus_devices)(void *cookie, int32 *count);