soc/cavium: Add PCI support

* Add support for secure/unsecure split
* Use MMCONF to access devices in domain0
* Program MSIX vectors to fix a crash in GNU/Linux

Tested on Cavium CN81XX_EVB.

All PCI devices are visible.

Change-Id: I881f38a26a165e6bd965fcd73547473b5e32d4b0
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/25750
Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Patrick Rudolph 2018-04-17 13:47:55 +02:00 committed by Philipp Deppenwiese
parent 02c0814764
commit d0c6797e79
8 changed files with 633 additions and 1 deletions

View File

@ -15,4 +15,196 @@
chip soc/cavium/cn81xx
device cpu_cluster 0 on end
device domain 0 on
chip soc/cavium/common/pci
register "secure" = "0"
device pci 01.0 on # PCI bridge
chip soc/cavium/common/pci
register "secure" = "0"
device pci 00.0 on end # MRML
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 00.1 on end # RESET
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 00.2 on end # DAP
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 00.3 on end # MDIO
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 00.4 on end # FUSE
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 01.2 on end # SGPIO
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 01.3 on end # SMI
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 01.4 on end # MMC
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 01.5 on end # KEY
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 01.6 on end # BOOT BUS
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 01.7 on end # PBUS
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 02.0 on end # XCV
end
device pci 04.0 on end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 06.0 on end # L2C-TAD
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 07.0 on end # L2C-CBC
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 07.4 on end # L2C-MCI
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 08.0 on end # UUA0
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 08.1 on end # UUA1
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 08.2 on end # UUA2
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 08.3 on end # UUA3
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 08.4 on end # VRM
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 09.0 on end # I2C0
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 09.1 on end # I2C1
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 0a.0 on end # PCC Bridge
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 0b.0 on end # IOBN
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 0c.0 on end # OCLA0
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 0c.1 on end # OCLA1
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 0d.0 on end
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 0e.0 on end # PCIe0
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 0e.1 on end # PCIe1
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 0e.2 on end # PCIe2
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 10.0 on end # bgx0
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 10.1 on end # bgx1
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 11.0 on end # rgx0
end
chip soc/cavium/common/pci
register "secure" = "0"
device pci 12.0 on end # MAC
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 1c.0 on end # GSER0
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 1c.1 on end # GSER1
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 1c.2 on end # GSER2
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 1c.3 on end # GSER3
end
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 02.0 on end #SMMU
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 03.0 on end #GIC
end
chip soc/cavium/common/pci
register "secure" = "1"
device pci 04.0 on end #GTI
end
device pci 05.0 on end # NIC
device pci 06.0 on end # GPIO
device pci 07.0 on end # SPI
device pci 08.0 on end # MIO
device pci 09.0 on end # PCI bridge
device pci 0a.0 on end # PCI bridge
device pci 0b.0 on end # NFC
device pci 0c.0 on end # PCI bridge
device pci 0d.0 on end # PCM
device pci 0e.0 on end # VRM
device pci 0f.0 on end # PCI bridge
device pci 10.0 on end # USB0
device pci 11.0 on end # USB1
device pci 16.0 on end # SATA0
device pci 17.0 on end # SATA1
end
end
end

View File

@ -12,6 +12,8 @@ config SOC_CAVIUM_CN81XX
select UART_OVERRIDE_REFCLK
select SOC_CAVIUM_COMMON
select CAVIUM_BDK_DDR_TUNE_HW_OFFSETS
select MMCONF_SUPPORT
select PCI
if SOC_CAVIUM_CN81XX
@ -25,4 +27,7 @@ config HEAP_SIZE
config STACK_SIZE
default 0x2000
config MMCONF_BASE_ADDRESS
default 0x848000000000
endif

View File

@ -63,6 +63,7 @@ ramstage-y += sdram.c
ramstage-y += soc.c
ramstage-y += cpu.c
ramstage-y += cpu_secondary.S
ramstage-y += ecam0.c
# BDK coreboot interface
ramstage-y += ../common/bdk-coreboot.c

View File

@ -0,0 +1,374 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018 Facebook, Inc.
* Copyright 2003-2017 Cavium Inc. <support@cavium.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <console/console.h>
#include <arch/io.h>
#include <device/pci.h>
#include <device/pci_ops.h>
#include <soc/addressmap.h>
#include <soc/cavium/common/pci/chip.h>
#include <assert.h>
#define CAVM_PCCPF_XXX_VSEC_CTL 0x108
#define CAVM_PCCPF_XXX_VSEC_SCTL 0x10c
/*
* Hide PCI device function on BUS 1 in non secure world.
*/
static void disable_func(unsigned int devfn)
{
u64 *addr;
printk(BIOS_DEBUG, "PCI: 01:%02x.%x is secure\n", devfn >> 3,
devfn & 7);
/* disable function */
addr = (void *)ECAM0_RSLX_SDIS;
u64 reg = read64(&addr[devfn]);
reg &= ~3;
reg |= 2;
write64(&addr[devfn], reg);
}
/*
* Show PCI device function on BUS 1 in non secure world.
*/
static void enable_func(unsigned int devfn)
{
u64 *addr;
printk(BIOS_DEBUG, "PCI: 01:%02x.%x is insecure\n", devfn >> 3,
devfn & 7);
/* enable function */
addr = (void *)ECAM0_RSLX_SDIS;
u64 reg = read64(&addr[devfn]);
reg &= ~3;
write64(&addr[devfn], reg);
addr = (void *)ECAM0_RSLX_NSDIS;
reg = read64(&addr[devfn]);
reg &= ~1;
write64(&addr[devfn], reg);
}
/*
* Hide PCI device on BUS 0 in non secure world.
*/
static void disable_device(unsigned int dev)
{
u64 *addr;
printk(BIOS_DEBUG, "PCI: 00:%02x.0 is secure\n", dev);
/* disable function */
addr = (void *)ECAM0_DEVX_SDIS;
u64 reg = read64(&addr[dev]);
reg &= ~3;
write64(&addr[dev], reg);
addr = (void *)ECAM0_DEVX_NSDIS;
reg = read64(&addr[dev]);
reg |= 1;
write64(&addr[dev], reg);
}
/*
* Show PCI device on BUS 0 in non secure world.
*/
static void enable_device(unsigned int dev)
{
u64 *addr;
printk(BIOS_DEBUG, "PCI: 00:%02x.0 is insecure\n", dev);
/* enable function */
addr = (void *)ECAM0_DEVX_SDIS;
u64 reg = read64(&addr[dev]);
reg &= ~3;
write64(&addr[dev], reg);
addr = (void *)ECAM0_DEVX_NSDIS;
reg = read64(&addr[dev]);
reg &= ~1;
write64(&addr[dev], reg);
}
static void ecam0_read_resources(struct device *dev)
{
/* There are no dynamic PCI resources on Cavium SoC */
}
static void ecam0_fix_missing_devices(struct bus *link)
{
size_t i;
/**
* Cavium thinks it's a good idea to violate the PCI spec.
* Disabled multi-function PCI devices might have active functions.
* Add devices here manually, as coreboot's PCI allocator won't find
* them otherwise...
*/
for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
struct device_path pci_path;
struct device *child;
pci_path.type = DEVICE_PATH_PCI;
pci_path.pci.devfn = i;
child = find_dev_path(link, &pci_path);
if (!child)
pci_probe_dev(NULL, link, i);
}
}
/**
* Get PCI BAR address from cavium specific extended capability.
* Use regular BAR if not found in extended capability space.
*
* @return The pyhsical address of the BAR, zero on error
*/
static uint64_t get_bar_val(struct device *dev, u8 bar)
{
size_t cap_offset = pci_find_capability(dev, 0x14);
uint64_t h, l, ret = 0;
if (cap_offset) {
/* Found EA */
u8 es, bei;
u8 ne = pci_read_config8(dev, cap_offset + 2) & 0x3f;
cap_offset += 4;
while (ne) {
uint32_t dw0 = pci_read_config32(dev, cap_offset);
es = dw0 & 7;
bei = (dw0 >> 4) & 0xf;
if (bei == bar) {
h = 0;
l = pci_read_config32(dev, cap_offset + 4);
if (l & 2)
h = pci_read_config32(dev,
cap_offset + 12);
ret = (h << 32) | (l & ~0xfull);
break;
}
cap_offset += (es + 1) * 4;
ne--;
}
} else {
h = 0;
l = pci_read_config32(dev, bar * 4 + PCI_BASE_ADDRESS_0);
if (l & 4)
h = pci_read_config32(dev, bar * 4 + PCI_BASE_ADDRESS_0
+ 4);
ret = (h << 32) | (l & ~0xfull);
}
return ret;
}
/**
* pci_enable_msix - configure device's MSI-X capability structure
* @dev: pointer to the pci_dev data structure of MSI-X device function
* @entries: pointer to an array of MSI-X entries
* @nvec: number of MSI-X irqs requested for allocation by device driver
*
* Setup the MSI-X capability structure of device function with the number
* of requested irqs upon its software driver call to request for
* MSI-X mode enabled on its hardware device function. A return of zero
* indicates the successful configuration of MSI-X capability structure.
* A return of < 0 indicates a failure.
* Or a return of > 0 indicates that driver request is exceeding the number
* of irqs or MSI-X vectors available. Driver should use the returned value to
* re-send its request.
**/
static size_t ecam0_pci_enable_msix(struct device *dev,
struct msix_entry *entries, size_t nvec)
{
struct msix_entry *msixtable;
u32 offset;
u8 bar_idx;
u64 bar;
size_t nr_entries;
size_t i;
u16 control;
if (!entries) {
printk(BIOS_ERR, "%s: No entries specified\n", __func__);
return -1;
}
const size_t pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
if (!pos) {
printk(BIOS_ERR, "%s: Device not MSI-X capable\n",
dev_path(dev));
return -1;
}
nr_entries = pci_msix_table_size(dev);
if (nvec > nr_entries) {
printk(BIOS_ERR, "ERROR: %s: Specified to many table entries\n",
dev_path(dev));
return nr_entries;
}
/* Ensure MSI-X is disabled while it is set up */
control = pci_read_config16(dev, pos + PCI_MSIX_FLAGS);
control &= ~PCI_MSIX_FLAGS_ENABLE;
pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
/* Find MSI-X table region */
offset = 0;
bar_idx = 0;
if (pci_msix_table_bar(dev, &offset, &bar_idx)) {
printk(BIOS_ERR, "ERROR: %s: Failed to find MSI-X entry\n",
dev_path(dev));
return -1;
}
bar = get_bar_val(dev, bar_idx);
if (!bar) {
printk(BIOS_ERR, "ERROR: %s: Failed to find MSI-X bar\n",
dev_path(dev));
return -1;
}
msixtable = (struct msix_entry *)((void *)bar + offset);
/*
* Some devices require MSI-X to be enabled before we can touch the
* MSI-X registers. We need to mask all the vectors to prevent
* interrupts coming in before they're fully set up.
*/
control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE;
pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
for (i = 0; i < nvec; i++) {
write64(&msixtable[i].addr, entries[i].addr);
write32(&msixtable[i].data, entries[i].data);
write32(&msixtable[i].vec_control, entries[i].vec_control);
}
control &= ~PCI_MSIX_FLAGS_MASKALL;
pci_write_config16(dev, pos + PCI_MSIX_FLAGS, control);
return 0;
}
static void ecam0_init(struct device *dev)
{
struct soc_cavium_common_pci_config *config;
struct device *child, *child_last;
size_t i;
u32 reg32;
printk(BIOS_INFO, "ECAM0: init\n");
const struct device *bridge = dev_find_slot(0, PCI_DEVFN(1, 0));
if (!bridge) {
printk(BIOS_INFO, "ECAM0: ERROR: PCI 00:01.0 not found.\n");
return;
}
/**
* Search for missing devices on BUS 1.
* Only required for ARI capability programming.
*/
ecam0_fix_missing_devices(bridge->link_list);
/* Program secure ARI capability on bus 1 */
child_last = NULL;
for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
child = dev_find_slot(bridge->link_list->secondary, i);
if (!child || !child->enabled)
continue;
if (child_last) {
/* Program ARI capability of the previous device */
reg32 = pci_read_config32(child_last,
CAVM_PCCPF_XXX_VSEC_SCTL);
reg32 &= ~(0xffU << 24);
reg32 |= child->path.pci.devfn << 24;
pci_write_config32(child_last, CAVM_PCCPF_XXX_VSEC_SCTL,
reg32);
}
child_last = child;
}
/* Program insecure ARI capability on bus 1 */
child_last = NULL;
for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
child = dev_find_slot(bridge->link_list->secondary, i);
if (!child)
continue;
config = child->chip_info;
if (!child->enabled || (config && config->secure))
continue;
if (child_last) {
/* Program ARI capability of the previous device */
reg32 = pci_read_config32(child_last,
CAVM_PCCPF_XXX_VSEC_CTL);
reg32 &= ~(0xffU << 24);
reg32 |= child->path.pci.devfn << 24;
pci_write_config32(child_last, CAVM_PCCPF_XXX_VSEC_CTL,
reg32);
}
child_last = child;
}
/* Enable / disable devices on bus 0 */
for (i = 0; i <= 0x1f; i++) {
child = dev_find_slot(0, PCI_DEVFN(i, 0));
config = child ? child->chip_info : NULL;
if (child && child->enabled && config && !config->secure)
enable_device(i);
else
disable_device(i);
}
/* Enable / disable devices and functions on bus 1 */
for (i = 0; i <= PCI_DEVFN(0x1f, 7); i++) {
child = dev_find_slot(bridge->link_list->secondary, i);
config = child ? child->chip_info : NULL;
if (child && child->enabled &&
((config && !config->secure) || !config))
enable_func(i);
else
disable_func(i);
}
/* Apply IRQ on PCI devices */
/* UUA */
for (i = 0; i < 4; i++) {
child = dev_find_slot(bridge->link_list->secondary,
PCI_DEVFN(8, i));
if (!child)
continue;
struct msix_entry entry[2] = {
{.addr = CAVM_GICD_SETSPI_NSR, .data = 37 + i},
{.addr = CAVM_GICD_CLRSPI_NSR, .data = 37 + i},
};
ecam0_pci_enable_msix(child, entry, 2);
}
printk(BIOS_INFO, "ECAM0: done\n");
}
struct device_operations pci_domain_ops_ecam0 = {
.set_resources = NULL,
.enable_resources = NULL,
.read_resources = ecam0_read_resources,
.init = ecam0_init,
.scan_bus = pci_domain_scan_bus,
};

View File

@ -52,6 +52,10 @@
/* PCC */
#define ECAM_PF_BAR2 0x848000000000ULL
#define ECAM0_DEVX_NSDIS 0x87e048070000ULL
#define ECAM0_DEVX_SDIS 0x87e048060000ULL
#define ECAM0_RSLX_NSDIS 0x87e048050000ULL
#define ECAM0_RSLX_SDIS 0x87e048040000ULL
/* CPT */
/* SLI */
@ -102,6 +106,8 @@
((((x) == 0) || ((x) == 1) || ((x) == 2) || ((x) == 3)) ? \
(0x87E028000000ULL + ((x) << 24)) : 0)
#define CAVM_GICD_SETSPI_NSR 0x801000000040ULL
#define CAVM_GICD_CLRSPI_NSR 0x801000000048ULL
/* TWSI */
#define MIO_TWS0_PF_BAR0 (0x87E0D0000000ULL + (0 << 24))

View File

@ -0,0 +1,21 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __COREBOOT_SRC_SOC_CAVIUM_COMMON_INCLUDE_SOC_ECAM0_H
#define __COREBOOT_SRC_SOC_CAVIUM_COMMON_INCLUDE_SOC_ECAM0_H
extern struct device_operations pci_domain_ops_ecam0;
#endif

View File

@ -29,6 +29,7 @@
#include <string.h>
#include <symbols.h>
#include <libbdk-boot/bdk-boot.h>
#include <soc/ecam0.h>
static void soc_read_resources(device_t dev)
{
@ -59,7 +60,12 @@ static struct device_operations soc_ops = {
static void enable_soc_dev(device_t dev)
{
dev->ops = &soc_ops;
if (dev->path.type == DEVICE_PATH_DOMAIN &&
dev->path.domain.domain == 0) {
dev->ops = &pci_domain_ops_ecam0;
} else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) {
dev->ops = &soc_ops;
}
}
struct chip_operations soc_cavium_cn81xx_ops = {

View File

@ -0,0 +1,27 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2018-present Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_CAVIUM_COMMON_PCI_CHIP_H
#define __SOC_CAVIUM_COMMON_PCI_CHIP_H
struct soc_cavium_common_pci_config {
/**
* Mark the PCI device as secure.
* It will be visible from EL3, but hidden in EL2-0.
*/
u8 secure;
};
#endif /* __SOC_CAVIUM_COMMON_PCI_CHIP_H */