Files
u-boot/board/qualcomm/dragonboard410c/dragonboard410c.c
Stephan Gerhold 0204d1b56b board: dragonboard410c: Load U-Boot directly without LK
At the moment the U-Boot port for the DragonBoard 410c is designed
to be loaded as an Android boot image after Qualcomm's Little Kernel (LK)
bootloader. This is simple to set up but LK is redundant in this case,
since everything done by LK can be also done directly by U-Boot.

Dropping LK entirely has at least the following advantages:
  - Easier installation/board code (no need for Android boot images)
  - (Slightly) faster boot
  - Boot directly in 64-bit without a round trip to 32-bit for LK

So far this was not possible yet because of unsolved problems:

  1. Signing tool: The firmware expects a "signed" ELF image with extra
     (Qualcomm-specific) ELF headers, usually used for secure boot.
     The DragonBoard 410c does not have secure boot by default but the
     extra ELF headers are still required.

  2. PSCI bug: There seems to be a bug in the PSCI implementation
     (part of the TrustZone/tz firmware) that causes all other CPU cores
     to be started in 32-bit mode if LK is missing in the boot chain.
     This causes Linux to hang early during boot.

There is a solution for both problems now:

  1. qtestsign (https://github.com/msm8916-mainline/qtestsign)
     can be used as a "signing" tool for U-Boot and other firmware.

  2. A workaround for the "PSCI bug" is to execute the TZ syscall when
     entering U-Boot. That way PSCI is made aware of the 64-bit switch
     and starts all other CPU cores in 64-bit mode as well.

Simplify the dragonboard410c board by removing all the extra code that
is only used to build an Android boot image that can be loaded by LK.
This allows dropping the custom linker script, special image magic,
as well as most of the special build/installation instructions.

CONFIG_REMAKE_ELF is used to build a new ELF image that has both U-Boot
and the appended DTB combined. The resulting u-boot.elf can then be
passed to the "signing" tool (e.g. qtestsign).

The PSCI workaround is placed in the "boot0" hook that is enabled
with CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK. The extra check for EL1 allows
compatibility with custom firmware that enters U-Boot in EL2 or EL3,
e.g. qhypstub (https://github.com/msm8916-mainline/qhypstub).

As a first step these changes apply only to DragonBoard410c.
Similar changes could likely also work for the DragonBoard 820c.

Note that removing LK wouldn't be possible that easily without a lot of
work already done three years ago by Ramon Fried. A lot of missing
initialization, pinctrl etc was already added back then even though
it was not strictly needed yet.

Cc: Ramon Fried <rfried.dev@gmail.com>
Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
2021-07-23 18:53:45 -04:00

197 lines
4.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Board init file for Dragonboard 410C
*
* (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
*/
#include <common.h>
#include <cpu_func.h>
#include <dm.h>
#include <env.h>
#include <init.h>
#include <net.h>
#include <usb.h>
#include <asm/cache.h>
#include <asm/global_data.h>
#include <asm/gpio.h>
#include <fdt_support.h>
#include <asm/arch/dram.h>
#include <asm/arch/misc.h>
#include <linux/delay.h>
DECLARE_GLOBAL_DATA_PTR;
int dram_init(void)
{
gd->ram_size = PHYS_SDRAM_1_SIZE;
return 0;
}
int dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
return 0;
}
int board_usb_init(int index, enum usb_init_type init)
{
static struct udevice *pmic_gpio;
static struct gpio_desc hub_reset, usb_sel;
int ret = 0, node;
if (!pmic_gpio) {
ret = uclass_get_device_by_name(UCLASS_GPIO,
"pm8916_gpios@c000",
&pmic_gpio);
if (ret < 0) {
printf("Failed to find pm8916_gpios@c000 node.\n");
return ret;
}
}
/* Try to request gpios needed to start usb host on dragonboard */
if (!dm_gpio_is_valid(&hub_reset)) {
node = fdt_subnode_offset(gd->fdt_blob,
dev_of_offset(pmic_gpio),
"usb_hub_reset_pm");
if (node < 0) {
printf("Failed to find usb_hub_reset_pm dt node.\n");
return node;
}
ret = gpio_request_by_name_nodev(offset_to_ofnode(node),
"gpios", 0, &hub_reset, 0);
if (ret < 0) {
printf("Failed to request usb_hub_reset_pm gpio.\n");
return ret;
}
}
if (!dm_gpio_is_valid(&usb_sel)) {
node = fdt_subnode_offset(gd->fdt_blob,
dev_of_offset(pmic_gpio),
"usb_sw_sel_pm");
if (node < 0) {
printf("Failed to find usb_sw_sel_pm dt node.\n");
return 0;
}
ret = gpio_request_by_name_nodev(offset_to_ofnode(node),
"gpios", 0, &usb_sel, 0);
if (ret < 0) {
printf("Failed to request usb_sw_sel_pm gpio.\n");
return ret;
}
}
if (init == USB_INIT_HOST) {
/* Start USB Hub */
dm_gpio_set_dir_flags(&hub_reset,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
mdelay(100);
/* Switch usb to host connectors */
dm_gpio_set_dir_flags(&usb_sel,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
mdelay(100);
} else { /* Device */
/* Disable hub */
dm_gpio_set_dir_flags(&hub_reset, GPIOD_IS_OUT);
/* Switch back to device connector */
dm_gpio_set_dir_flags(&usb_sel, GPIOD_IS_OUT);
}
return 0;
}
/* Check for vol- button - if pressed - stop autoboot */
int misc_init_r(void)
{
struct udevice *pon;
struct gpio_desc resin;
int node, ret;
ret = uclass_get_device_by_name(UCLASS_GPIO, "pm8916_pon@800", &pon);
if (ret < 0) {
printf("Failed to find PMIC pon node. Check device tree\n");
return 0;
}
node = fdt_subnode_offset(gd->fdt_blob, dev_of_offset(pon),
"key_vol_down");
if (node < 0) {
printf("Failed to find key_vol_down node. Check device tree\n");
return 0;
}
if (gpio_request_by_name_nodev(offset_to_ofnode(node), "gpios", 0,
&resin, 0)) {
printf("Failed to request key_vol_down button.\n");
return 0;
}
if (dm_gpio_get_value(&resin)) {
env_set("bootdelay", "-1");
env_set("bootcmd", "fastboot 0");
printf("key_vol_down pressed - Starting fastboot.\n");
}
return 0;
}
int board_init(void)
{
return 0;
}
int board_late_init(void)
{
char serial[16];
memset(serial, 0, 16);
snprintf(serial, 13, "%x", msm_board_serial());
env_set("serial#", serial);
return 0;
}
/* Fixup of DTB for Linux Kernel
* 1. Fixup installed DRAM.
* 2. Fixup WLAN/BT Mac address:
* First, check if MAC addresses for WLAN/BT exists as environemnt
* variables wlanaddr,btaddr. if not, generate a unique address.
*/
int ft_board_setup(void *blob, struct bd_info *bd)
{
u8 mac[ARP_HLEN];
msm_fixup_memory(blob);
if (!eth_env_get_enetaddr("wlanaddr", mac)) {
msm_generate_mac_addr(mac);
};
do_fixup_by_compat(blob, "qcom,wcnss-wlan",
"local-mac-address", mac, ARP_HLEN, 1);
if (!eth_env_get_enetaddr("btaddr", mac)) {
msm_generate_mac_addr(mac);
/* The BD address is same as WLAN MAC address but with
* least significant bit flipped.
*/
mac[0] ^= 0x01;
};
do_fixup_by_compat(blob, "qcom,wcnss-bt",
"local-bd-address", mac, ARP_HLEN, 1);
return 0;
}
void reset_cpu(void)
{
psci_system_reset();
}