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:
@@ -12,7 +12,6 @@ KernelAddon pci :
|
||||
pci_module.cpp
|
||||
pci_root.cpp
|
||||
pci_device.cpp
|
||||
pci_msi.cpp
|
||||
: pci_arch_bus_manager.a
|
||||
;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user