PCI: implemented reading, configuring and enabling MSI-X.

This commit is contained in:
Jérôme Duval 2013-07-09 23:31:58 +02:00
parent 0c0f333a67
commit 8cd7d43670
4 changed files with 309 additions and 0 deletions

View File

@ -9,4 +9,5 @@ void
pci_read_arch_info(PCIDev *dev)
{
pci_read_msi_info(dev);
pci_read_msix_info(dev);
}

View File

@ -9,6 +9,7 @@
typedef struct pci_arch_info {
msi_info msi;
msix_info msix;
} pci_arch_info;

View File

@ -1,4 +1,5 @@
/*
* 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.
*/
@ -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;
}

View File

@ -1,12 +1,15 @@
/*
* 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.
*/
#ifndef _PCI_x86_MSI_H
#define _PCI_x86_MSI_H
#include <OS.h>
#include <SupportDefs.h>
class PCIDev;
// Message Signaled Interrupts
@ -30,4 +33,34 @@ 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);
// Message Signaled Interrupts
typedef struct msix_info {
bool msix_capable;
uint8 capability_offset;
uint8 message_count;
uint8 table_bar;
uint32 table_offset;
area_id table_area_id;
addr_t table_address;
uint8 pba_bar;
uint32 pba_offset;
area_id pba_area_id;
addr_t pba_address;
uint8 configured_count;
uint8 start_vector;
uint16 control_value;
uint16 data_value;
uint64 address_value;
} 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_unconfigure_msix(uint8 virtualBus, uint8 device, uint8 function);
status_t pci_enable_msix(uint8 virtualBus, uint8 _device, uint8 function);
status_t pci_disable_msix(uint8 virtualBus, uint8 _device, uint8 function);
void pci_read_msix_info(PCIDev *device);
#endif // _PCI_x86_MSI_H