|
|
|
@@ -1,4 +1,5 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright 2013, Jérôme Duval, [email protected].
|
|
|
|
|
* Copyright 2010, Michael Lotz, [email protected]. All Rights Reserved.
|
|
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
|
*/
|
|
|
|
@@ -225,3 +226,276 @@ pci_read_msi_info(PCIDev *device)
|
|
|
|
|
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->arch_info.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->arch_info.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;
|
|
|
|
|
area_id area = map_physical_memory("msi table map",
|
|
|
|
|
device->info.u.h0.base_registers[info->table_bar],
|
|
|
|
|
tableSize + info->table_offset,
|
|
|
|
|
B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_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) {
|
|
|
|
|
area = map_physical_memory("msi pba map",
|
|
|
|
|
device->info.u.h0.base_registers[info->pba_bar],
|
|
|
|
|
tableSize + info->pba_offset,
|
|
|
|
|
B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
|
pci_unconfigure_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->arch_info.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->arch_info.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);
|
|
|
|
|
|
|
|
|
|
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(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->arch_info.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);
|
|
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
pci_read_msix_info(PCIDev *device)
|
|
|
|
|
{
|
|
|
|
|
if (!msi_supported())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
msix_info *info = &device->arch_info.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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|