blob: c4b6c37e4959be56f8c678c940f33a42c66f71f0 [file] [edit]
// SPDX-License-Identifier: GPL-2.0+
/*
* BTT[3C] iMX28 board
*
* Copyright (C) 2025 DENX Software Engineering
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
*/
#include <fdt_support.h>
#include <init.h>
#include <log.h>
#include <net.h>
#include <env.h>
#include <asm/global_data.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/iomux-mx28.h>
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
#include <linux/delay.h>
#include <netdev.h>
#include <errno.h>
#include <serial.h>
#include <u-boot/crc.h>
#include "boot_img_scr.h"
#include <spi.h>
#include <spi_flash.h>
#ifdef CONFIG_XPL_BUILD
#include <spl.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
/*
* Functions
*/
static void init_clocks(void)
{
/* IO0 clock at 480MHz */
mxs_set_ioclk(MXC_IOCLK0, 480000);
/* IO1 clock at 480MHz */
mxs_set_ioclk(MXC_IOCLK1, 480000);
/* SSP0 clock at 96MHz */
mxs_set_sspclk(MXC_SSPCLK0, 96000, 0);
/* SSP2 clock at 160MHz */
mxs_set_sspclk(MXC_SSPCLK2, 160000, 0);
/* SSP3 clock at 96MHz */
mxs_set_sspclk(MXC_SSPCLK3, 96000, 0);
}
#if defined(CONFIG_XPL_BUILD) && defined(CONFIG_SPL_FRAMEWORK)
void board_init_f(ulong arg)
{
init_clocks();
spl_early_init();
preloader_console_init();
}
static struct boot_img_src img_src[2];
static int spi_load_boot_info(void)
{
struct spi_flash *flash;
int err;
flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
CONFIG_SF_DEFAULT_CS,
CONFIG_SF_DEFAULT_SPEED,
CONFIG_SF_DEFAULT_MODE);
if (!flash) {
printf("%s: SPI probe err\n", __func__);
return -ENODEV;
}
/*
* Load both boot info structs from SPI flash
*/
err = spi_flash_read(flash, SPI_FLASH_BOOT_SRC_OFFS,
sizeof(img_src[0]),
(void *)&img_src[0]);
if (err) {
debug("%s: First boot info NOR sector read error %d\n",
__func__, err);
return err;
}
err = spi_flash_read(flash,
SPI_FLASH_BOOT_SRC_OFFS + SPI_FLASH_SECTOR_SIZE,
sizeof(img_src[0]),
(void *)&img_src[1]);
if (err) {
debug("%s: First boot info NOR sector read error %d\n",
__func__, err);
return err;
}
debug("%s: BI0 0x%x 0x%x 0x%x\n", __func__,
img_src[0].magic, img_src[0].flags, img_src[0].crc8);
debug("%s: BI1 0x%x 0x%x 0x%x\n", __func__,
img_src[1].magic, img_src[1].flags, img_src[1].crc8);
return 0;
}
#define BTT_MONITORING_DEVICE_TIMEOUT 100
static int rescue_val;
static void lookup_and_request(struct gpio_desc *desc, const char *name, const char *label)
{
int ret;
ret = dm_gpio_lookup_name(name, desc);
if (ret)
printf("Cannot get %s\n", name);
ret = dm_gpio_request(desc, label);
if (ret)
printf("Cannot request %s\n", name);
}
void spl_board_init(void)
{
struct gpio_desc phy_rst, boot, rescue, wifi_en, bt_en;
int i;
/*
* On the new HW version of BTTC/3 (with LAN8720ai PHY) the !RST pin
* (15) is pulled LOW by external resistor. As a result it needs to be
* set HIGH as soon as possible to allow correct generation of RESET
* pulse.
*
* In the old BTTC (with TLK105 PHY) the RC circuit was used instead
* to set the RESET pin to HIGH after 100us, so there was no need to
* set it explicitly.
*/
lookup_and_request(&phy_rst, "GPIO4_12", "phy-rst");
dm_gpio_set_dir_flags(&phy_rst, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
/*
* Explicitly set GPIO, which controls WL_EN (wifi) to LOW. On the BTT3
* it is directly connected to Jody module without any externa pull up
* down register.
*/
lookup_and_request(&wifi_en, "GPIO0_27", "wifi-en");
dm_gpio_set_dir_flags(&wifi_en, GPIOD_IS_OUT | GPIOD_ACTIVE_LOW |
GPIOD_IS_OUT_ACTIVE);
/*
* Explicitly set GPIO, which controls BT_EN (Bluetooth) to LOW. On the
* BTT3 it is connected to Jody module via RC circuit (after some R*C
* time this pin is set to HIGH). However, the manual recommends setting
* it high from LOW state.
*/
lookup_and_request(&bt_en, "GPIO3_27", "bt-en");
dm_gpio_set_dir_flags(&bt_en, GPIOD_IS_OUT | GPIOD_ACTIVE_LOW |
GPIOD_IS_OUT_ACTIVE);
/* 'boot' and 'rescue' pins */
lookup_and_request(&boot, "GPIO4_9", "boot");
dm_gpio_set_dir_flags(&boot, GPIOD_IS_IN);
lookup_and_request(&rescue, "GPIO4_11", "rescue");
dm_gpio_set_dir_flags(&rescue, GPIOD_IS_IN);
/* Wait for ready signal from system "monitoring" device */
for (i = 0; i < BTT_MONITORING_DEVICE_TIMEOUT; i++) {
if (dm_gpio_get_value(&boot))
break;
mdelay(10);
}
rescue_val = dm_gpio_get_value(&rescue);
}
int spl_mmc_emmc_boot_partition(struct mmc *mmc)
{
int i, src_idx = -1, ret;
ret = spi_load_boot_info();
if (ret) {
printf("%s: Cannot read BTT boot info! [%d]\n", __func__, ret);
/* To avoid bricking board - by default boot from boot0 eMMC */
return 1;
}
for (i = 0; i < 2; i++) {
if (img_src[i].magic == 'B' &&
img_src[i].crc8 == crc8(0, &img_src[i].magic, 2)) {
src_idx = i;
break;
}
}
debug("%s: src idx: %d\n", __func__, src_idx);
if (src_idx < 0)
/*
* Always use eMMC (mmcblkX) boot0 if no
* valid image source description found
*/
return 1;
if (img_src[src_idx].flags & BOOT_SRC_PART1)
return 2;
return 1;
}
void board_boot_order(u32 *spl_boot_list)
{
spl_boot_list[0] = BOOT_DEVICE_MMC1;
spl_boot_list[1] = BOOT_DEVICE_SPI;
spl_boot_list[2] = BOOT_DEVICE_UART;
}
int spl_start_uboot(void)
{
/* break into full u-boot on 'c' */
if (serial_tstc() && serial_getc() == 'c')
return 1;
debug("%s: rescue: %d\n", __func__, rescue_val);
return rescue_val;
}
#else
/*
* Providing proper board name - i.e. 'bttc' vs 'btt3'
* The distinction is made on the size of DRAM memory - i.e.
* bttc has only 128 MiB, whereas btt3 has 256 MiB.
*/
#define STR_BTTC "bttc"
#define STR_BTT3 "btt3"
static const char *get_board_name(void)
{
if (gd->bd->bi_dram[0].size == SZ_128M)
return STR_BTTC;
return STR_BTT3;
}
/*
* Reading the HW ID number for BTT3 device
*
* GPIOs from Port 4:
* E0: GPIO4_10
* E1: GPIO4_5
* E2: GPIO4_14
* E3: GPIO4_15
* are used on BTT3 to store HW revision information.
*
* From rev 1+ the REV GPIOs are properly connected on the PCB, so PULL UPs
* shall be disabled (as they are by default on pins' SPL configuration)
*.
* Rev 0: - read all '1' (first production version without HW rev set)
* Rev 1: - read 0x1 (E0 set)
* Rev 2: - read 0x2 (E1 set)
*
*/
#define BTT3_HW_ID_GPIO_PORT (MXS_PINCTRL_BASE + (0x0900 + ((4) * 0x10)))
#define BTT3_HW_ID_E0 BIT(10)
#define BTT3_HW_ID_E1 BIT(5)
#define BTT3_HW_ID_E2 BIT(14)
#define BTT3_HW_ID_E3 BIT(15)
static u8 get_som_rev(void)
{
struct mxs_register_32 *reg =
(struct mxs_register_32 *)BTT3_HW_ID_GPIO_PORT;
u32 tmp = ~readl(&reg->reg);
u8 id = 0;
if (tmp & BTT3_HW_ID_E0)
id += 1;
if (tmp & BTT3_HW_ID_E1)
id += 2;
if (tmp & BTT3_HW_ID_E2)
id += 4;
if (tmp & BTT3_HW_ID_E3)
id += 8;
/*
* Special case for first production BTT3 version, without HW
* revision support (so it reads 0x0s as pullups are disabled
* and hence 0xF is set for ID)
*/
if (id == 0xF)
id = 0;
return id;
}
int board_early_init_f(void)
{
init_clocks();
return 0;
}
int board_init(void)
{
struct gpio_desc phy_rst;
int ret;
/* Address of boot parameters */
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100;
cpu_eth_init(NULL);
/* PHY INT#/PWDN# */
ret = dm_gpio_lookup_name("GPIO4_13", &phy_rst);
if (ret) {
printf("Cannot get GPIO4_13\n");
return ret;
}
ret = dm_gpio_request(&phy_rst, "phy-rst");
if (ret) {
printf("Cannot request GPIO4_13\n");
return ret;
}
dm_gpio_set_dir_flags(&phy_rst, GPIOD_IS_IN);
udelay(1000);
return 0;
}
#if defined(CONFIG_BOARD_LATE_INIT)
int board_late_init(void)
{
int ret = env_set_ulong("board_som_rev", get_som_rev());
if (ret)
printf("Cannot set BTT's SoM revision env variable!\n");
ret = env_set("arch", get_board_name());
if (ret)
printf("Cannot set SoM 'arch' env variable!\n");
return 0;
}
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
int checkboard(void)
{
printf("Board: LWE BTT SoM HW rev %d\n", get_som_rev());
return 0;
}
#endif
int dram_init(void)
{
return mxs_dram_init();
}
#if defined(CONFIG_OF_BOARD)
int board_fdt_blob_setup(void **fdtp)
{
/*
* The only purpose of this function is the specific BTT's DTB
* setup in u-boot proper. To be more specific - the SPL
* cannot support DTB selection due to size constraints
* (SPL < 50 KiB).
*
* Hence, the DTB selection is done in u-boot, which due to
* board's partition sizes (and backward compatibility) has also
* size constrain (~448 KiB).
*
* To support multiple DTBs appended, the compression has been used
* for them. Unfortunately, the initf_malloc() is called
* after the DTB needs to be selected. To fix this problem for this
* particular setup (i.e. BTT board) the initf_malloc() is called here.
*/
initf_malloc();
return -EEXIST;
}
#endif
#if defined(CONFIG_MULTI_DTB_FIT)
int board_fit_config_name_match(const char *name)
{
u8 rev_id = get_som_rev();
char board[12];
sprintf(board, "imx28-btt3-%d", rev_id);
if (!strncmp(name, board, sizeof(board)))
return 0;
return -EINVAL;
}
#endif
/*
* NOTE:
*
* IMX28 clock "stub" DM driver!
*
* Only used for SPL stage, which is NOT using DM; serial and
* eMMC configuration.
*
* It is required for SPL_OF_PLATDATA proper code generation as,
* this device has hard constrain on the size of the SPL binary
* (u-boot.sb).
*/
static const struct udevice_id imx28_clk_ids[] = {
{ .compatible = "fsl,imx28-clkctrl", },
{ }
};
U_BOOT_DRIVER(fsl_imx28_clkctrl) = {
.name = "fsl_imx28_clkctrl",
.id = UCLASS_CLK,
.of_match = imx28_clk_ids,
};
#endif /* CONFIG_XPL_BUILD */