diff options
author | Patrick Rudolph <patrick.rudolph@9elements.com> | 2018-04-17 13:47:55 +0200 |
---|---|---|
committer | Philipp Deppenwiese <zaolin.daisuki@gmail.com> | 2018-07-19 13:57:24 +0000 |
commit | d0c6797e796af155cd435ed344958dbb9c418a86 (patch) | |
tree | 57bc66cdea7783d3d6025d7df458fc790f4bece6 /src/soc | |
parent | 02c08147645d37e8d21f89b62cb7029be7543bd6 (diff) | |
download | coreboot-d0c6797e796af155cd435ed344958dbb9c418a86.tar.xz |
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')
-rw-r--r-- | src/soc/cavium/cn81xx/Kconfig | 5 | ||||
-rw-r--r-- | src/soc/cavium/cn81xx/Makefile.inc | 1 | ||||
-rw-r--r-- | src/soc/cavium/cn81xx/ecam0.c | 374 | ||||
-rw-r--r-- | src/soc/cavium/cn81xx/include/soc/addressmap.h | 6 | ||||
-rw-r--r-- | src/soc/cavium/cn81xx/include/soc/ecam0.h | 21 | ||||
-rw-r--r-- | src/soc/cavium/cn81xx/soc.c | 8 | ||||
-rw-r--r-- | src/soc/cavium/common/pci/chip.h | 27 |
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 */ |