77 lines
2.3 KiB
C
77 lines
2.3 KiB
C
#define pr_fmt(fmt) "pci: " fmt
|
|
|
|
#include <zinnia/alloc.h>
|
|
#include <zinnia/mmio.h>
|
|
#include <zinnia/pci.h>
|
|
#include <zinnia/pmap.h>
|
|
#include <zinnia/print.h>
|
|
#include <zinnia/utils.h>
|
|
#include <zinnia/virt.h>
|
|
#include <zinnia/vma.h>
|
|
#include <arch/mem.h>
|
|
|
|
struct pci_ecam {
|
|
struct pci_access access;
|
|
void* base;
|
|
};
|
|
|
|
static void* ecam_ptr(struct pci_access* self, const struct pci_address* addr, uint16_t offset) {
|
|
struct pci_ecam* ecam = containerof(self, struct pci_ecam, access);
|
|
size_t index = (size_t)(addr->bus - self->start_bus) << 20 | (size_t)(addr->device & 0x1f) << 15 |
|
|
(size_t)(addr->function & 0x7) << 12 | (offset & 0xffc);
|
|
return (uint8_t*)ecam->base + index;
|
|
}
|
|
|
|
static uint32_t ecam_read32(struct pci_access* self, const struct pci_address* addr, uint16_t offset) {
|
|
return mmio_read32(ecam_ptr(self, addr, offset));
|
|
}
|
|
|
|
static void ecam_write32(struct pci_access* self, const struct pci_address* addr, uint16_t offset, uint32_t value) {
|
|
mmio_write32(ecam_ptr(self, addr, offset), value);
|
|
}
|
|
|
|
errno_t pci_ecam_register(phys_t base, uint16_t domain, uint8_t start_bus, uint8_t end_bus) {
|
|
if (end_bus < start_bus)
|
|
return EINVAL;
|
|
|
|
size_t length = ((size_t)(end_bus - start_bus) + 1) << 20;
|
|
phys_t phys = base + ((phys_t)start_bus << 20);
|
|
|
|
uintptr_t vaddr = vm_alloc(length);
|
|
if (!vaddr)
|
|
return ENOMEM;
|
|
|
|
for (size_t i = 0; i < length; i += arch_mem_page_size()) {
|
|
errno_t status = pmap_map(&kernel_vma.pmap, vaddr + i, phys + i, PTE_READ | PTE_WRITE, CACHE_MMIO);
|
|
if (status != 0)
|
|
return status;
|
|
}
|
|
|
|
struct pci_ecam* ecam = mem_alloc(sizeof(*ecam), 0);
|
|
if (ecam == nullptr)
|
|
return ENOMEM;
|
|
|
|
*ecam = (struct pci_ecam){
|
|
.access =
|
|
{
|
|
.name = "ecam",
|
|
.domain = domain,
|
|
.start_bus = start_bus,
|
|
.end_bus = end_bus,
|
|
.priority = PCI_ACCESS_PRIO_ECAM,
|
|
.read32 = ecam_read32,
|
|
.write32 = ecam_write32,
|
|
},
|
|
.base = (void*)vaddr,
|
|
};
|
|
|
|
errno_t status = pci_access_register(&ecam->access);
|
|
if (status != 0) {
|
|
mem_free(ecam);
|
|
return status;
|
|
}
|
|
|
|
log("ECAM for %.4hx:%.2hhx - %.4hx:%.2hhx at %p\n", domain, start_bus, domain, end_bus, (void*)phys);
|
|
return 0;
|
|
}
|