217 lines
5.5 KiB
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;
|
|
}
|