From 47dedfb5b9afbd5b71bb3e98c7bae09dec6c2b18 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 7 Apr 2021 09:12:32 +0200 Subject: [PATCH] mips: octeon: cpu.c: Add arch_misc_init() for pci-console & pci-bootcmd This patch adds the necessary platform infrastructure code, so that the MIPS Octeon drivers "serial_octeon_pcie_console" & "serial_bootcmd" can be used. This is e.g. the bootmem initialization in a compatible way to the Marvell 2013 U-Boot, so that the exisiting PC remote tools like "oct-remote-console" & "oct-remote-load" can be used. This is be done in the newly introduced arch_misc_init(), which calls the necessary init functions when enabled. These patches are in preparation for the MIPS Octeon NIC23 board support, which is a desktop PCIe target board enabling these features. Signed-off-by: Stefan Roese Cc: Aaron Williams Cc: Chandrakala Chavva Cc: Daniel Schwierzeck --- arch/mips/mach-octeon/cpu.c | 327 ++++++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) diff --git a/arch/mips/mach-octeon/cpu.c b/arch/mips/mach-octeon/cpu.c index 6f87a4ef8c..3fde9fbc47 100644 --- a/arch/mips/mach-octeon/cpu.c +++ b/arch/mips/mach-octeon/cpu.c @@ -3,6 +3,10 @@ * Copyright (C) 2020 Marvell International Ltd. */ +#include +#include +#include +#include #include #include #include @@ -10,9 +14,24 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; +/* + * Important: + * This address cannot be changed as the PCI console tool relies on exactly + * this value! + */ +#define BOOTLOADER_BOOTMEM_DESC_ADDR 0x6c100 +#define BOOTLOADER_BOOTMEM_DESC_SPACE (BOOTLOADER_BOOTMEM_DESC_ADDR + 0x8) + +#define OCTEON_RESERVED_LOW_BOOT_MEM_SIZE (1024 * 1024) + +#define BOOTCMD_NAME "pci-bootcmd" +#define CONSOLE_NAME "pci-console@0" +#define OCTEON_BOOTLOADER_LOAD_MEM_NAME "__tmp_load" + /* * TRUE for devices having registers with little-endian byte * order, FALSE for registers with native-endian byte order. @@ -85,3 +104,311 @@ int print_cpuinfo(void) return 0; } + +static int octeon_bootmem_init(void) +{ + int ret; + + /* Call old single-node func: it uses only gd->ram_size */ + ret = cvmx_bootmem_phy_mem_list_init(gd->ram_size, + OCTEON_RESERVED_LOW_BOOT_MEM_SIZE, + (void *)CKSEG0ADDR(BOOTLOADER_BOOTMEM_DESC_SPACE)); + if (!ret) { + printf("FATAL: Error initializing bootmem list\n"); + return -ENOSPC; + } + + /* + * Put bootmem descriptor address in known location for host. + * Make sure it is not in kseg0, as we want physical address + */ + writeq((u64)__cvmx_bootmem_internal_get_desc_ptr() & 0x7fffffffull, + (void *)CKSEG0ADDR(BOOTLOADER_BOOTMEM_DESC_ADDR)); + + debug("Reserving first 1MB of memory\n"); + ret = cvmx_bootmem_reserve_memory(0, OCTEON_RESERVED_LOW_BOOT_MEM_SIZE, + "__low_reserved", 0); + if (!ret) + puts("Error reserving low 1MB of memory\n"); + +#ifdef DEBUG + cvmx_bootmem_phy_list_print(); +#endif + + return 0; +} + +static int octeon_configure_load_memory(void) +{ + char *eptr; + u32 addr; + u32 size; + int ret; + + eptr = env_get("octeon_reserved_mem_load_size"); + if (!eptr || !strcmp("auto", eptr)) { + /* + * Pick a size that we think is appropriate. + * Please note that for small memory boards this guess + * will likely not be ideal. + * Please pick a specific size for boards/applications + * that require it. + */ + if (gd->ram_size <= (256 << 20)) { + size = min_t(u64, (128 << 20), + ((gd->ram_size * 2) / 5) & ~0xFFFFF); + } else { + size = min_t(u64, (256 << 20), + ((gd->ram_size - (256 << 20)) / 3) & ~0xFFFFF); + } + } else { + size = simple_strtol(eptr, NULL, 16); + debug("octeon_reserved_mem_load_size=0x%08x\n", size); + } + + if (size) { + debug("Linux reserved load size 0x%08x\n", size); + eptr = env_get("octeon_reserved_mem_load_base"); + if (!eptr || !strcmp("auto", eptr)) { + u64 mem_top; + /* + * Leave some room for previous allocations that + * are made starting at the top of the low + * 256 Mbytes of DRAM + */ + int adjust = (1 << 20); + + if (gd->ram_size <= (512 << 20)) + adjust = (17 << 20); + + /* Put block at the top of DDR0, or bottom of DDR2 */ + if ((gd->ram_size <= (256 << 20)) || + (size > (gd->ram_size - (256 << 20)))) { + mem_top = min_t(u64, gd->ram_size - adjust, + (256 << 20) - adjust); + } else if ((gd->ram_size <= (512 << 20)) || + (size > (gd->ram_size - (512 << 20)))) { + mem_top = min_t(u64, gd->ram_size - adjust, + (512 << 20) - adjust); + } else { + /* + * We have enough room, so set + * mem_top so that the block is + * at the base of the DDR2 + * segment + */ + mem_top = (512 << 20) + size; + } + + /* + * Adjust for boot bus memory hole on OCTEON II + * and later. + */ + if ((gd->ram_size > (256 << 20))) + mem_top += (256 << 20); + + debug("Adjusted memory top is 0x%llx\n", mem_top); + addr = mem_top - size; + if (addr > (512 << 20)) + addr = (512 << 20); + if ((addr >= (256 << 20)) && addr < (512 << 20)) { + /* + * The address landed in the boot-bus + * memory hole. Dig it out of the hole. + */ + addr = (512 << 20); + } + } else { + addr = simple_strtol(eptr, NULL, 16); + } + + ret = cvmx_bootmem_phy_named_block_alloc(size, addr, + addr + size, 0, + OCTEON_BOOTLOADER_LOAD_MEM_NAME, + 0); + if (ret < 0) { + printf("ERROR: Unable to allocate bootloader reserved memory (addr: 0x%x, size: 0x%x).\n", + addr, size); + } else { + /* + * Set default load address to base of memory + * reserved for loading. The setting of the + * env. variable also sets the load_addr global + * variable. + * This environment variable is overridden each + * boot if a reserved block is created. + */ + char str[20]; + + snprintf(str, sizeof(str), "0x%x", addr); + env_set("loadaddr", str); + debug("Setting load address to 0x%08x, size 0x%x\n", + addr, size); + } + return 0; + } + + printf("WARNING: No reserved memory for image loading.\n"); + return -1; +} + +static int init_pcie_console(void) +{ + char *stdinname = env_get("stdin"); + char *stdoutname = env_get("stdout"); + char *stderrname = env_get("stderr"); + struct udevice *pcie_console_dev = NULL; + bool stdin_set, stdout_set, stderr_set; + char iomux_name[128]; + int ret = 0; + + debug("%s: stdin: %s, stdout: %s, stderr: %s\n", __func__, stdinname, + stdoutname, stderrname); + if (!stdinname) { + env_set("stdin", "serial"); + stdinname = env_get("stdin"); + } + if (!stdoutname) { + env_set("stdout", "serial"); + stdoutname = env_get("stdout"); + } + if (!stderrname) { + env_set("stderr", "serial"); + stderrname = env_get("stderr"); + } + + if (!stdinname || !stdoutname || !stderrname) { + printf("%s: Error setting environment variables for serial\n", + __func__); + return -1; + } + + stdin_set = !!strstr(stdinname, CONSOLE_NAME); + stdout_set = !!strstr(stdoutname, CONSOLE_NAME); + stderr_set = !!strstr(stderrname, CONSOLE_NAME); + + log_debug("stdin: %d, \"%s\", stdout: %d, \"%s\", stderr: %d, \"%s\"\n", + stdin_set, stdinname, stdout_set, stdoutname, + stderr_set, stderrname); + ret = uclass_get_device_by_name(UCLASS_SERIAL, CONSOLE_NAME, + &pcie_console_dev); + if (ret || !pcie_console_dev) { + debug("%s: No PCI console device %s found\n", __func__, + CONSOLE_NAME); + return 0; + } + + if (stdin_set) + strncpy(iomux_name, stdinname, sizeof(iomux_name)); + else + snprintf(iomux_name, sizeof(iomux_name), "%s,%s", + stdinname, pcie_console_dev->name); + + ret = iomux_doenv(stdin, iomux_name); + if (ret) { + log_err("%s: Error setting I/O stdin MUX to %s\n", + __func__, iomux_name); + return ret; + } + + if (!stdin_set) + env_set("stdin", iomux_name); + + if (stdout_set) + strncpy(iomux_name, stdoutname, sizeof(iomux_name)); + else + snprintf(iomux_name, sizeof(iomux_name), "%s,%s", stdoutname, + pcie_console_dev->name); + + ret = iomux_doenv(stdout, iomux_name); + if (ret) { + log_err("%s: Error setting I/O stdout MUX to %s\n", + __func__, iomux_name); + return ret; + } + if (!stdout_set) + env_set("stdout", iomux_name); + + if (stderr_set) + strncpy(iomux_name, stderrname, sizeof(iomux_name)); + else + snprintf(iomux_name, sizeof(iomux_name), "%s,%s", stderrname, + pcie_console_dev->name); + + ret = iomux_doenv(stderr, iomux_name); + if (ret) { + log_err("%s: Error setting I/O stderr MUX to %s\n", + __func__, iomux_name); + return ret; + } + + if (!stderr_set) + env_set("stderr", iomux_name); + + debug("%s: stdin: %s, stdout: %s, stderr: %s, ret: %d\n", + __func__, env_get("stdin"), env_get("stdout"), + env_get("stderr"), ret); + + return ret; +} + +static int init_bootcmd_console(void) +{ + char *stdinname = env_get("stdin"); + struct udevice *bootcmd_dev = NULL; + bool stdin_set; + char iomux_name[128]; + int ret = 0; + + debug("%s: stdin before: %s\n", __func__, + stdinname ? stdinname : "NONE"); + if (!stdinname) { + env_set("stdin", "serial"); + stdinname = env_get("stdin"); + } + stdin_set = !!strstr(stdinname, BOOTCMD_NAME); + ret = uclass_get_device_by_driver(UCLASS_SERIAL, + DM_DRIVER_GET(octeon_bootcmd), + &bootcmd_dev); + if (ret) { + log_err("%s: Error getting %s serial class\n", __func__, + BOOTCMD_NAME); + } else if (bootcmd_dev) { + if (stdin_set) + strncpy(iomux_name, stdinname, sizeof(iomux_name)); + else + snprintf(iomux_name, sizeof(iomux_name), "%s,%s", + stdinname, bootcmd_dev->name); + ret = iomux_doenv(stdin, iomux_name); + if (ret) + log_err("%s: Error %d enabling the PCI bootcmd input console \"%s\"\n", + __func__, ret, iomux_name); + if (!stdin_set) + env_set("stdin", iomux_name); + } + + debug("%s: Set iomux and stdin to %s (ret: %d)\n", + __func__, iomux_name, ret); + return ret; +} + +int arch_misc_init(void) +{ + int ret; + + ret = octeon_bootmem_init(); + if (ret) + return ret; + + ret = octeon_configure_load_memory(); + if (ret) + return ret; + + if (CONFIG_IS_ENABLED(OCTEON_SERIAL_PCIE_CONSOLE)) + init_pcie_console(); + + if (CONFIG_IS_ENABLED(OCTEON_SERIAL_BOOTCMD)) + init_bootcmd_console(); + + return 0; +} -- 2.39.5