blob: 04a6776521e4db0f930fc36a0b8179be8723a3b5 [file] [log] [blame]
/*
* Copyright (c) 2018 The Fuchsia Authors
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm/arch/reboot.h>
#include <asm/arch/secure_apb.h>
#include <asm/io.h>
#include <common.h>
#include <dm/uclass.h>
#include <fs.h>
#include <version.h>
#include <wdt.h>
#include <zircon_uboot/zircon.h>
#define PDEV_VID_GOOGLE 3
#define PDEV_PID_SHERLOCK 5
#define NVRAM_LENGTH (8 * 1024)
static const zbi_mem_range_t mem_config[] = {
{
.type = ZBI_MEM_RANGE_RAM,
.length = 0x80000000, // 2 GB
},
{
.type = ZBI_MEM_RANGE_PERIPHERAL,
.paddr = 0xf5800000,
.length = 0x0a800000,
},
/* secmon_reserved:linux,secmon */
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x05000000,
.length = 0x2400000,
},
/* logo_reserved:linux,meson-fb */
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x76800000,
.length = 0x800000,
},
/* linux,usable-memory */
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x00000000,
.length = 0x100000,
},
};
static const dcfg_simple_t uart_driver = {
.mmio_phys = 0xff803000,
.irq = 225,
};
static const dcfg_arm_gicv2_driver_t gicv2_driver = {
.mmio_phys = 0xffc00000,
.gicd_offset = 0x1000,
.gicc_offset = 0x2000,
.gich_offset = 0x4000,
.gicv_offset = 0x6000,
.ipi_base = 0,
};
static const dcfg_arm_psci_driver_t psci_driver = {
.use_hvc = false,
.reboot_args = { 1, 0, 0 },
.reboot_bootloader_args = { 4, 0, 0 },
.reboot_recovery_args = { 2, 0, 0 },
};
static const dcfg_arm_generic_timer_driver_t timer_driver = {
.irq_phys = 30,
};
#define WDT_CTRL 0xffd0f0d0
#define WDT_PET 0xffd0f0dc
#define WATCHDOG_TIMEOUT_SECONDS 5
#define SECONDS_TO_NANOSECONDS 1000000000LL
static const dcfg_generic_32bit_watchdog_t watchdog_driver = {
.pet_action = {
.addr = WDT_PET,
.clr_mask = 0xffffffff,
.set_mask = 0x00000000,
},
.enable_action = {
.addr = WDT_CTRL,
.clr_mask = 0x00000000,
.set_mask = 0x00040000,
},
.disable_action = {
.addr = WDT_CTRL,
.clr_mask = 0x00040000,
.set_mask = 0x00000000,
},
.watchdog_period_nsec =
WATCHDOG_TIMEOUT_SECONDS * SECONDS_TO_NANOSECONDS,
.flags = KDRV_GENERIC_32BIT_WATCHDOG_FLAG_ENABLED,
};
static const zbi_platform_id_t platform_id = {
.vid = PDEV_VID_GOOGLE,
.pid = PDEV_PID_SHERLOCK,
.board_name = "sherlock",
};
#define NEWMAN_FACTORY_IF "mmc"
#define NEWMAN_FACTORY_PART "1:5"
#define FACTORY_MAC_ADDR_BUFF_LEN 30
#define NEWMAN_FACTORY_MACADDR_FILE "mac_addr"
#define FACTORY_MAC_ADDR_FILE_LEN 25
static int add_mac_addresses(zbi_header_t* zbi) {
char buffer[FACTORY_MAC_ADDR_BUFF_LEN];
u64 fullmac[2];
u8 mac_addr[6];
int len_read;
int mac_num, i;
if (fs_set_blk_dev(NEWMAN_FACTORY_IF, NEWMAN_FACTORY_PART,
FS_TYPE_EXT)) {
printf("set_blk_dev %s-%s failed.\n",
NEWMAN_FACTORY_IF, NEWMAN_FACTORY_PART);
return -1;
}
if (fs_read(NEWMAN_FACTORY_MACADDR_FILE, buffer, 0,
FACTORY_MAC_ADDR_BUFF_LEN, &len_read)) {
printf("Failed to read Mac Addresses from Factory partition\n");
}
if (len_read != FACTORY_MAC_ADDR_FILE_LEN) {
printf("Factory MAC Addr File length (%d) incorrect.\n",
len_read);
return -1;
}
buffer[len_read] = '\0';
/*
* "buffer" should now contain two hex strings separated by \n,
* for a total of 25 bytes. Separate into 2 C strings and convert.
*/
buffer[len_read/2] = '\0';
fullmac[0] = simple_strtoull(buffer, NULL, 16);
fullmac[1] = simple_strtoull(&buffer[(len_read/2)+1], NULL, 16);
for (mac_num = 0; mac_num < ARRAY_SIZE(fullmac); mac_num++) {
for (i = ARRAY_SIZE(mac_addr)-1; i >= 0; i--) {
mac_addr[i] = (u8) (fullmac[mac_num] & 0xff);
fullmac[mac_num] >>= 8;
}
zircon_append_boot_item(zbi, ZBI_TYPE_DRV_MAC_ADDRESS, mac_num,
mac_addr, sizeof(mac_addr));
}
return 0;
}
static void add_board_info(zbi_header_t* zbi)
{
zbi_board_info_t board_info = {};
char* s;
if (((s = env_get("hw_id")) != NULL) && (*s != '\0')) {
uint32_t hw_id = simple_strtoul(s, NULL, 16);
board_info.revision = hw_id;
} else {
printf("Failed to retrieve Board Revision\n");
}
zircon_append_boot_item(zbi, ZBI_TYPE_DRV_BOARD_INFO, 0, &board_info,
sizeof(board_info));
}
// Define a cpu topology for the system that captures how all of the cores are
// connected and grouped. Since this is a GICv2 system we include the ID for the
// cores, especially since it is a tricky one with a gap between the little and
// big clusters.
static void add_cpu_topology(zbi_header_t* zbi)
{
int index = 0;
int logical_processor = 0;
int big_cluster = 0, little_cluster = 0;
zbi_topology_node_t nodes[8];
little_cluster = index++;
nodes[little_cluster] = (zbi_topology_node_t){
.entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
.parent_index = ZBI_TOPOLOGY_NO_PARENT,
.entity = {
.cluster = {
.performance_class = 0, // low performance cluster
}
}
};
nodes[index++] = (zbi_topology_node_t){
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = little_cluster,
.entity = {
.processor = {
.logical_ids = {logical_processor++},
.logical_id_count = 1,
.flags = ZBI_TOPOLOGY_PROCESSOR_PRIMARY,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info = {
.arm = {
.cluster_1_id = 0,
.cpu_id = 0,
.gic_id = 0,
}
}
}
}
};
nodes[index++] = (zbi_topology_node_t){
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = little_cluster,
.entity = {
.processor = {
.logical_ids = {logical_processor++},
.logical_id_count = 1,
.flags = 0,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info = {
.arm = {
.cluster_1_id = 0,
.cpu_id = 1,
.gic_id = 1,
}
}
}
}
};
big_cluster = index++;
nodes[big_cluster] = (zbi_topology_node_t){
.entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
.parent_index = ZBI_TOPOLOGY_NO_PARENT,
.entity = {
.cluster = {
.performance_class = 1, // high performance cluster
}
}
};
nodes[index++] = (zbi_topology_node_t){
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = big_cluster,
.entity = {
.processor = {
.logical_ids = {logical_processor++},
.logical_id_count = 1,
.flags = 0,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info = {
.arm = {
.cluster_1_id = 1,
.cpu_id = 0,
.gic_id = 4,
}
}
}
}
};
nodes[index++] = (zbi_topology_node_t){
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = big_cluster,
.entity = {
.processor = {
.logical_ids = {logical_processor++},
.logical_id_count = 1,
.flags = 0,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info = {
.arm = {
.cluster_1_id = 1,
.cpu_id = 1,
.gic_id = 5,
}
}
}
}
};
nodes[index++] = (zbi_topology_node_t){
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = big_cluster,
.entity = {
.processor = {
.logical_ids = {logical_processor++},
.logical_id_count = 1,
.flags = 0,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info = {
.arm = {
.cluster_1_id = 1,
.cpu_id = 2,
.gic_id = 6,
}
}
}
}
};
nodes[index++] = (zbi_topology_node_t){
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = big_cluster,
.entity = {
.processor = {
.logical_ids = {logical_processor++},
.logical_id_count = 1,
.flags = 0,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info = {
.arm = {
.cluster_1_id = 1,
.cpu_id = 3,
.gic_id = 7,
}
}
}
}
};
zircon_append_boot_item(zbi, ZBI_TYPE_CPU_TOPOLOGY, sizeof(zbi_topology_node_t),
&nodes, sizeof(zbi_topology_node_t) * index);
}
static void add_reboot_reason(zbi_header_t* zbi) {
// See cmd/amlogic/cmd_reboot.c
const uint32_t reboot_mode_val = ((readl(AO_SEC_SD_CFG15) >> 12) & 0xf);
zbi_hw_reboot_reason_t reboot_reason;
switch (reboot_mode_val) {
case AMLOGIC_COLD_BOOT:
reboot_reason = ZBI_HW_REBOOT_COLD;
break;
case AMLOGIC_NORMAL_BOOT:
case AMLOGIC_FACTORY_RESET_REBOOT:
case AMLOGIC_UPDATE_REBOOT:
case AMLOGIC_FASTBOOT_REBOOT:
case AMLOGIC_SUSPEND_REBOOT:
case AMLOGIC_HIBERNATE_REBOOT:
case AMLOGIC_BOOTLOADER_REBOOT:
case AMLOGIC_SHUTDOWN_REBOOT:
case AMLOGIC_RPMBP_REBOOT:
case AMLOGIC_QUIESCENT_REBOOT:
case AMLOGIC_CRASH_REBOOT:
case AMLOGIC_KERNEL_PANIC:
case AMLOGIC_RECOVERY_QUIESCENT_REBOOT:
reboot_reason = ZBI_HW_REBOOT_WARM;
break;
case AMLOGIC_WATCHDOG_REBOOT:
reboot_reason = ZBI_HW_REBOOT_WATCHDOG;
break;
default:
reboot_reason = ZBI_HW_REBOOT_UNDEFINED;
break;
}
zircon_append_boot_item(zbi, ZBI_TYPE_HW_REBOOT_REASON, 0,
&reboot_reason, sizeof(reboot_reason));
}
#define WATCHDOG_DEV_NAME "watchdog"
static void start_meson_watchdog(void) {
struct udevice *wdt_dev;
int ret = uclass_get_device_by_name(
UCLASS_WDT, WATCHDOG_DEV_NAME, &wdt_dev);
if (ret < 0) {
printf("failed to get meson watchdog\n");
return;
}
printf("starting meson watchdog\n");
// Note: the watchdog uclass expects a timeout in milliseconds, but the
// Amlogic code uses seconds instead.
wdt_start(wdt_dev, WATCHDOG_TIMEOUT_SECONDS, 0);
wdt_reset(wdt_dev);
}
int zircon_preboot(zbi_header_t *zbi)
{
/*
* allocate crashlog save area before 0x5f800000-0x60000000
* reserved area
*/
zbi_nvram_t nvram;
nvram.base = 0x5f800000 - NVRAM_LENGTH;
nvram.length = NVRAM_LENGTH;
zircon_append_boot_item(zbi, ZBI_TYPE_NVRAM, 0, &nvram, sizeof(nvram));
/* add memory configuration */
zircon_append_boot_item(zbi, ZBI_TYPE_MEM_CONFIG, 0, &mem_config,
sizeof(mem_config));
/* add kernel drivers */
zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_AMLOGIC_UART,
&uart_driver, sizeof(uart_driver));
zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V2,
&gicv2_driver, sizeof(gicv2_driver));
zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_PSCI,
&psci_driver, sizeof(psci_driver));
zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER,
KDRV_ARM_GENERIC_TIMER,
&timer_driver, sizeof(timer_driver));
zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER,
KDRV_GENERIC_32BIT_WATCHDOG,
&watchdog_driver, sizeof(watchdog_driver));
const char* bootloader_type = "bootloader-type=U-Boot";
zircon_append_boot_item(zbi, ZBI_TYPE_CMDLINE, 0,
bootloader_type,
strlen(bootloader_type)+1);
char uboot_ver[] = "bootloader.name=" U_BOOT_VERSION_STRING;
// Zircon's cmdline parameters cannot contain spaces so
// convert spaces in autogenerated U-boot version string
// to underscores.
// See zircon/docs/kernel_cmdline.md
int i;
int len = strlen(uboot_ver);
for (i = 0; i < len; i++) {
if (uboot_ver[i] == ' ') {
uboot_ver[i] = '_';
}
}
zircon_append_boot_item(zbi, ZBI_TYPE_CMDLINE, 0, uboot_ver, len + 1);
/* Add board-specific information */
add_board_info(zbi);
/* add platform ID */
zircon_append_boot_item(zbi, ZBI_TYPE_PLATFORM_ID, 0, &platform_id,
sizeof(platform_id));
add_cpu_topology(zbi);
int ret = add_mac_addresses(zbi);
if (ret < 0) {
printf("ERROR: unable to read MAC addresses from the"
" factory partition!\n");
}
add_reboot_reason(zbi);
if (watchdog_driver.flags & KDRV_GENERIC_32BIT_WATCHDOG_FLAG_ENABLED) {
start_meson_watchdog();
}
return 0;
}