diff options
Diffstat (limited to 'src/soc/cavium')
-rw-r--r-- | src/soc/cavium/cn81xx/include/soc/addressmap.h | 2 | ||||
-rw-r--r-- | src/soc/cavium/cn81xx/soc.c | 269 |
2 files changed, 271 insertions, 0 deletions
diff --git a/src/soc/cavium/cn81xx/include/soc/addressmap.h b/src/soc/cavium/cn81xx/include/soc/addressmap.h index 392c93f1c1..f6983064fc 100644 --- a/src/soc/cavium/cn81xx/include/soc/addressmap.h +++ b/src/soc/cavium/cn81xx/include/soc/addressmap.h @@ -94,6 +94,8 @@ (0x87E090000000ULL + ((x) << 24)) : 0) /* PEM */ +#define PEM_PEMX_PF_BAR0(x) (0x87e0c0000000ULL + 0x1000000ULL * (x)) + /* SATA */ /* USB */ diff --git a/src/soc/cavium/cn81xx/soc.c b/src/soc/cavium/cn81xx/soc.c index 5e540a6765..370e2e83a9 100644 --- a/src/soc/cavium/cn81xx/soc.c +++ b/src/soc/cavium/cn81xx/soc.c @@ -30,6 +30,264 @@ #include <symbols.h> #include <libbdk-boot/bdk-boot.h> #include <soc/ecam0.h> +#include <console/uart.h> +#include <libbdk-hal/bdk-pcie.h> +#include <soc/ecam0.h> +#include <device/pci.h> +#include <libbdk-hal/bdk-qlm.h> +#include <libbdk-hal/bdk-config.h> +#include <libbdk-arch/bdk-csrs-bgx.h> + +#include <fit.h> + +static const char *QLM_BGX_MODE_MAP[BDK_QLM_MODE_LAST] = { + [BDK_QLM_MODE_SGMII_4X1] = "sgmii", + [BDK_QLM_MODE_SGMII_2X1] = "sgmii", + [BDK_QLM_MODE_SGMII_1X1] = "sgmii", + [BDK_QLM_MODE_XAUI_1X4] = "xaui", + [BDK_QLM_MODE_RXAUI_2X2] = "rxaui", + [BDK_QLM_MODE_RXAUI_1X2] = "rxaui", + [BDK_QLM_MODE_XFI_4X1] = "xfi", + [BDK_QLM_MODE_XFI_2X1] = "xfi", + [BDK_QLM_MODE_XFI_1X1] = "xfi", + [BDK_QLM_MODE_XLAUI_1X4] = "xlaui", + [BDK_QLM_MODE_10G_KR_4X1] = "xfi-10g-kr", + [BDK_QLM_MODE_10G_KR_2X1] = "xfi-10g-kr", + [BDK_QLM_MODE_10G_KR_1X1] = "xfi-10g-kr", + [BDK_QLM_MODE_40G_KR4_1X4] = "xlaui-40g-kr", + [BDK_QLM_MODE_QSGMII_4X1] = "qsgmii", +}; + +static void dt_platform_fixup_phy(struct device_tree_node *node, char *path, + int64_t phy_address, bdk_qlm_modes_t qlm_mode) +{ + char *data = NULL; + size_t size = 0; + dt_find_bin_prop(node, "qlm-mode", (void **)&data, &size); + + if (!data || strncmp(data, path, 6) != 0) + return; /* No key prefix match. */ + printk(BIOS_INFO, "%s: Node %s = %s\n", __func__, node->name, data); + + if (strlen(path) == strlen(data) && strcmp(data, path) == 0) { + /* Keep node, remove "qlm-mode" property */ + dt_delete_prop(node, "qlm-mode"); + printk(BIOS_INFO, "%s: Removing qlm-mode on " + "node %s\n", __func__, node->name); + /* Linux only access the Phy via MDIO. + Remove 'phy-handle' if this option is not available */ + switch (qlm_mode) { + case BDK_QLM_MODE_SGMII_4X1: + case BDK_QLM_MODE_SGMII_2X1: + case BDK_QLM_MODE_SGMII_1X1: + case BDK_QLM_MODE_QSGMII_4X1: + if ((phy_address & BDK_IF_PHY_TYPE_MASK) != + BDK_IF_PHY_MDIO) { + dt_delete_prop(node, "phy-handle"); + printk(BIOS_INFO, "%s: Removing phy-handle on " + "node %s\n", __func__, node->name); + } + break; + default: + break; + } + } else { + printk(BIOS_INFO, "%s: Removing node %s\n", __func__, + node->name); + /* No match, remove node */ + list_remove(&node->list_node); + } +} + +static void dt_iterate_phy(struct device_tree_node *parent, + const char *name, + char *path, + int64_t phy_address, + bdk_qlm_modes_t qlm_mode) +{ + struct device_tree_property *prop; + + /* Check if parent itself has the required property value. */ + list_for_each(prop, parent->properties, list_node) { + if (!strcmp(name, prop->prop.name)) { + dt_platform_fixup_phy(parent, path, phy_address, + qlm_mode); + } + } + + struct device_tree_node *child; + list_for_each(child, parent->children, list_node) { + dt_iterate_phy(child, name, path, phy_address, qlm_mode); + } +} + +static void dt_platform_fixup_mac(struct device_tree_node *node) +{ + const char *name = "local-mac-address"; + u64 *localmac = NULL; + size_t size = 0; + + dt_find_bin_prop(node, name, (void **)&localmac, &size); + + if (!localmac) + return; + static size_t used_mac; + + /* Extract our MAC address info so we can assign them */ + size_t next_free_mac_address = + bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS); + size_t num_free_mac_addresses = + bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS_NUM); + size_t num_free_override = + bdk_config_get_int(BDK_CONFIG_MAC_ADDRESS_NUM_OVERRIDE); + if (num_free_override != -1) + num_free_mac_addresses = num_free_override; + + if (size == 6) { + if (*localmac) + return; + if (used_mac < num_free_mac_addresses) { + *localmac = next_free_mac_address + used_mac; + dt_add_bin_prop(node, name, (void *)&localmac, 6); + used_mac++; + return; + } + } + + printk(BIOS_INFO, "%s: Removing node %s\n", __func__, node->name); + list_remove(&node->list_node); +} + +static void dt_iterate_mac(struct device_tree_node *parent) +{ + struct device_tree_property *prop; + const char *name = "local-mac-address"; + + /* Check if parent itself has the required property value. */ + list_for_each(prop, parent->properties, list_node) { + if (!strcmp(name, prop->prop.name)) + dt_platform_fixup_mac(parent); + } + + struct device_tree_node *child; + list_for_each(child, parent->children, list_node) { + dt_iterate_mac(child); + } +} + +/* Do additional device_tree modifications. */ +static int dt_platform_fixup(struct device_tree_fixup *fixup, + struct device_tree *tree) +{ + struct device_tree_node *dt_node; + size_t i; + + /* Set the sclk clock rate. */ + dt_node = dt_find_node_by_path(tree->root, "soc@0/sclk", NULL, NULL, 0); + if (dt_node) { + const u32 freq = thunderx_get_io_clock(); + printk(BIOS_INFO, "%s: Set SCLK to %u Hz\n", __func__, freq); + dt_add_u32_prop(dt_node, "clock-frequency", freq); + } else + printk(BIOS_ERR, "%s: Node not found. OS might miss-behave !\n", + __func__); + + /* Set refclkuaa clock rate. */ + dt_node = dt_find_node_by_path(tree->root, "soc@0/refclkuaa", NULL, + NULL, 0); + if (dt_node) { + const u32 freq = uart_platform_refclk(); + printk(BIOS_INFO, "%s: Set REFCLKUAA to %u Hz\n", __func__, + freq); + dt_add_u32_prop(dt_node, "clock-frequency", freq); + } else + printk(BIOS_ERR, "%s: Node not found. OS might miss-behave !\n", + __func__); + + /* Remove unused PEM entries */ + for (i = 0; i < 8; i++) { + char path[32]; + u32 phandle = 0; + const uint64_t addr = PEM_PEMX_PF_BAR0(i); + /* Remove the node */ + snprintf(path, sizeof(path), "soc@0/pci@%llx", addr); + dt_node = dt_find_node_by_path(tree->root, path, NULL, NULL, 0); + if (!dt_node || bdk_pcie_is_running(0, i)) { + printk(BIOS_INFO, "%s: ignoring %s\n", __func__, path); + continue; + } + /* Store the phandle */ + phandle = dt_get_phandle(dt_node); + printk(BIOS_INFO, "%s: Removing node %s\n", __func__, path); + list_remove(&dt_node->list_node); + + /* Remove phandle to non existing nodes */ + snprintf(path, sizeof(path), "soc@0/smmu0@%llx", SMMU_PF_BAR0); + dt_node = dt_find_node_by_path(tree->root, path, NULL, NULL, 0); + if (!dt_node) { + printk(BIOS_ERR, "%s: SMMU entry not found\n", + __func__); + continue; + } + u32 *data = NULL; + size_t size = 0; + dt_find_bin_prop(dt_node, "mmu-masters", (void **)&data, &size); + if (!size) { + printk(BIOS_ERR, "%s: mmu-masters entry not found\n", + __func__); + continue; + } + for (size_t j = 0; j < size / (sizeof(u32) * 2); j++) + if (be32_to_cpu(data[j * 2]) == phandle) { + data[j * 2] = 0; + data[j * 2 + 1] = 0; + break; + } + } + + /* Remove QLM mode entries */ + size_t bgx_index, bgx_iface; + for (bgx_iface = 0; bgx_iface < 4; bgx_iface++) { + for (bgx_index = 0; bgx_index < 4; bgx_index++) { + char path[32]; + int qlm = bdk_qlm_get_qlm_num(0, BDK_IF_BGX, + bgx_iface, bgx_index); + bdk_qlm_modes_t qlm_mode = (qlm == -1) ? + BDK_QLM_MODE_DISABLED : + bdk_qlm_get_mode(0, qlm); + + /* BGXX_CMRX_RX_DMAC_CTL is used to mark ports as + * disabled that would otherwise be enabled */ + if (qlm_mode != BDK_QLM_MODE_DISABLED) { + BDK_CSR_INIT(rx_dmac_ctl, 0, + BDK_BGXX_CMRX_RX_DMAC_CTL(bgx_iface, + bgx_index)); + if (rx_dmac_ctl.u == 0) + qlm_mode = BDK_QLM_MODE_DISABLED; + } + + if (qlm_mode == BDK_QLM_MODE_DISABLED) + snprintf(path, sizeof(path), "0x0%x%x,disabled", + bgx_iface, bgx_index); + else + snprintf(path, sizeof(path), "0x0%x%x,%s", + bgx_iface, bgx_index, + QLM_BGX_MODE_MAP[qlm_mode]); + + int64_t phy_address = + bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, 0, + bgx_iface, bgx_index); + + dt_iterate_phy(tree->root, "qlm-mode", path, + phy_address, qlm_mode); + } + } + + /* Set local MAC address */ + dt_iterate_mac(tree->root); + + return 0; +} static void soc_read_resources(device_t dev) { @@ -42,6 +300,17 @@ static void soc_init(device_t dev) bdk_boot(); /* TODO: additional trustzone init */ + + if (IS_ENABLED(CONFIG_PAYLOAD_FIT_SUPPORT)) { + struct device_tree_fixup *dt_fixup; + + dt_fixup = malloc(sizeof(*dt_fixup)); + if (dt_fixup) { + dt_fixup->fixup = dt_platform_fixup; + list_insert_after(&dt_fixup->list_node, + &device_tree_fixups); + } + } } static void soc_final(device_t dev) |