metal-cos/sys/arm64/pci.c

217 lines
5.5 KiB
C

#include <stdint.h>
#include <sys/kassert.h>
#include <sys/pci.h>
#include <sys/contrib/libfdt/libfdt.h>
#include <sys/fdt_helper.h>
#include <errno.h>
#include <machine/bootinfo.h>
#include <machine/cpu.h>
#include <machine/cpuop.h>
#include <machine/pmap.h>
#include <machine/pci.h>
static paddr_t pci_conf_space;
static size_t pci_conf_size;
static paddr_t pci_io_space;
static size_t pci_io_size;
static size_t pci_io_used = 0;
static paddr_t pci_mem_space;
static size_t pci_mem_size;
static size_t pci_mem_used = 0;
static inline volatile void *
PCIGetAddr(uint32_t bus, uint32_t slot, uint32_t func, uint32_t reg)
{
ASSERT(bus < 256 && slot < 64 && func < 8 && reg < 256);
return (volatile void *)(DEVPA2VA(pci_conf_space) + ((bus << 20) | (slot << 15) | (func << 12) | (reg & 0xff)));
}
paddr_t
PCI_GetIOAddr(void)
{
return pci_io_space;
}
size_t
PCI_AllocIOSpace(size_t sz)
{
const uint32_t new_used = ROUNDUP(pci_io_used, sz);
sz = ROUNDUP(sz, 4);
if (sz > (pci_io_size - new_used)) {
Panic("Out of PCI IO space!\n");
} else {
const paddr_t ret = new_used;
pci_io_used = new_used + sz;
return ret;
}
}
size_t
PCI_AllocMemSpace(size_t sz)
{
const uint32_t new_used = ROUNDUP(pci_io_used, sz);
sz = ROUNDUP(sz, 4096);
if (sz > (pci_mem_size - new_used)) {
Panic("Out of PCI memory space!\n");
} else {
const paddr_t ret = new_used;
pci_mem_used = new_used + sz;
return ret;
}
}
paddr_t
PCI_GetMemAddr(void)
{
return pci_mem_space;
}
uint8_t
PCICfgRead8(uint32_t bus, uint32_t slot, uint32_t func, uint32_t reg)
{
volatile uint8_t * ret = PCIGetAddr(bus, slot, func, reg);
return * ret;
}
uint16_t
PCICfgRead16(uint32_t bus, uint32_t slot, uint32_t func, uint32_t reg)
{
volatile uint16_t * ret = PCIGetAddr(bus, slot, func, reg);
return * ret;
}
uint32_t
PCICfgRead32(uint32_t bus, uint32_t slot, uint32_t func, uint32_t reg)
{
volatile uint32_t * ret = PCIGetAddr(bus, slot, func, reg);
return * ret;
}
void
PCICfgWrite8(uint32_t bus, uint32_t slot, uint32_t func, uint32_t reg, uint8_t data)
{
volatile uint8_t * ret = PCIGetAddr(bus, slot, func, reg);
*ret = data;
}
void
PCICfgWrite16(uint32_t bus, uint32_t slot, uint32_t func, uint32_t reg,
uint16_t data)
{
volatile uint16_t * ret = PCIGetAddr(bus, slot, func, reg);
*ret = data;
}
void
PCICfgWrite32(uint32_t bus, uint32_t slot, uint32_t func, uint32_t reg,
uint32_t data)
{
volatile uint32_t * ret = PCIGetAddr(bus, slot, func, reg);
*ret = data;
}
static inline int
pci_range_attr_space_code(uint32_t attr_cell)
{
return (attr_cell >> 24) & 0b11;
}
static inline int
pci_range_attr_bus(uint32_t attr_cell)
{
return (attr_cell >> 16) & 0xff;
}
static inline int
pci_range_attr_dev(uint32_t attr_cell)
{
return (attr_cell >> 11) & 0b11111;
}
static inline int
pci_range_attr_func(uint32_t attr_cell)
{
return (attr_cell >> 8) & 0b111;
}
#define PCI_RANGE_ATTR_CONF (0b00)
#define PCI_RANGE_ATTR_IO (0b01)
#define PCI_RANGE_ATTR_M32 (0b10)
#define PCI_RANGE_ATTR_M64 (0b11)
int
PCI_Discover(void)
{
const void * dtb = kbootinfo.dtb_addr;
const int offset = fdt_node_offset_by_compatible(dtb, -1, "pci-host-ecam-generic");
if (offset < 0) {
return ENOENT;
}
// parse PCI Conf space
int lenp;
const struct fdt_property * prop = fdt_get_property(dtb, offset, "reg", &lenp);
if (prop == NULL || lenp != 4 * sizeof(uint32_t)) {
kprintf("Unrecognized PCI reg format!\n");
return EINVAL;
}
const uint32_t * ptr = (const uint32_t *)prop->data;
const uint64_t conf_base = fdth_make_uint64(ptr[0], ptr[1]);
const uint64_t limit = fdth_make_uint64(ptr[2], ptr[3]);;
pci_conf_space = conf_base;
pci_conf_size = limit;
// io / memory space
prop = fdt_get_property(dtb, offset, "ranges", &lenp);
if (prop == NULL || lenp % ((3 + 2 + 2) * sizeof(uint32_t)) != 0) {
kprintf("Unrecognized PCI ranges format!\n");
return EINVAL;
}
bool has_io = false, has_mem = false;
ptr = (const uint32_t *)prop->data;
for (unsigned int i = 0; i < (lenp / sizeof(uint32_t)); i += (3 + 2 + 2)) {
const uint32_t attr_cell = fdt32_to_cpu(ptr[i]);
const UNUSED uint64_t pci_addr = fdth_make_uint64(ptr[i+1], ptr[i+2]);
const uint64_t cpu_addr = fdth_make_uint64(ptr[i+3], ptr[i+4]);
const uint64_t cpu_limit = fdth_make_uint64(ptr[i+5], ptr[i+6]);
//kprintf("attr: 0x%x, cpu_addr 0x%lx, limit 0x%lx\n", attr_cell, cpu_addr, cpu_limit);
if (pci_range_attr_bus(attr_cell) == 0 && pci_range_attr_dev(attr_cell) == 0 && pci_range_attr_func(attr_cell) == 0) {
const int ss = pci_range_attr_space_code(attr_cell);
if (ss == PCI_RANGE_ATTR_IO) {
has_io = true;
pci_io_space = cpu_addr;
pci_io_size = cpu_limit;
} else if (ss == PCI_RANGE_ATTR_M32) {
has_mem = true;
pci_mem_space = cpu_addr;
pci_mem_size = cpu_limit;
}
// don't support m64 and conf types
}
// only support root bridge addresses for now
}
if (!has_io || !has_mem) {
return EINVAL;
}
kprintf("PCI Host Controller: Conf space = 0x%lx(0x%lx). IO space = 0x%lx(0x%lx). Memory space = 0x%lx(0x%lx).\n",
pci_conf_space, pci_conf_size,
pci_io_space, pci_io_size,
pci_mem_space, pci_mem_size
);
return 0;
}