| /* |
| * 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; |
| } |