In order to prepare support for bridges enumeration, split out the actual endpoint enumeration code out of the enumeration loop. Pass a skip_next_func boolean to indicate if the current endpoint is multifunction of not, to continue to next dev or next function. Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
233 lines
5.8 KiB
C
233 lines
5.8 KiB
C
/*
|
|
* Copyright (c) 2021 BayLibre, SAS
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(pcie_core, LOG_LEVEL_INF);
|
|
|
|
#include <kernel.h>
|
|
#include <drivers/pcie/pcie.h>
|
|
#include <drivers/pcie/controller.h>
|
|
|
|
#if CONFIG_PCIE_MSI
|
|
#include <drivers/pcie/msi.h>
|
|
#endif
|
|
|
|
/* arch agnostic PCIe API implementation */
|
|
|
|
uint32_t pcie_conf_read(pcie_bdf_t bdf, unsigned int reg)
|
|
{
|
|
const struct device *dev;
|
|
|
|
dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_pcie_controller));
|
|
if (!dev) {
|
|
LOG_ERR("Failed to get PCIe root complex");
|
|
return 0xffffffff;
|
|
}
|
|
|
|
return pcie_ctrl_conf_read(dev, bdf, reg);
|
|
}
|
|
|
|
void pcie_conf_write(pcie_bdf_t bdf, unsigned int reg, uint32_t data)
|
|
{
|
|
const struct device *dev;
|
|
|
|
dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_pcie_controller));
|
|
if (!dev) {
|
|
LOG_ERR("Failed to get PCIe root complex");
|
|
return;
|
|
}
|
|
|
|
pcie_ctrl_conf_write(dev, bdf, reg, data);
|
|
}
|
|
|
|
uint32_t pcie_generic_ctrl_conf_read(mm_reg_t cfg_addr, pcie_bdf_t bdf, unsigned int reg)
|
|
{
|
|
volatile uint32_t *bdf_cfg_mem = (volatile uint32_t *)((uintptr_t)cfg_addr + (bdf << 4));
|
|
|
|
if (!cfg_addr) {
|
|
return 0xffffffff;
|
|
}
|
|
|
|
return bdf_cfg_mem[reg];
|
|
}
|
|
|
|
void pcie_generic_ctrl_conf_write(mm_reg_t cfg_addr, pcie_bdf_t bdf,
|
|
unsigned int reg, uint32_t data)
|
|
{
|
|
volatile uint32_t *bdf_cfg_mem = (volatile uint32_t *)((uintptr_t)cfg_addr + (bdf << 4));
|
|
|
|
if (!cfg_addr) {
|
|
return;
|
|
}
|
|
|
|
bdf_cfg_mem[reg] = data;
|
|
}
|
|
|
|
static void pcie_generic_ctrl_enumerate_type1(const struct device *ctrl_dev, pcie_bdf_t bdf)
|
|
{
|
|
/* Not yet supported */
|
|
}
|
|
|
|
static void pcie_generic_ctrl_enumerate_bars(const struct device *ctrl_dev, pcie_bdf_t bdf,
|
|
unsigned int nbars)
|
|
{
|
|
unsigned int bar, reg, data;
|
|
uintptr_t scratch, bar_bus_addr;
|
|
size_t size, bar_size;
|
|
|
|
for (bar = 0, reg = PCIE_CONF_BAR0; bar < nbars && reg <= PCIE_CONF_BAR5; reg ++, bar++) {
|
|
bool found_mem64 = false;
|
|
bool found_mem = false;
|
|
|
|
data = scratch = pcie_conf_read(bdf, reg);
|
|
|
|
if (PCIE_CONF_BAR_INVAL_FLAGS(data)) {
|
|
continue;
|
|
}
|
|
|
|
if (PCIE_CONF_BAR_MEM(data)) {
|
|
found_mem = true;
|
|
if (PCIE_CONF_BAR_64(data)) {
|
|
found_mem64 = true;
|
|
scratch |= ((uint64_t)pcie_conf_read(bdf, reg + 1)) << 32;
|
|
if (PCIE_CONF_BAR_ADDR(scratch) == PCIE_CONF_BAR_INVAL64) {
|
|
continue;
|
|
}
|
|
} else {
|
|
if (PCIE_CONF_BAR_ADDR(scratch) == PCIE_CONF_BAR_INVAL) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
pcie_conf_write(bdf, reg, 0xFFFFFFFF);
|
|
size = pcie_conf_read(bdf, reg);
|
|
pcie_conf_write(bdf, reg, scratch & 0xFFFFFFFF);
|
|
|
|
if (found_mem64) {
|
|
pcie_conf_write(bdf, reg + 1, 0xFFFFFFFF);
|
|
size |= ((uint64_t)pcie_conf_read(bdf, reg + 1)) << 32;
|
|
pcie_conf_write(bdf, reg + 1, scratch >> 32);
|
|
}
|
|
|
|
if (!PCIE_CONF_BAR_ADDR(size)) {
|
|
if (found_mem64) {
|
|
reg++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (found_mem) {
|
|
if (found_mem64) {
|
|
bar_size = (uint64_t)~PCIE_CONF_BAR_ADDR(size) + 1;
|
|
} else {
|
|
bar_size = (uint32_t)~PCIE_CONF_BAR_ADDR(size) + 1;
|
|
}
|
|
} else {
|
|
bar_size = (uint32_t)~PCIE_CONF_BAR_IO_ADDR(size) + 1;
|
|
}
|
|
|
|
if (pcie_ctrl_region_allocate(ctrl_dev, bdf, found_mem,
|
|
found_mem64, bar_size, &bar_bus_addr)) {
|
|
uintptr_t bar_phys_addr;
|
|
|
|
pcie_ctrl_region_translate(ctrl_dev, bdf, found_mem,
|
|
found_mem64, bar_bus_addr, &bar_phys_addr);
|
|
|
|
LOG_INF("[%02x:%02x.%x] BAR%d size 0x%lx "
|
|
"assigned [%s 0x%lx-0x%lx -> 0x%lx-0x%lx]",
|
|
PCIE_BDF_TO_BUS(bdf), PCIE_BDF_TO_DEV(bdf), PCIE_BDF_TO_FUNC(bdf),
|
|
bar, bar_size,
|
|
found_mem ? (found_mem64 ? "mem64" : "mem") : "io",
|
|
bar_bus_addr, bar_bus_addr + bar_size - 1,
|
|
bar_phys_addr, bar_phys_addr + bar_size - 1);
|
|
|
|
pcie_conf_write(bdf, reg, bar_bus_addr & 0xFFFFFFFF);
|
|
if (found_mem64) {
|
|
pcie_conf_write(bdf, reg + 1, bar_bus_addr >> 32);
|
|
}
|
|
} else {
|
|
LOG_INF("[%02x:%02x.%x] BAR%d size 0x%lx Failed memory allocation.",
|
|
PCIE_BDF_TO_BUS(bdf), PCIE_BDF_TO_DEV(bdf), PCIE_BDF_TO_FUNC(bdf),
|
|
bar, bar_size);
|
|
}
|
|
|
|
if (found_mem64) {
|
|
reg++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void pcie_generic_ctrl_enumerate_type0(const struct device *ctrl_dev, pcie_bdf_t bdf)
|
|
{
|
|
/* Setup Type0 BARs */
|
|
pcie_generic_ctrl_enumerate_bars(ctrl_dev, bdf, 6);
|
|
}
|
|
|
|
static void pcie_generic_ctrl_enumerate_endpoint(const struct device *ctrl_dev,
|
|
pcie_bdf_t bdf, bool *skip_next_func)
|
|
{
|
|
bool multifunction_device = false;
|
|
bool layout_type_1 = false;
|
|
uint32_t data, class, id;
|
|
|
|
*skip_next_func = false;
|
|
|
|
id = pcie_conf_read(bdf, PCIE_CONF_ID);
|
|
if (id == PCIE_ID_NONE) {
|
|
return;
|
|
}
|
|
|
|
class = pcie_conf_read(bdf, PCIE_CONF_CLASSREV);
|
|
data = pcie_conf_read(bdf, PCIE_CONF_TYPE);
|
|
|
|
multifunction_device = PCIE_CONF_MULTIFUNCTION(data);
|
|
layout_type_1 = PCIE_CONF_TYPE_BRIDGE(data);
|
|
|
|
LOG_INF("[%02x:%02x.%x] %04x:%04x class %x subclass %x progif %x "
|
|
"rev %x Type%x multifunction %s",
|
|
PCIE_BDF_TO_BUS(bdf), PCIE_BDF_TO_DEV(bdf), PCIE_BDF_TO_FUNC(bdf),
|
|
id & 0xffff, id >> 16,
|
|
PCIE_CONF_CLASSREV_CLASS(class),
|
|
PCIE_CONF_CLASSREV_SUBCLASS(class),
|
|
PCIE_CONF_CLASSREV_PROGIF(class),
|
|
PCIE_CONF_CLASSREV_REV(class),
|
|
layout_type_1 ? 1 : 0,
|
|
multifunction_device ? "true" : "false");
|
|
|
|
/* Do not enumerate sub-functions if not a multifunction device */
|
|
if (PCIE_BDF_TO_FUNC(bdf) == 0 && !multifunction_device) {
|
|
*skip_next_func = true;
|
|
}
|
|
|
|
if (layout_type_1) {
|
|
pcie_generic_ctrl_enumerate_type1(ctrl_dev, bdf);
|
|
} else {
|
|
pcie_generic_ctrl_enumerate_type0(ctrl_dev, bdf);
|
|
}
|
|
}
|
|
|
|
void pcie_generic_ctrl_enumerate(const struct device *ctrl_dev, pcie_bdf_t bdf_start)
|
|
{
|
|
uint32_t data, class, id;
|
|
bool skip_next_func = false;
|
|
unsigned int dev = PCIE_BDF_TO_DEV(bdf_start),
|
|
func = 0,
|
|
bus = PCIE_BDF_TO_BUS(bdf_start);
|
|
|
|
for (; dev <= PCIE_MAX_DEV; dev++) {
|
|
func = 0;
|
|
for (; func <= PCIE_MAX_FUNC; func++) {
|
|
pcie_bdf_t bdf = PCIE_BDF(bus, dev, func);
|
|
|
|
pcie_generic_ctrl_enumerate_endpoint(ctrl_dev, bdf, &skip_next_func);
|
|
|
|
if (skip_next_func) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|