diff options
author | Stefan Roese <sr@denx.de> | 2021-04-07 09:12:32 +0200 |
---|---|---|
committer | Daniel Schwierzeck <daniel.schwierzeck@gmail.com> | 2021-04-23 21:22:55 +0200 |
commit | 480fa8346b17cd533be9d870e10cb6a07fcd2c7e (patch) | |
tree | dbf94a3d7634ea1c987e333f7332297f047081c7 /arch/mips | |
parent | f1054661e50fc553bf049c7134c58c46fdb50285 (diff) | |
download | u-boot-480fa8346b17cd533be9d870e10cb6a07fcd2c7e.tar.gz |
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 <sr@denx.de>
Cc: Aaron Williams <awilliams@marvell.com>
Cc: Chandrakala Chavva <cchavva@marvell.com>
Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/mach-octeon/cpu.c | 327 |
1 files changed, 327 insertions, 0 deletions
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 <dm.h> +#include <dm/uclass.h> +#include <env.h> +#include <iomux.h> #include <asm/global_data.h> #include <linux/bitfield.h> #include <linux/bitops.h> @@ -10,10 +14,25 @@ #include <linux/io.h> #include <mach/clock.h> #include <mach/cavm-reg.h> +#include <mach/cvmx-bootmem.h> 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. * PCI mandates little-endian, USB and SATA are configurable, @@ -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; +} |