PCI: implemented reading, configuring and enabling MSI-X.
This commit is contained in:
parent
0c0f333a67
commit
8cd7d43670
@ -9,4 +9,5 @@ void
|
||||
pci_read_arch_info(PCIDev *dev)
|
||||
{
|
||||
pci_read_msi_info(dev);
|
||||
pci_read_msix_info(dev);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
typedef struct pci_arch_info {
|
||||
msi_info msi;
|
||||
msix_info msix;
|
||||
} pci_arch_info;
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user