bus_managers/pci: move MSI handling to generic code

Change-Id: I6194838b7b46222f720fc328bb4512fdb88a9c8a
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6222
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
X512
2023-04-25 15:54:32 +00:00
committed by waddlesplash
parent e942269a1f
commit 97b7c7719d
9 changed files with 731 additions and 744 deletions
@@ -12,7 +12,6 @@ KernelAddon pci :
pci_module.cpp
pci_root.cpp
pci_device.cpp
pci_msi.cpp
: pci_arch_bus_manager.a
;
+480 -3
View File
@@ -10,6 +10,10 @@
#include <KernelExport.h>
#define __HAIKU_PCI_BUS_MANAGER_TESTING 1
#include <PCI.h>
#include <arch/generic/msi.h>
#if defined(__i386__) || defined(__x86_64__)
#include <arch/x86/msi.h>
#endif
#include "util/kernel_cpp.h"
#include "pci_fixup.h"
@@ -1551,9 +1555,9 @@ PCI::_RefreshDeviceInfo(PCIBus *bus)
for (PCIDev *dev = bus->child; dev; dev = dev->next) {
_ReadBasicInfo(dev);
_ReadHeaderInfo(dev);
pci_read_msi_info(dev);
pci_read_msix_info(dev);
pci_read_ht_mapping_info(dev);
_ReadMSIInfo(dev);
_ReadMSIXInfo(dev);
_ReadHtMappingInfo(dev);
if (dev->child)
_RefreshDeviceInfo(dev->child);
}
@@ -1925,3 +1929,476 @@ PCI::SetPowerstate(uint8 domain, uint8 bus, uint8 _device, uint8 function,
return B_OK;
}
//#pragma mark - MSI
uint8
PCI::GetMSICount(PCIDev *device)
{
if (!msi_supported())
return 0;
msi_info *info = &device->msi;
if (!info->msi_capable)
return 0;
return info->message_count;
}
status_t
PCI::ConfigureMSI(PCIDev *device, uint8 count, uint8 *startVector)
{
if (!msi_supported())
return B_UNSUPPORTED;
if (count == 0 || startVector == NULL)
return B_BAD_VALUE;
msi_info *info = &device->msi;
if (!info->msi_capable)
return B_UNSUPPORTED;
if (count > 32 || count > info->message_count
|| ((count - 1) & count) != 0 /* needs to be a power of 2 */) {
return B_BAD_VALUE;
}
if (info->configured_count != 0)
return B_BUSY;
status_t result = msi_allocate_vectors(count, &info->start_vector,
&info->address_value, &info->data_value);
if (result != B_OK)
return result;
uint8 offset = info->capability_offset;
WriteConfig(device, offset + PCI_msi_address, 4,
info->address_value & 0xffffffff);
if (info->control_value & PCI_msi_control_64bit) {
WriteConfig(device, offset + PCI_msi_address_high, 4,
info->address_value >> 32);
WriteConfig(device, offset + PCI_msi_data_64bit, 2,
info->data_value);
} else
WriteConfig(device, offset + PCI_msi_data, 2, info->data_value);
info->control_value &= ~PCI_msi_control_mme_mask;
info->control_value |= (ffs(count) - 1) << 4;
WriteConfig(device, offset + PCI_msi_control, 2, info->control_value);
info->configured_count = count;
*startVector = info->start_vector;
return B_OK;
}
status_t
PCI::UnconfigureMSI(PCIDev *device)
{
if (!msi_supported())
return B_UNSUPPORTED;
// try MSI-X
status_t result = _UnconfigureMSIX(device);
if (result != B_UNSUPPORTED && result != B_NO_INIT)
return result;
msi_info *info = &device->msi;
if (!info->msi_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
msi_free_vectors(info->configured_count, info->start_vector);
info->control_value &= ~PCI_msi_control_mme_mask;
WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
info->control_value);
info->configured_count = 0;
info->address_value = 0;
info->data_value = 0;
return B_OK;
}
status_t
PCI::EnableMSI(PCIDev *device)
{
if (!msi_supported())
return B_UNSUPPORTED;
msi_info *info = &device->msi;
if (!info->msi_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// ensure the pinned interrupt is disabled
WriteConfig(device, PCI_command, 2,
ReadConfig(device, PCI_command, 2) | PCI_command_int_disable);
// enable msi generation
info->control_value |= PCI_msi_control_enable;
WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
info->control_value);
// enable HT msi mapping (if applicable)
_HtMSIMap(device, info->address_value);
dprintf("msi enabled: 0x%04" B_PRIx32 "\n",
ReadConfig(device, info->capability_offset + PCI_msi_control, 2));
return B_OK;
}
status_t
PCI::DisableMSI(PCIDev *device)
{
if (!msi_supported())
return B_UNSUPPORTED;
// try MSI-X
status_t result = _DisableMSIX(device);
if (result != B_UNSUPPORTED && result != B_NO_INIT)
return result;
msi_info *info = &device->msi;
if (!info->msi_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// disable HT msi mapping (if applicable)
_HtMSIMap(device, 0);
// disable msi generation
info->control_value &= ~PCI_msi_control_enable;
WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
info->control_value);
return B_OK;
}
uint8
PCI::GetMSIXCount(PCIDev *device)
{
if (!msi_supported())
return 0;
msix_info *info = &device->msix;
if (!info->msix_capable)
return 0;
return info->message_count;
}
status_t
PCI::ConfigureMSIX(PCIDev *device, uint8 count, uint8 *startVector)
{
if (!msi_supported())
return B_UNSUPPORTED;
if (count == 0 || startVector == NULL)
return B_BAD_VALUE;
msix_info *info = &device->msix;
if (!info->msix_capable)
return B_UNSUPPORTED;
if (count > 32 || count > info->message_count) {
return B_BAD_VALUE;
}
if (info->configured_count != 0)
return B_BUSY;
// map the table bar
size_t tableSize = info->message_count * 16;
addr_t address;
phys_addr_t barAddr = device->info.u.h0.base_registers[info->table_bar];
uchar flags = device->info.u.h0.base_register_flags[info->table_bar];
if ((flags & PCI_address_type) == PCI_address_type_64) {
barAddr |= (uint64)device->info.u.h0.base_registers[
info->table_bar + 1] << 32;
}
area_id area = map_physical_memory("msi table map",
barAddr, tableSize + info->table_offset,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void**)&address);
if (area < 0)
return area;
info->table_area_id = area;
info->table_address = address + info->table_offset;
// and the pba bar if necessary
if (info->table_bar != info->pba_bar) {
barAddr = device->info.u.h0.base_registers[info->pba_bar];
flags = device->info.u.h0.base_register_flags[info->pba_bar];
if ((flags & PCI_address_type) == PCI_address_type_64) {
barAddr |= (uint64)device->info.u.h0.base_registers[
info->pba_bar + 1] << 32;
}
area = map_physical_memory("msi pba map",
barAddr, tableSize + info->pba_offset,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void**)&address);
if (area < 0) {
delete_area(info->table_area_id);
info->table_area_id = -1;
return area;
}
info->pba_area_id = area;
} else
info->pba_area_id = -1;
info->pba_address = address + info->pba_offset;
status_t result = msi_allocate_vectors(count, &info->start_vector,
&info->address_value, &info->data_value);
if (result != B_OK) {
delete_area(info->pba_area_id);
delete_area(info->table_area_id);
info->pba_area_id = -1;
info->table_area_id = -1;
return result;
}
// ensure the memory i/o is enabled
WriteConfig(device, PCI_command, 2,
ReadConfig(device, PCI_command, 2) | PCI_command_memory);
uint32 data_value = info->data_value;
for (uint32 index = 0; index < count; index++) {
volatile uint32 *entry = (uint32*)(info->table_address + 16 * index);
*(entry + 3) |= PCI_msix_vctrl_mask;
*entry++ = info->address_value & 0xffffffff;
*entry++ = info->address_value >> 32;
*entry++ = data_value++;
*entry &= ~PCI_msix_vctrl_mask;
}
info->configured_count = count;
*startVector = info->start_vector;
dprintf("msix configured for %d vectors\n", count);
return B_OK;
}
status_t
PCI::EnableMSIX(PCIDev *device)
{
if (!msi_supported())
return B_UNSUPPORTED;
msix_info *info = &device->msix;
if (!info->msix_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// ensure the pinned interrupt is disabled
WriteConfig(device, PCI_command, 2,
ReadConfig(device, PCI_command, 2) | PCI_command_int_disable);
// enable msi-x generation
info->control_value |= PCI_msix_control_enable;
WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
info->control_value);
// enable HT msi mapping (if applicable)
_HtMSIMap(device, info->address_value);
dprintf("msi-x enabled: 0x%04" B_PRIx32 "\n",
ReadConfig(device, info->capability_offset + PCI_msix_control, 2));
return B_OK;
}
void
PCI::_HtMSIMap(PCIDev *device, uint64 address)
{
ht_mapping_info *info = &device->ht_mapping;
if (!info->ht_mapping_capable)
return;
bool enabled = (info->control_value & PCI_ht_command_msi_enable) != 0;
if ((address != 0) != enabled) {
if (enabled) {
info->control_value &= ~PCI_ht_command_msi_enable;
} else {
if ((address >> 20) != (info->address_value >> 20))
return;
dprintf("ht msi mapping enabled\n");
info->control_value |= PCI_ht_command_msi_enable;
}
WriteConfig(device, info->capability_offset + PCI_ht_command, 2,
info->control_value);
}
}
void
PCI::_ReadMSIInfo(PCIDev *device)
{
if (!msi_supported())
return;
msi_info *info = &device->msi;
info->msi_capable = false;
status_t result = FindCapability(device->domain, device->bus,
device->device, device->function, PCI_cap_id_msi,
&info->capability_offset);
if (result != B_OK)
return;
info->msi_capable = true;
info->control_value = ReadConfig(device->domain, device->bus,
device->device, device->function,
info->capability_offset + PCI_msi_control, 2);
info->message_count
= 1 << ((info->control_value & PCI_msi_control_mmc_mask) >> 1);
info->configured_count = 0;
info->data_value = 0;
info->address_value = 0;
}
void
PCI::_ReadMSIXInfo(PCIDev *device)
{
if (!msi_supported())
return;
msix_info *info = &device->msix;
info->msix_capable = false;
status_t result = FindCapability(device->domain, device->bus,
device->device, device->function, PCI_cap_id_msix,
&info->capability_offset);
if (result != B_OK)
return;
info->msix_capable = true;
info->control_value = ReadConfig(device->domain, device->bus,
device->device, device->function,
info->capability_offset + PCI_msix_control, 2);
info->message_count
= (info->control_value & PCI_msix_control_table_size) + 1;
info->configured_count = 0;
info->data_value = 0;
info->address_value = 0;
info->table_area_id = -1;
info->pba_area_id = -1;
uint32 table_value = ReadConfig(device->domain, device->bus,
device->device, device->function,
info->capability_offset + PCI_msix_table, 4);
uint32 pba_value = ReadConfig(device->domain, device->bus,
device->device, device->function,
info->capability_offset + PCI_msix_pba, 4);
info->table_bar = table_value & PCI_msix_bir_mask;
info->table_offset = table_value & PCI_msix_offset_mask;
info->pba_bar = pba_value & PCI_msix_bir_mask;
info->pba_offset = pba_value & PCI_msix_offset_mask;
}
void
PCI::_ReadHtMappingInfo(PCIDev *device)
{
if (!msi_supported())
return;
ht_mapping_info *info = &device->ht_mapping;
info->ht_mapping_capable = false;
uint8 offset = 0;
if (FindHTCapability(device, PCI_ht_command_cap_msi_mapping,
&offset) == B_OK) {
info->control_value = ReadConfig(device, offset + PCI_ht_command,
2);
info->capability_offset = offset;
info->ht_mapping_capable = true;
if ((info->control_value & PCI_ht_command_msi_fixed) != 0) {
#if defined(__i386__) || defined(__x86_64__)
info->address_value = MSI_ADDRESS_BASE;
#else
// TODO: investigate what should be set here for non-x86
dprintf("PCI_ht_command_msi_fixed flag unimplemented\n");
info->address_value = 0;
#endif
} else {
info->address_value = ReadConfig(device, offset
+ PCI_ht_msi_address_high, 4);
info->address_value <<= 32;
info->address_value |= ReadConfig(device, offset
+ PCI_ht_msi_address_low, 4);
}
dprintf("found an ht msi mapping at %#" B_PRIx64 "\n",
info->address_value);
}
}
status_t
PCI::_UnconfigureMSIX(PCIDev *device)
{
msix_info *info = &device->msix;
if (!info->msix_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// disable msi-x generation
info->control_value &= ~PCI_msix_control_enable;
WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
info->control_value);
msi_free_vectors(info->configured_count, info->start_vector);
for (uint8 index = 0; index < info->configured_count; index++) {
volatile uint32 *entry = (uint32*)(info->table_address + 16 * index);
if ((*(entry + 3) & PCI_msix_vctrl_mask) == 0)
*(entry + 3) |= PCI_msix_vctrl_mask;
}
if (info->pba_area_id != -1)
delete_area(info->pba_area_id);
if (info->table_area_id != -1)
delete_area(info->table_area_id);
info->pba_area_id= -1;
info->table_area_id = -1;
info->configured_count = 0;
info->address_value = 0;
info->data_value = 0;
return B_OK;
}
status_t
PCI::_DisableMSIX(PCIDev *device)
{
msix_info *info = &device->msix;
if (!info->msix_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// disable HT msi mapping (if applicable)
_HtMSIMap(device, 0);
// disable msi-x generation
info->control_value &= ~PCI_msix_control_enable;
gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
info->control_value);
return B_OK;
}
+16
View File
@@ -126,6 +126,15 @@ public:
uint8 device, uint8 function,
uint8 newInterruptLineValue);
uint8 GetMSICount(PCIDev *device);
status_t ConfigureMSI(PCIDev *device, uint8 count, uint8 *startVector);
status_t UnconfigureMSI(PCIDev *device);
status_t EnableMSI(PCIDev *device);
status_t DisableMSI(PCIDev *device);
uint8 GetMSIXCount(PCIDev *device);
status_t ConfigureMSIX(PCIDev *device, uint8 count, uint8 *startVector);
status_t EnableMSIX(PCIDev *device);
private:
void _EnumerateBus(uint8 domain, uint8 bus,
uint8 *subordinateBus = NULL);
@@ -170,6 +179,13 @@ private:
PCIDev * _FindDevice(PCIBus *current, uint8 domain,
uint8 bus, uint8 device, uint8 function);
void _HtMSIMap(PCIDev *device, uint64 address);
void _ReadMSIInfo(PCIDev *device);
void _ReadMSIXInfo(PCIDev *device);
void _ReadHtMappingInfo(PCIDev *device);
status_t _UnconfigureMSIX(PCIDev *device);
status_t _DisableMSIX(PCIDev *device);
private:
PCIBus * fRootBus;
@@ -21,111 +21,6 @@ struct pci_device {
};
static uint8
pci_device_read_io_8(pci_device* device, addr_t mappedIOAddress)
{
return pci_read_io_8(mappedIOAddress);
}
static void
pci_device_write_io_8(pci_device* device, addr_t mappedIOAddress,
uint8 value)
{
pci_write_io_8(mappedIOAddress, value);
}
static uint16
pci_device_read_io_16(pci_device* device, addr_t mappedIOAddress)
{
return pci_read_io_16(mappedIOAddress);
}
static void
pci_device_write_io_16(pci_device* device, addr_t mappedIOAddress,
uint16 value)
{
pci_write_io_16(mappedIOAddress, value);
}
static uint32
pci_device_read_io_32(pci_device* device, addr_t mappedIOAddress)
{
return pci_read_io_32(mappedIOAddress);
}
static void
pci_device_write_io_32(pci_device* device, addr_t mappedIOAddress, uint32 value)
{
pci_write_io_32(mappedIOAddress, value);
}
static uint32
pci_device_read_pci_config(pci_device* device, uint16 offset, uint8 size)
{
return gPCI->ReadConfig(device->device, offset, size);
}
static void
pci_device_write_pci_config(pci_device* device, uint16 offset, uint8 size,
uint32 value)
{
gPCI->WriteConfig(device->device, offset, size, value);
}
static phys_addr_t
pci_device_ram_address(pci_device* device, phys_addr_t physicalAddress)
{
return pci_ram_address(physicalAddress);
}
static status_t
pci_device_find_capability(pci_device* device, uint8 capID, uint8* offset)
{
return gPCI->FindCapability(device->device, capID, offset);
}
static status_t
pci_device_find_extended_capability(pci_device* device, uint16 capID,
uint16* offset)
{
return gPCI->FindExtendedCapability(device->device, capID, offset);
}
static uint8
pci_device_get_powerstate(pci_device *device)
{
return gPCI->GetPowerstate(device->device);
}
static void
pci_device_set_powerstate(pci_device *device, uint8 state)
{
return gPCI->SetPowerstate(device->device, state);
}
static void
pci_device_get_pci_info(pci_device* device, struct pci_info* info)
{
if (info == NULL)
return;
*info = device->device->info;
}
static status_t
pci_device_init_driver(device_node* node, void** _cookie)
{
@@ -188,29 +83,76 @@ pci_device_module_info gPCIDeviceModule = {
pci_device_std_ops
},
NULL, // supports device
NULL, // register device (our parent registered us)
pci_device_init_driver,
pci_device_uninit_driver,
NULL, // register child devices
NULL, // rescan devices
NULL, // device removed
.init_driver = pci_device_init_driver,
.uninit_driver = pci_device_uninit_driver,
},
pci_device_read_io_8,
pci_device_write_io_8,
pci_device_read_io_16,
pci_device_write_io_16,
pci_device_read_io_32,
pci_device_write_io_32,
pci_device_ram_address,
pci_device_read_pci_config,
pci_device_write_pci_config,
pci_device_find_capability,
pci_device_get_pci_info,
pci_device_find_extended_capability,
pci_device_get_powerstate,
pci_device_set_powerstate
.read_io_8 = [](pci_device *device, addr_t mappedIOAddress) {
return pci_read_io_8(mappedIOAddress);
},
.write_io_8 = [](pci_device *device, addr_t mappedIOAddress, uint8 value) {
pci_write_io_8(mappedIOAddress, value);
},
.read_io_16 = [](pci_device *device, addr_t mappedIOAddress) {
return pci_read_io_16(mappedIOAddress);
},
.write_io_16 = [](pci_device *device, addr_t mappedIOAddress, uint16 value) {
pci_write_io_16(mappedIOAddress, value);
},
.read_io_32 = [](pci_device *device, addr_t mappedIOAddress) {
return pci_read_io_32(mappedIOAddress);
},
.write_io_32 = [](pci_device *device, addr_t mappedIOAddress, uint32 value) {
pci_write_io_32(mappedIOAddress, value);
},
.ram_address = [](pci_device *device, phys_addr_t physicalAddress) {
return pci_ram_address(physicalAddress);
},
.read_pci_config = [](pci_device *device, uint16 offset, uint8 size) {
return gPCI->ReadConfig(device->device, offset, size);
},
.write_pci_config = [](pci_device *device, uint16 offset, uint8 size, uint32 value) {
gPCI->WriteConfig(device->device, offset, size, value);
},
.find_pci_capability = [](pci_device *device, uint8 capID, uint8 *offset) {
return gPCI->FindCapability(device->device, capID, offset);
},
.get_pci_info = [](pci_device *device, struct pci_info *info) {
if (info == NULL)
return;
*info = device->device->info;
},
.find_pci_extended_capability = [](pci_device *device, uint16 capID, uint16 *offset) {
return gPCI->FindExtendedCapability(device->device, capID, offset);
},
.get_powerstate = [](pci_device *device) {
return gPCI->GetPowerstate(device->device);
},
.set_powerstate = [](pci_device *device, uint8 state) {
gPCI->SetPowerstate(device->device, state);
},
.get_msi_count = [](pci_device *device) {
return gPCI->GetMSICount(device->device);
},
.configure_msi = [](pci_device *device, uint8 count, uint8 *startVector) {
return gPCI->ConfigureMSI(device->device, count, startVector);
},
.unconfigure_msi = [](pci_device *device) {
return gPCI->UnconfigureMSI(device->device);
},
.enable_msi = [](pci_device *device) {
return gPCI->EnableMSI(device->device);
},
.disable_msi = [](pci_device *device) {
return gPCI->DisableMSI(device->device);
},
.get_msix_count = [](pci_device *device) {
return gPCI->GetMSIXCount(device->device);
},
.configure_msix = [](pci_device *device, uint8 count, uint8 *startVector) {
return gPCI->ConfigureMSIX(device->device, count, startVector);
},
.enable_msix = [](pci_device *device) {
return gPCI->EnableMSIX(device->device);
}
};
@@ -14,9 +14,12 @@
#include "pci_info.h"
#include "pci.h"
#define CHECK_RET(err) {status_t _err = (err); if (_err < B_OK) return _err;}
device_manager_info *gDeviceManager;
static int32
pci_old_module_std_ops(int32 op, ...)
{
@@ -69,6 +72,23 @@ pci_arch_module_std_ops(int32 op, ...)
}
static status_t
ResolveBDF(uint8 virtualBus, uint8 device, uint8 function, PCIDev*& dev)
{
uint8 bus;
uint8 domain;
status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
if (result != B_OK)
return result;
dev = gPCI->FindDevice(domain, bus, device, function);
if (dev == NULL)
return B_ERROR;
return B_OK;
}
static struct pci_module_info sOldPCIModule = {
{
{
@@ -78,23 +98,66 @@ static struct pci_module_info sOldPCIModule = {
},
NULL
},
&pci_read_io_8,
&pci_write_io_8,
&pci_read_io_16,
&pci_write_io_16,
&pci_read_io_32,
&pci_write_io_32,
&pci_get_nth_pci_info,
&pci_read_config,
&pci_write_config,
&pci_ram_address,
&pci_find_capability,
&pci_reserve_device,
&pci_unreserve_device,
&pci_update_interrupt_line,
&pci_find_extended_capability,
&pci_get_powerstate,
&pci_set_powerstate
.read_io_8 = pci_read_io_8,
.write_io_8 = pci_write_io_8,
.read_io_16 = pci_read_io_16,
.write_io_16 = pci_write_io_16,
.read_io_32 = pci_read_io_32,
.write_io_32 = pci_write_io_32,
.get_nth_pci_info = pci_get_nth_pci_info,
.read_pci_config = pci_read_config,
.write_pci_config = pci_write_config,
.ram_address = pci_ram_address,
.find_pci_capability = pci_find_capability,
.reserve_device = pci_reserve_device,
.unreserve_device = pci_unreserve_device,
.update_interrupt_line = pci_update_interrupt_line,
.find_pci_extended_capability = pci_find_extended_capability,
.get_powerstate = pci_get_powerstate,
.set_powerstate = pci_set_powerstate,
.get_msi_count = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
if (ResolveBDF(bus, device, function, dev) < B_OK)
return (uint8)0;
return gPCI->GetMSICount(dev);
},
.configure_msi = [](uint8 bus, uint8 device, uint8 function, uint8 count, uint8 *startVector) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->ConfigureMSI(dev, count, startVector);
},
.unconfigure_msi = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->UnconfigureMSI(dev);
},
.enable_msi = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->EnableMSI(dev);
},
.disable_msi = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->DisableMSI(dev);
},
.get_msix_count = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
if (ResolveBDF(bus, device, function, dev) < B_OK)
return (uint8)0;
return gPCI->GetMSIXCount(dev);
},
.configure_msix = [](uint8 bus, uint8 device, uint8 function, uint8 count, uint8 *startVector) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->ConfigureMSIX(dev, count, startVector);
},
.enable_msix = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->EnableMSIX(dev);
}
};
@@ -104,15 +167,48 @@ static pci_x86_module_info sPCIArchModule = {
0,
pci_arch_module_std_ops
},
&pci_get_msi_count,
&pci_configure_msi,
&pci_unconfigure_msi,
&pci_enable_msi,
&pci_disable_msi,
&pci_get_msix_count,
&pci_configure_msix,
&pci_enable_msix
.get_msi_count = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
if (ResolveBDF(bus, device, function, dev) < B_OK)
return (uint8)0;
return gPCI->GetMSICount(dev);
},
.configure_msi = [](uint8 bus, uint8 device, uint8 function, uint8 count, uint8 *startVector) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->ConfigureMSI(dev, count, startVector);
},
.unconfigure_msi = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->UnconfigureMSI(dev);
},
.enable_msi = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->EnableMSI(dev);
},
.disable_msi = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->DisableMSI(dev);
},
.get_msix_count = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
if (ResolveBDF(bus, device, function, dev) < B_OK)
return (uint8)0;
return gPCI->GetMSIXCount(dev);
},
.configure_msix = [](uint8 bus, uint8 device, uint8 function, uint8 count, uint8 *startVector) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->ConfigureMSIX(dev, count, startVector);
},
.enable_msix = [](uint8 bus, uint8 device, uint8 function) {
PCIDev* dev;
CHECK_RET(ResolveBDF(bus, device, function, dev));
return gPCI->EnableMSIX(dev);
}
};
@@ -1,566 +0,0 @@
/*
* Copyright 2013, Jérôme Duval, korli@users.berlios.de.
* Copyright 2010, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include "pci_msi.h"
#include "pci.h"
#include "pci_private.h"
#include <strings.h>
#include <arch/x86/msi.h>
#include <debug.h>
extern PCI *gPCI;
static status_t pci_unconfigure_msix(PCIDev *device);
static status_t pci_disable_msix(PCIDev *device);
static void
pci_ht_msi_map(PCIDev *device, uint64 address)
{
ht_mapping_info *info = &device->ht_mapping;
if (!info->ht_mapping_capable)
return;
bool enabled = (info->control_value & PCI_ht_command_msi_enable) != 0;
if ((address != 0) != enabled) {
if (enabled) {
info->control_value &= ~PCI_ht_command_msi_enable;
} else {
if ((address >> 20) != (info->address_value >> 20))
return;
dprintf("ht msi mapping enabled\n");
info->control_value |= PCI_ht_command_msi_enable;
}
gPCI->WriteConfig(device, info->capability_offset + PCI_ht_command, 2,
info->control_value);
}
}
void
pci_read_ht_mapping_info(PCIDev *device)
{
if (!msi_supported())
return;
ht_mapping_info *info = &device->ht_mapping;
info->ht_mapping_capable = false;
uint8 offset = 0;
if (gPCI->FindHTCapability(device, PCI_ht_command_cap_msi_mapping,
&offset) == B_OK) {
info->control_value = gPCI->ReadConfig(device, offset + PCI_ht_command,
2);
info->capability_offset = offset;
info->ht_mapping_capable = true;
if ((info->control_value & PCI_ht_command_msi_fixed) != 0)
info->address_value = MSI_ADDRESS_BASE;
else {
info->address_value = gPCI->ReadConfig(device, offset
+ PCI_ht_msi_address_high, 4);
info->address_value <<= 32;
info->address_value |= gPCI->ReadConfig(device, offset
+ PCI_ht_msi_address_low, 4);
}
dprintf("found an ht msi mapping at %#" B_PRIx64 "\n",
info->address_value);
}
}
uint8
pci_get_msi_count(uint8 virtualBus, uint8 _device, uint8 function)
{
if (!msi_supported())
return 0;
uint8 bus;
uint8 domain;
if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK)
return 0;
PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
if (device == NULL)
return 0;
msi_info *info = &device->msi;
if (!info->msi_capable)
return 0;
return info->message_count;
}
status_t
pci_configure_msi(uint8 virtualBus, uint8 _device, uint8 function,
uint8 count, uint8 *startVector)
{
if (!msi_supported())
return B_UNSUPPORTED;
if (count == 0 || startVector == NULL)
return B_BAD_VALUE;
uint8 bus;
uint8 domain;
status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
if (result != B_OK)
return result;
PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
if (device == NULL)
return B_ERROR;
msi_info *info = &device->msi;
if (!info->msi_capable)
return B_UNSUPPORTED;
if (count > 32 || count > info->message_count
|| ((count - 1) & count) != 0 /* needs to be a power of 2 */) {
return B_BAD_VALUE;
}
if (info->configured_count != 0)
return B_BUSY;
result = msi_allocate_vectors(count, &info->start_vector,
&info->address_value, &info->data_value);
if (result != B_OK)
return result;
uint8 offset = info->capability_offset;
gPCI->WriteConfig(device, offset + PCI_msi_address, 4,
info->address_value & 0xffffffff);
if (info->control_value & PCI_msi_control_64bit) {
gPCI->WriteConfig(device, offset + PCI_msi_address_high, 4,
info->address_value >> 32);
gPCI->WriteConfig(device, offset + PCI_msi_data_64bit, 2,
info->data_value);
} else
gPCI->WriteConfig(device, offset + PCI_msi_data, 2, info->data_value);
info->control_value &= ~PCI_msi_control_mme_mask;
info->control_value |= (ffs(count) - 1) << 4;
gPCI->WriteConfig(device, offset + PCI_msi_control, 2, info->control_value);
info->configured_count = count;
*startVector = info->start_vector;
return B_OK;
}
status_t
pci_unconfigure_msi(uint8 virtualBus, uint8 _device, uint8 function)
{
if (!msi_supported())
return B_UNSUPPORTED;
uint8 bus;
uint8 domain;
status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
if (result != B_OK)
return result;
PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
if (device == NULL)
return B_ERROR;
// try MSI-X
result = pci_unconfigure_msix(device);
if (result != B_UNSUPPORTED && result != B_NO_INIT)
return result;
msi_info *info = &device->msi;
if (!info->msi_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
msi_free_vectors(info->configured_count, info->start_vector);
info->control_value &= ~PCI_msi_control_mme_mask;
gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
info->control_value);
info->configured_count = 0;
info->address_value = 0;
info->data_value = 0;
return B_OK;
}
status_t
pci_enable_msi(uint8 virtualBus, uint8 _device, uint8 function)
{
if (!msi_supported())
return B_UNSUPPORTED;
uint8 bus;
uint8 domain;
status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
if (result != B_OK)
return result;
PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
if (device == NULL)
return B_ERROR;
msi_info *info = &device->msi;
if (!info->msi_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// ensure the pinned interrupt is disabled
gPCI->WriteConfig(device, PCI_command, 2,
gPCI->ReadConfig(device, PCI_command, 2) | PCI_command_int_disable);
// enable msi generation
info->control_value |= PCI_msi_control_enable;
gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
info->control_value);
// enable HT msi mapping (if applicable)
pci_ht_msi_map(device, info->address_value);
dprintf("msi enabled: 0x%04" B_PRIx32 "\n",
gPCI->ReadConfig(device, info->capability_offset + PCI_msi_control, 2));
return B_OK;
}
status_t
pci_disable_msi(uint8 virtualBus, uint8 _device, uint8 function)
{
if (!msi_supported())
return B_UNSUPPORTED;
uint8 bus;
uint8 domain;
status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
if (result != B_OK)
return result;
PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
if (device == NULL)
return B_ERROR;
// try MSI-X
result = pci_disable_msix(device);
if (result != B_UNSUPPORTED && result != B_NO_INIT)
return result;
msi_info *info = &device->msi;
if (!info->msi_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// disable HT msi mapping (if applicable)
pci_ht_msi_map(device, 0);
// disable msi generation
info->control_value &= ~PCI_msi_control_enable;
gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
info->control_value);
return B_OK;
}
void
pci_read_msi_info(PCIDev *device)
{
if (!msi_supported())
return;
msi_info *info = &device->msi;
info->msi_capable = false;
status_t result = gPCI->FindCapability(device->domain, device->bus,
device->device, device->function, PCI_cap_id_msi,
&info->capability_offset);
if (result != B_OK)
return;
info->msi_capable = true;
info->control_value = gPCI->ReadConfig(device->domain, device->bus,
device->device, device->function,
info->capability_offset + PCI_msi_control, 2);
info->message_count
= 1 << ((info->control_value & PCI_msi_control_mmc_mask) >> 1);
info->configured_count = 0;
info->data_value = 0;
info->address_value = 0;
}
uint8
pci_get_msix_count(uint8 virtualBus, uint8 _device, uint8 function)
{
if (!msi_supported())
return 0;
uint8 bus;
uint8 domain;
if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK)
return 0;
PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
if (device == NULL)
return 0;
msix_info *info = &device->msix;
if (!info->msix_capable)
return 0;
return info->message_count;
}
status_t
pci_configure_msix(uint8 virtualBus, uint8 _device, uint8 function,
uint8 count, uint8 *startVector)
{
if (!msi_supported())
return B_UNSUPPORTED;
if (count == 0 || startVector == NULL)
return B_BAD_VALUE;
uint8 bus;
uint8 domain;
status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
if (result != B_OK)
return result;
PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
if (device == NULL)
return B_ERROR;
msix_info *info = &device->msix;
if (!info->msix_capable)
return B_UNSUPPORTED;
if (count > 32 || count > info->message_count) {
return B_BAD_VALUE;
}
if (info->configured_count != 0)
return B_BUSY;
// map the table bar
size_t tableSize = info->message_count * 16;
addr_t address;
phys_addr_t barAddr = device->info.u.h0.base_registers[info->table_bar];
uchar flags = device->info.u.h0.base_register_flags[info->table_bar];
if ((flags & PCI_address_type) == PCI_address_type_64) {
barAddr |= (uint64)device->info.u.h0.base_registers[
info->table_bar + 1] << 32;
}
area_id area = map_physical_memory("msi table map",
barAddr, tableSize + info->table_offset,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void**)&address);
if (area < 0)
return area;
info->table_area_id = area;
info->table_address = address + info->table_offset;
// and the pba bar if necessary
if (info->table_bar != info->pba_bar) {
barAddr = device->info.u.h0.base_registers[info->pba_bar];
flags = device->info.u.h0.base_register_flags[info->pba_bar];
if ((flags & PCI_address_type) == PCI_address_type_64) {
barAddr |= (uint64)device->info.u.h0.base_registers[
info->pba_bar + 1] << 32;
}
area = map_physical_memory("msi pba map",
barAddr, tableSize + info->pba_offset,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void**)&address);
if (area < 0) {
delete_area(info->table_area_id);
info->table_area_id = -1;
return area;
}
info->pba_area_id = area;
} else
info->pba_area_id = -1;
info->pba_address = address + info->pba_offset;
result = msi_allocate_vectors(count, &info->start_vector,
&info->address_value, &info->data_value);
if (result != B_OK) {
delete_area(info->pba_area_id);
delete_area(info->table_area_id);
info->pba_area_id = -1;
info->table_area_id = -1;
return result;
}
// ensure the memory i/o is enabled
gPCI->WriteConfig(device, PCI_command, 2,
gPCI->ReadConfig(device, PCI_command, 2) | PCI_command_memory);
uint32 data_value = info->data_value;
for (uint32 index = 0; index < count; index++) {
volatile uint32 *entry = (uint32*)(info->table_address + 16 * index);
*(entry + 3) |= PCI_msix_vctrl_mask;
*entry++ = info->address_value & 0xffffffff;
*entry++ = info->address_value >> 32;
*entry++ = data_value++;
*entry &= ~PCI_msix_vctrl_mask;
}
info->configured_count = count;
*startVector = info->start_vector;
dprintf("msix configured for %d vectors\n", count);
return B_OK;
}
static status_t
pci_unconfigure_msix(PCIDev *device)
{
msix_info *info = &device->msix;
if (!info->msix_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// disable msi-x generation
info->control_value &= ~PCI_msix_control_enable;
gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
info->control_value);
msi_free_vectors(info->configured_count, info->start_vector);
for (uint8 index = 0; index < info->configured_count; index++) {
volatile uint32 *entry = (uint32*)(info->table_address + 16 * index);
if ((*(entry + 3) & PCI_msix_vctrl_mask) == 0)
*(entry + 3) |= PCI_msix_vctrl_mask;
}
if (info->pba_area_id != -1)
delete_area(info->pba_area_id);
if (info->table_area_id != -1)
delete_area(info->table_area_id);
info->pba_area_id= -1;
info->table_area_id = -1;
info->configured_count = 0;
info->address_value = 0;
info->data_value = 0;
return B_OK;
}
status_t
pci_enable_msix(uint8 virtualBus, uint8 _device, uint8 function)
{
if (!msi_supported())
return B_UNSUPPORTED;
uint8 bus;
uint8 domain;
status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
if (result != B_OK)
return result;
PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
if (device == NULL)
return B_ERROR;
msix_info *info = &device->msix;
if (!info->msix_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// ensure the pinned interrupt is disabled
gPCI->WriteConfig(device, PCI_command, 2,
gPCI->ReadConfig(device, PCI_command, 2) | PCI_command_int_disable);
// enable msi-x generation
info->control_value |= PCI_msix_control_enable;
gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
info->control_value);
// enable HT msi mapping (if applicable)
pci_ht_msi_map(device, info->address_value);
dprintf("msi-x enabled: 0x%04" B_PRIx32 "\n",
gPCI->ReadConfig(device, info->capability_offset + PCI_msix_control, 2));
return B_OK;
}
status_t
pci_disable_msix(PCIDev *device)
{
msix_info *info = &device->msix;
if (!info->msix_capable)
return B_UNSUPPORTED;
if (info->configured_count == 0)
return B_NO_INIT;
// disable HT msi mapping (if applicable)
pci_ht_msi_map(device, 0);
// disable msi-x generation
info->control_value &= ~PCI_msix_control_enable;
gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
info->control_value);
return B_OK;
}
void
pci_read_msix_info(PCIDev *device)
{
if (!msi_supported())
return;
msix_info *info = &device->msix;
info->msix_capable = false;
status_t result = gPCI->FindCapability(device->domain, device->bus,
device->device, device->function, PCI_cap_id_msix,
&info->capability_offset);
if (result != B_OK)
return;
info->msix_capable = true;
info->control_value = gPCI->ReadConfig(device->domain, device->bus,
device->device, device->function,
info->capability_offset + PCI_msix_control, 2);
info->message_count
= (info->control_value & PCI_msix_control_table_size) + 1;
info->configured_count = 0;
info->data_value = 0;
info->address_value = 0;
info->table_area_id = -1;
info->pba_area_id = -1;
uint32 table_value = gPCI->ReadConfig(device->domain, device->bus,
device->device, device->function,
info->capability_offset + PCI_msix_table, 4);
uint32 pba_value = gPCI->ReadConfig(device->domain, device->bus,
device->device, device->function,
info->capability_offset + PCI_msix_pba, 4);
info->table_bar = table_value & PCI_msix_bir_mask;
info->table_offset = table_value & PCI_msix_offset_mask;
info->pba_bar = pba_value & PCI_msix_bir_mask;
info->pba_offset = pba_value & PCI_msix_offset_mask;
}
@@ -13,8 +13,6 @@
// Message Signaled Interrupts
struct PCIDev;
// MSI
typedef struct msi_info {
bool msi_capable;
@@ -28,15 +26,6 @@ typedef struct msi_info {
} msi_info;
uint8 pci_get_msi_count(uint8 virtualBus, uint8 _device, uint8 function);
status_t pci_configure_msi(uint8 virtualBus, uint8 _device, uint8 function,
uint8 count, uint8 *startVector);
status_t pci_unconfigure_msi(uint8 virtualBus, uint8 device, uint8 function);
status_t pci_enable_msi(uint8 virtualBus, uint8 device, uint8 function);
status_t pci_disable_msi(uint8 virtualBus, uint8 device, uint8 function);
void pci_read_msi_info(PCIDev *device);
// MSI-X
typedef struct msix_info {
bool msix_capable;
@@ -58,12 +47,6 @@ typedef struct msix_info {
} msix_info;
uint8 pci_get_msix_count(uint8 virtualBus, uint8 _device, uint8 function);
status_t pci_configure_msix(uint8 virtualBus, uint8 _device, uint8 function,
uint8 count, uint8 *startVector);
status_t pci_enable_msix(uint8 virtualBus, uint8 _device, uint8 function);
void pci_read_msix_info(PCIDev *device);
// HyperTransport MSI mapping
typedef struct ht_mapping_info {
bool ht_mapping_capable;
@@ -73,7 +56,4 @@ typedef struct ht_mapping_info {
} ht_mapping_info;
void pci_read_ht_mapping_info(PCIDev *device);
#endif // _PCI_x86_MSI_H