/* SPDX-License-Identifier: GPL-2.0-only */ /* * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0. */ #include <console/console.h> #include <device/device.h> #include <soc/addressmap.h> #include <soc/clock.h> #include <soc/sdram.h> #include <soc/timer.h> #include <soc/uart.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #include <symbols.h> #include <libbdk-boot/bdk-boot.h> #include <soc/ecam0.h> #include <console/uart.h> #include <libbdk-hal/bdk-pcie.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 <bootmem.h> #include <soc/bl31_plat_params.h> #include <cbfs.h> #include <cbmem.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) { const char *data = NULL; size_t size = 0; dt_find_bin_prop(node, "qlm-mode", (const 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"; const u64 *localmac = NULL; size_t size = 0; dt_find_bin_prop(node, name, (const 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) { u64 genmac = next_free_mac_address + used_mac; dt_add_bin_prop(node, name, &genmac, 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, "/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, "/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 UART entries */ for (i = 0; i < 4; i++) { char path[32]; const uint64_t addr = UAAx_PF_BAR0(i); /* Remove the node */ snprintf(path, sizeof(path), "/soc@0/serial@%llx", addr); dt_node = dt_find_node_by_path(tree, path, NULL, NULL, 0); if (!dt_node || uart_is_enabled(i)) { printk(BIOS_INFO, "%s: ignoring %s\n", __func__, path); continue; } printk(BIOS_INFO, "%s: Removing node %s\n", __func__, path); list_remove(&dt_node->list_node); } /* 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, 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_node->phandle; 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, path, NULL, NULL, 0); if (!dt_node) { printk(BIOS_ERR, "%s: SMMU entry not found\n", __func__); continue; } const u32 *data = NULL; size_t size = 0; dt_find_bin_prop(dt_node, "mmu-masters", (const void **)&data, &size); if (!size) { printk(BIOS_ERR, "%s: mmu-masters entry not found\n", __func__); continue; } u32 *data_cleaned = malloc(size); if (!data_cleaned) continue; size_t n = 0; /* Remove phandle from mmu-masters list */ for (size_t j = 0; j < size / (sizeof(u32) * 2); j++) if (be32_to_cpu(data[j * 2]) != phandle) { data_cleaned[n * 2] = data[j * 2]; data_cleaned[n * 2 + 1] = data[j * 2 + 1]; n++; } dt_add_bin_prop(dt_node, "mmu-masters", data_cleaned, n * sizeof(u32) * 2); free(data_cleaned); } /* 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; } extern u8 _sff8104[]; extern u8 _esff8104[]; void bootmem_platform_add_ranges(void) { bootmem_add_range((uintptr_t)_sff8104, ((uintptr_t)_esff8104 - (uintptr_t)_sff8104), BM_MEM_RESERVED); /* Scratchpad for ATF SATA quirks */ bootmem_add_range((sdram_size_mb() - 1) * MiB, 1 * MiB, BM_MEM_RESERVED); } static void soc_read_resources(struct device *dev) { // HACK: Don't advertise bootblock romstage CAR region, it's broken... ram_from_to(dev, 0, 2 * MiB, sdram_size_mb() * (uint64_t)MiB); } static void soc_init_atf(void) { static struct bl31_fdt_param fdt_param = { .h = { .type = PARAM_FDT, }, }; size_t size = 0; void *ptr = cbfs_map("sff8104-linux.dtb", &size); if (ptr) memcpy(_sff8104, ptr, size); /* Point to devicetree in secure memory */ fdt_param.fdt_ptr = (uintptr_t)_sff8104; cn81xx_register_bl31_param(&fdt_param.h); static struct bl31_u64_param cbtable_param = { .h = { .type = PARAM_COREBOOT_TABLE, }, }; /* Point to coreboot tables */ cbtable_param.value = (uint64_t)cbmem_find(CBMEM_ID_CBTABLE); if (cbtable_param.value) cn81xx_register_bl31_param(&cbtable_param.h); } static void soc_init(struct device *dev) { /* Init ECAM, MDIO, PEM, PHY, QLM ... */ bdk_boot(); if (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); } } if (CONFIG(ARM64_USE_ARM_TRUSTED_FIRMWARE)) soc_init_atf(); } static void soc_final(struct device *dev) { watchdog_disable(0); } static struct device_operations soc_ops = { .read_resources = soc_read_resources, .set_resources = noop_set_resources, .init = soc_init, .final = soc_final, }; static void enable_soc_dev(struct device *dev) { 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 = { CHIP_NAME("SOC Cavium CN81XX") .enable_dev = enable_soc_dev, };