summaryrefslogtreecommitdiff
path: root/src/soc/cavium
diff options
context:
space:
mode:
authorPatrick Rudolph <patrick.rudolph@9elements.com>2018-04-17 13:47:55 +0200
committerPhilipp Deppenwiese <zaolin.daisuki@gmail.com>2018-07-19 13:57:24 +0000
commitd0c6797e796af155cd435ed344958dbb9c418a86 (patch)
tree57bc66cdea7783d3d6025d7df458fc790f4bece6 /src/soc/cavium
parent02c08147645d37e8d21f89b62cb7029be7543bd6 (diff)
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>
Diffstat (limited to 'src/soc/cavium')
-rw-r--r--src/soc/cavium/cn81xx/Kconfig5
-rw-r--r--src/soc/cavium/cn81xx/Makefile.inc1
-rw-r--r--src/soc/cavium/cn81xx/ecam0.c374
-rw-r--r--src/soc/cavium/cn81xx/include/soc/addressmap.h6
-rw-r--r--src/soc/cavium/cn81xx/include/soc/ecam0.h21
-rw-r--r--src/soc/cavium/cn81xx/soc.c8
-rw-r--r--src/soc/cavium/common/pci/chip.h27
7 files changed, 441 insertions, 1 deletions
diff --git a/src/soc/cavium/cn81xx/Kconfig b/src/soc/cavium/cn81xx/Kconfig
index 9cc7b48438..3191ef3c16 100644
--- a/src/soc/cavium/cn81xx/Kconfig
+++ b/src/soc/cavium/cn81xx/Kconfig
@@ -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
diff --git a/src/soc/cavium/cn81xx/Makefile.inc b/src/soc/cavium/cn81xx/Makefile.inc
index 16aa4ff2ab..b2de484ac8 100644
--- a/src/soc/cavium/cn81xx/Makefile.inc
+++ b/src/soc/cavium/cn81xx/Makefile.inc
@@ -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
diff --git a/src/soc/cavium/cn81xx/ecam0.c b/src/soc/cavium/cn81xx/ecam0.c
new file mode 100644
index 0000000000..cff25076f7
--- /dev/null
+++ b/src/soc/cavium/cn81xx/ecam0.c
@@ -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,
+};
diff --git a/src/soc/cavium/cn81xx/include/soc/addressmap.h b/src/soc/cavium/cn81xx/include/soc/addressmap.h
index 938dd3233d..392c93f1c1 100644
--- a/src/soc/cavium/cn81xx/include/soc/addressmap.h
+++ b/src/soc/cavium/cn81xx/include/soc/addressmap.h
@@ -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))
diff --git a/src/soc/cavium/cn81xx/include/soc/ecam0.h b/src/soc/cavium/cn81xx/include/soc/ecam0.h
new file mode 100644
index 0000000000..1cc249d92d
--- /dev/null
+++ b/src/soc/cavium/cn81xx/include/soc/ecam0.h
@@ -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
diff --git a/src/soc/cavium/cn81xx/soc.c b/src/soc/cavium/cn81xx/soc.c
index 6c68bb2fe5..5e540a6765 100644
--- a/src/soc/cavium/cn81xx/soc.c
+++ b/src/soc/cavium/cn81xx/soc.c
@@ -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 = {
diff --git a/src/soc/cavium/common/pci/chip.h b/src/soc/cavium/common/pci/chip.h
new file mode 100644
index 0000000000..0d0d33f59d
--- /dev/null
+++ b/src/soc/cavium/common/pci/chip.h
@@ -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 */