Skip to content
Snippets Groups Projects
Commit 708d2e61 authored by Asias He's avatar Asias He Committed by Pekka Enberg
Browse files

pci: Support MSI interrupt


Currently, only MSI-X is support in our PCI layer. Devices like AHCI
controller support MSI interrupt only. This paves the way for AHCI
driver.

Signed-off-by: default avatarAsias He <asias@cloudius-systems.com>
Signed-off-by: default avatarPekka Enberg <penberg@cloudius-systems.com>
parent 3f2039b9
No related branches found
No related tags found
No related merge requests found
......@@ -211,6 +211,38 @@ namespace pci {
return msi_ok;
}
// Parse MSI
off = find_capability(PCI_CAP_MSI);
if (off != 0xFF) {
return parse_pci_msi(off);
}
return true;
}
bool function::parse_pci_msi(u8 off)
{
// Location within the configuration space
_msi.msi_location = off;
_msi.msi_ctrl = pci_readw(off + PCIR_MSI_CTRL);
// TODO: support multiple MSI message
_msi.msi_msgnum = 1;
if (_msi.msi_ctrl & (1 << 7)) {
_msi.is_64_address = true;
} else {
_msi.is_64_address = false;
}
if (_msi.msi_ctrl & (1 << 8)) {
_msi.is_vector_mask = true;
} else {
_msi.is_vector_mask = false;
}
// We've found an MSI-x capability
_have_msi = true;
return true;
}
......@@ -388,6 +420,11 @@ namespace pci {
return _have_msix;
}
bool function::is_msi()
{
return _have_msi;
}
unsigned function::msix_get_num_entries()
{
if (!is_msix()) {
......@@ -397,6 +434,15 @@ namespace pci {
return _msix.msix_msgnum;
}
unsigned function::msi_get_num_entries()
{
if (!is_msi()) {
return 0;
}
return _msi.msi_msgnum;
}
void function::msix_mask_all()
{
if (!is_msix()) {
......@@ -408,6 +454,16 @@ namespace pci {
msix_set_control(ctrl);
}
void function::msi_mask_all()
{
if (!is_msi()) {
return;
}
for (int i = 0; i < _msi.msi_msgnum; i++) {
msi_mask_entry(i);
}
}
void function::msix_unmask_all()
{
if (!is_msix()) {
......@@ -419,6 +475,16 @@ namespace pci {
msix_set_control(ctrl);
}
void function::msi_unmask_all()
{
if (!is_msi()) {
return;
}
for (int i = 0; i < _msi.msi_msgnum; i++) {
msi_unmask_entry(i);
}
}
bool function::msix_mask_entry(int entry_id)
{
if (!is_msix()) {
......@@ -439,6 +505,35 @@ namespace pci {
return true;
}
bool function::msi_mask_entry(int entry_id)
{
if (!is_msi()) {
return false;
}
if (entry_id >= _msi.msi_msgnum) {
return false;
}
// Per-vector mask enabled?
if (_msi.is_vector_mask) {
// 64 bits address enabled?
if (_msi.is_64_address) {
auto reg = _msi.msi_location + PCIR_MSI_MASK_64;
auto mask = pci_readl(reg);
mask |= 1 << entry_id;
pci_writel(reg, mask);
} else {
auto reg = _msi.msi_location + PCIR_MSI_MASK_32;
auto mask = pci_readl(reg);
mask |= 1 << entry_id;
pci_writel(reg, mask);
}
}
return true;
}
bool function::msix_unmask_entry(int entry_id)
{
if (!is_msix()) {
......@@ -459,6 +554,35 @@ namespace pci {
return true;
}
bool function::msi_unmask_entry(int entry_id)
{
if (!is_msi()) {
return false;
}
if (entry_id >= _msi.msi_msgnum) {
return false;
}
// Per-vector mask enabled?
if (_msi.is_vector_mask) {
// 64 bits address enabled?
if (_msi.is_64_address) {
auto reg = _msi.msi_location + PCIR_MSI_MASK_64;
auto mask = pci_readl(reg);
mask &= ~(1 << entry_id);
pci_writel(reg, mask);
} else {
auto reg = _msi.msi_location + PCIR_MSI_MASK_32;
auto mask = pci_readl(reg);
mask &= ~(1 << entry_id);
pci_writel(reg, mask);
}
}
return true;
}
bool function::msix_write_entry(int entry_id, u64 address, u32 data)
{
if (!is_msix()) {
......@@ -477,6 +601,29 @@ namespace pci {
return true;
}
bool function::msi_write_entry(int entry_id, u64 address, u32 data)
{
if (!is_msi()) {
return false;
}
if (entry_id >= _msi.msi_msgnum) {
return false;
}
// 64 Bit message address enabled ?
if (_msi.is_64_address) {
pci_writel(_msi.msi_location + PCIR_MSI_ADDR, address & 0xFFFFFFFF);
pci_writel(_msi.msi_location + PCIR_MSI_UADDR, address >> 32);
pci_writel(_msi.msi_location + PCIR_MSI_DATA_64, data);
} else {
pci_writel(_msi.msi_location + PCIR_MSI_ADDR, address & 0xFFFFFFFF);
pci_writel(_msi.msi_location + PCIR_MSI_DATA_32, data);
}
return true;
}
void function::msix_enable()
{
if (!is_msix()) {
......@@ -514,6 +661,29 @@ namespace pci {
_msix_enabled = true;
}
void function::msi_enable()
{
if (!is_msi()) {
return;
}
// Disabled intx assertions which is turned on by default
disable_intx();
u16 ctrl = msi_get_control();
ctrl |= PCIR_MSI_CTRL_ME;
// Mask all individual entries
for (int i = 0; i< _msi.msi_msgnum; i++) {
msi_mask_entry(i);
}
msi_set_control(ctrl);
_msi_enabled = true;
}
void function::msix_disable()
{
if (!is_msix()) {
......@@ -527,16 +697,39 @@ namespace pci {
_msix_enabled = false;
}
void function::msi_disable()
{
if (!is_msi()) {
return;
}
u16 ctrl = msi_get_control();
ctrl &= ~PCIR_MSI_CTRL_ME;
msix_set_control(ctrl);
_msi_enabled = false;
}
void function::msix_set_control(u16 ctrl)
{
pci_writew(_msix.msix_location + PCIR_MSIX_CTRL, ctrl);
}
void function::msi_set_control(u16 ctrl)
{
pci_writew(_msi.msi_location + PCIR_MSI_CTRL, ctrl);
}
u16 function::msix_get_control()
{
return pci_readw(_msix.msix_location + PCIR_MSIX_CTRL);
}
u16 function::msi_get_control()
{
return pci_readw(_msi.msi_location + PCIR_MSI_CTRL);
}
mmioaddr_t function::msix_get_table()
{
bar* msix_bar = get_bar(_msix.msix_table_bar + 1);
......
......@@ -101,6 +101,18 @@ namespace pci {
bool _is_prefetchable;
};
// MSI-X definitions
enum msi_pci_conf {
PCIR_MSI_CTRL = 0x2,
PCIR_MSI_CTRL_ME = 1 << 0,
PCIR_MSI_ADDR = 0x4,
PCIR_MSI_UADDR = 0x8,
PCIR_MSI_DATA_32 = 0x8,
PCIR_MSI_DATA_64 = 0x0C,
PCIR_MSI_MASK_32 = 0x0C,
PCIR_MSI_MASK_64 = 0x10,
};
// MSI-X definitions
enum msix_pci_conf {
PCIR_MSIX_CTRL = 0x2,
......@@ -131,6 +143,14 @@ namespace pci {
u32 msix_pba_offset;
};
struct pcicfg_msi {
u8 msi_location; // Offset of MSI capability registers.
u16 msi_ctrl; // Message Control
u16 msi_msgnum; // Number of messages
bool is_64_address; // 64 bit address
bool is_vector_mask; // Per-vector mask
};
// Represents a PCI function (pci device, bridge or virtio device)
class function : public hw_device {
public:
......@@ -277,6 +297,19 @@ namespace pci {
void msix_disable();
bool is_msix_enabled() {return _msix_enabled;}
// MSI
// Does this device support MSI
bool is_msi();
unsigned msi_get_num_entries();
void msi_mask_all();
void msi_unmask_all();
bool msi_mask_entry(int entry_id);
bool msi_unmask_entry(int entry_id);
bool msi_write_entry(int entry_id, u64 address, u32 data);
void msi_enable();
void msi_disable();
bool is_msi_enabled() {return _msi_enabled;}
// Access to PCI address space
virtual u8 pci_readb(u8 offset);
virtual u16 pci_readw(u8 offset);
......@@ -312,12 +345,16 @@ namespace pci {
// Parsing of extra capabilities
virtual bool parse_pci_capabilities();
virtual bool parse_pci_msix(u8 off);
virtual bool parse_pci_msi(u8 off);
// Don't call if msix capability is not present
void msix_set_control(u16 ctrl);
u16 msix_get_control();
mmioaddr_t msix_get_table();
void msi_set_control(u16 ctrl);
u16 msi_get_control();
// Position
u8 _bus, _device, _func;
......@@ -337,6 +374,11 @@ namespace pci {
bool _have_msix;
pcicfg_msix _msix;
bool _msix_enabled;
// MSI-x
bool _have_msi;
pcicfg_msi _msi;
bool _msi_enabled;
};
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment