mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-31 06:28:22 +00:00
pci: msi-x support
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
#define PCI_CONFIG_COMMAND 0x4
|
#define PCI_CONFIG_COMMAND 0x4
|
||||||
#define PCI_COMMAND_IO 0x1
|
#define PCI_COMMAND_IO 0x1
|
||||||
#define PCI_COMMAND_MMIO 0x2
|
#define PCI_COMMAND_MMIO 0x2
|
||||||
#define PCI_COMMAND_IRQ 0x400
|
#define PCI_COMMAND_IRQDISABLE 0x400
|
||||||
#define PCI_CONFIG_STATUS 0x6
|
#define PCI_CONFIG_STATUS 0x6
|
||||||
#define PCI_STATUS_HASCAP 0x10
|
#define PCI_STATUS_HASCAP 0x10
|
||||||
|
|
||||||
@@ -35,6 +35,13 @@ typedef struct {
|
|||||||
int offset;
|
int offset;
|
||||||
} pcicap_t;
|
} pcicap_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uintptr_t address;
|
||||||
|
size_t length;
|
||||||
|
bool mmio;
|
||||||
|
bool prefetchable;
|
||||||
|
} pcibar_t;
|
||||||
|
|
||||||
typedef struct pcienum_t {
|
typedef struct pcienum_t {
|
||||||
struct pcienum_t *next;
|
struct pcienum_t *next;
|
||||||
int bus;
|
int bus;
|
||||||
@@ -49,15 +56,18 @@ typedef struct pcienum_t {
|
|||||||
int revision;
|
int revision;
|
||||||
pcicap_t msi;
|
pcicap_t msi;
|
||||||
pcicap_t msix;
|
pcicap_t msix;
|
||||||
|
pcibar_t bar[6];
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
int bir;
|
||||||
|
uintmax_t tableoffset;
|
||||||
|
int pbir;
|
||||||
|
uintmax_t pboffset;
|
||||||
|
size_t entrycount;
|
||||||
|
} msix;
|
||||||
|
} irq;
|
||||||
} pcienum_t;
|
} pcienum_t;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uintptr_t address;
|
|
||||||
size_t length;
|
|
||||||
bool mmio;
|
|
||||||
bool prefetchable;
|
|
||||||
} pcibar_t;
|
|
||||||
|
|
||||||
uint8_t pci_read8(int bus, int device, int function, uint32_t offset);
|
uint8_t pci_read8(int bus, int device, int function, uint32_t offset);
|
||||||
uint16_t pci_read16(int bus, int device, int function, uint32_t offset);
|
uint16_t pci_read16(int bus, int device, int function, uint32_t offset);
|
||||||
uint32_t pci_read32(int bus, int device, int function, uint32_t offset);
|
uint32_t pci_read32(int bus, int device, int function, uint32_t offset);
|
||||||
@@ -69,6 +79,9 @@ int pci_getcapoffset(pcienum_t *e, int cap, int n);
|
|||||||
pcibar_t pci_getbar(pcienum_t *e, int bar);
|
pcibar_t pci_getbar(pcienum_t *e, int bar);
|
||||||
void *pci_mapbar(pcibar_t bar);
|
void *pci_mapbar(pcibar_t bar);
|
||||||
void pci_setcommand(pcienum_t *e, int mask, int v);
|
void pci_setcommand(pcienum_t *e, int mask, int v);
|
||||||
|
size_t pci_initmsix(pcienum_t *e);
|
||||||
|
void pci_msixsetmask(pcienum_t *e, int v);
|
||||||
|
void pci_msixadd(pcienum_t *e, int msixvec, int vec, int edgetrigger, int deassert);
|
||||||
void pci_init();
|
void pci_init();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <arch/io.h>
|
#include <arch/io.h>
|
||||||
#include <arch/acpi.h>
|
#include <arch/acpi.h>
|
||||||
#include <kernel/pmm.h>
|
#include <kernel/pmm.h>
|
||||||
|
#include <arch/cpu.h>
|
||||||
|
|
||||||
static size_t mcfgentrycount;
|
static size_t mcfgentrycount;
|
||||||
static mcfgentry_t *mcfgentries;
|
static mcfgentry_t *mcfgentries;
|
||||||
@@ -65,6 +66,11 @@ static void mcfg_write32(int bus, int device, int function, uint32_t offset, uin
|
|||||||
*address = value;
|
*address = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t msiformatmessage(uint32_t *data, int vector, int edgetrigger, int deassert) {
|
||||||
|
*data = (edgetrigger << 15) | (deassert << 14) | vector;
|
||||||
|
return 0xfee00000 | (_cpu()->id << 12);
|
||||||
|
}
|
||||||
|
|
||||||
static void pci_archinit() {
|
static void pci_archinit() {
|
||||||
mcfg_t *mcfg = arch_acpi_findtable("MCFG", 0);
|
mcfg_t *mcfg = arch_acpi_findtable("MCFG", 0);
|
||||||
if (mcfg) {
|
if (mcfg) {
|
||||||
|
|||||||
@@ -64,6 +64,58 @@ void pci_setcommand(pcienum_t *e, int mask, int v) {
|
|||||||
pci_write16(e->bus, e->device, e->function, PCI_CONFIG_COMMAND, current);
|
pci_write16(e->bus, e->device, e->function, PCI_CONFIG_COMMAND, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t addresslow;
|
||||||
|
uint32_t addresshigh;
|
||||||
|
uint32_t data;
|
||||||
|
uint32_t control;
|
||||||
|
} __attribute__((packed)) msixmessage_t;
|
||||||
|
|
||||||
|
void pci_msixadd(pcienum_t *e, int msixvec, int vec, int edgetrigger, int deassert) {
|
||||||
|
__assert(e->msix.exists);
|
||||||
|
__assert(msixvec < e->irq.msix.entrycount);
|
||||||
|
|
||||||
|
pcibar_t bir = pci_getbar(e, e->irq.msix.bir);
|
||||||
|
|
||||||
|
volatile msixmessage_t *table = (msixmessage_t *)(bir.address + e->irq.msix.tableoffset);
|
||||||
|
uint32_t data;
|
||||||
|
uint64_t address = msiformatmessage(&data, vec, edgetrigger, deassert);
|
||||||
|
|
||||||
|
table[msixvec].addresslow = address & 0xffffffff;
|
||||||
|
table[msixvec].addresshigh = (address >> 32) & 0xffffffff;
|
||||||
|
table[msixvec].data = data;
|
||||||
|
table[msixvec].control = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pci_msixsetmask(pcienum_t *e, int v) {
|
||||||
|
uint16_t msgctl = pci_read16(e->bus, e->device, e->function, e->msix.offset + 2);
|
||||||
|
msgctl = v ? msgctl | 0x4000 : msgctl & ~0x4000;
|
||||||
|
pci_write16(e->bus, e->device, e->function, e->msix.offset + 2, msgctl);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pci_initmsix(pcienum_t *e) {
|
||||||
|
if (e->msix.exists == false)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
uint16_t msgctl = pci_read16(e->bus, e->device, e->function, e->msix.offset + 2);
|
||||||
|
msgctl |= 0xc000;
|
||||||
|
pci_write16(e->bus, e->device, e->function, e->msix.offset + 2, msgctl);
|
||||||
|
|
||||||
|
e->irq.msix.entrycount = (msgctl & 0x3ff) + 1;
|
||||||
|
|
||||||
|
uint32_t bir = pci_read32(e->bus, e->device, e->function, e->msix.offset + 4);
|
||||||
|
e->irq.msix.bir = bir & 0x7;
|
||||||
|
e->irq.msix.tableoffset = bir & ~0x7;
|
||||||
|
|
||||||
|
bir = pci_read32(e->bus, e->device, e->function, e->msix.offset + 8);
|
||||||
|
e->irq.msix.pbir = bir & 0x7;
|
||||||
|
e->irq.msix.pboffset = bir & ~0x7;
|
||||||
|
|
||||||
|
pci_setcommand(e, PCI_COMMAND_IRQDISABLE, 1);
|
||||||
|
|
||||||
|
return e->irq.msix.entrycount;
|
||||||
|
}
|
||||||
|
|
||||||
#define BAR_MAP_FLAGS (ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_NOEXEC)
|
#define BAR_MAP_FLAGS (ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_NOEXEC)
|
||||||
|
|
||||||
void *pci_mapbar(pcibar_t bar) {
|
void *pci_mapbar(pcibar_t bar) {
|
||||||
@@ -77,6 +129,9 @@ void *pci_mapbar(pcibar_t bar) {
|
|||||||
#define BAR_PREFETCHABLE 0x8
|
#define BAR_PREFETCHABLE 0x8
|
||||||
|
|
||||||
pcibar_t pci_getbar(pcienum_t *e, int n) {
|
pcibar_t pci_getbar(pcienum_t *e, int n) {
|
||||||
|
if (e->bar[n].length)
|
||||||
|
return e->bar[n];
|
||||||
|
|
||||||
pcibar_t ret;
|
pcibar_t ret;
|
||||||
int offset = 0x10 + n * 4;
|
int offset = 0x10 + n * 4;
|
||||||
|
|
||||||
@@ -99,8 +154,11 @@ pcibar_t pci_getbar(pcienum_t *e, int n) {
|
|||||||
pci_write32(e->bus, e->device, e->function, offset, -1);
|
pci_write32(e->bus, e->device, e->function, offset, -1);
|
||||||
ret.length = ~(pci_read32(e->bus, e->device, e->function, offset) & 0xfffffff0) + 1;
|
ret.length = ~(pci_read32(e->bus, e->device, e->function, offset) & 0xfffffff0) + 1;
|
||||||
pci_write32(e->bus, e->device, e->function, offset, bar);
|
pci_write32(e->bus, e->device, e->function, offset, bar);
|
||||||
|
ret.address = (uint64_t)pci_mapbar(ret);
|
||||||
|
__assert(ret.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e->bar[n] = ret;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user