ARM: imx: pico-imx8mq: Add support for Technexion Pico-iMX8MQ

Add initial support for Technexion Pico-iMX8MQ SoM on PicoPI carrier
board. Currently working is ethernet, serial, eMMC. DT is imported
from Linux 5.4.28 ("462afcd6e7ea") .

Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Fabio Estevam <festevam@gmail.com>
Cc: "NXP i.MX U-Boot Team" <uboot-imx@nxp.com>
Cc: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Stefano Babic <sbabic@denx.de>
This commit is contained in:
Marek Vasut
2020-04-29 20:09:08 +02:00
committed by Stefano Babic
parent b77d0292ca
commit b52fb0b0b5
16 changed files with 8075 additions and 1 deletions

View File

@@ -0,0 +1,12 @@
if TARGET_PICO_IMX8MQ
config SYS_BOARD
default "pico-imx8mq"
config SYS_VENDOR
default "technexion"
config SYS_CONFIG_NAME
default "pico-imx8mq"
endif

View File

@@ -0,0 +1,6 @@
PICOPI IMX8MQ BOARD
M: Marek Vasut <marek.vasut@gmail.com>
S: Maintained
F: board/technexion/pico-imx8mq/
F: include/configs/pico-imx8mq.h
F: configs/pico-imx8mq_defconfig

View File

@@ -0,0 +1,12 @@
#
# Copyright 2017 NXP
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += pico-imx8mq.o
ifdef CONFIG_SPL_BUILD
obj-y += spl.o
obj-$(CONFIG_IMX8M_LPDDR4) += lpddr4_timing_1gb.o lpddr4_timing_2gb.o lpddr4_timing_3gb.o lpddr4_timing_4gb.o
endif

View File

@@ -0,0 +1,52 @@
U-Boot for the Technexion Pico i.MX8MQ
Quick Start
===========
- Build the TFA binary
- Get DDR and HDMI firmware
- Build U-Boot
- Boot
Get and Build the TFA blob
==========================
Note: srctree is U-Boot source directory
Get ATF from: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git
branch: master
$ CROSS_COMPILE=aarch64-linux-gnu- make PLAT=imx8mq bl31
$ cp build/imx8mq/release/bl31.bin $(builddir)
Get the DDR and HDMI firmware
=============================
$ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-7.9.bin
$ chmod +x firmware-imx-7.9.bin
$ ./firmware-imx-7.9.bin
# Or use this to avoid running random scripts from the internet,
# but note that you must agree to the license the script displays:
# $ dd if=firmware-imx-7.9.bin of=firmware-imx-7.9.tar.bz2 bs=38868 skip=1
# $ tar -xf firmware-imx-7.9.tar.bz2
$ cp firmware-imx-7.9/firmware/hdmi/cadence/signed_hdmi_imx8m.bin $(builddir)
$ cp firmware-imx-7.9/firmware/ddr/synopsys/lpddr4*.bin $(builddir)
Build U-Boot
============
$ export CROSS_COMPILE=aarch64-poky-linux-
$ make pico-imx8mq_defconfig
$ make flash.bin
Burn the flash.bin to MicroSD card offset 33KB
$ dd if=flash.bin of=/dev/mmcblkX bs=1024 seek=33
Or into eMMC from a running system
$ dhcp flash.bin && mmc write $loadaddr 0x42 0x800
Boot
====
"o" denotes a pin
"[]" denotes two pins bridged by a jumper
eMMC boot:
J1 o[] []o J2
[]o o[]
USB upload via USB-C connector:
J1 ooo ooo J2
o[] []o

View File

@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2020 Marek Vasut <marek.vasut@gmail.com>
*/
#ifndef __LPDDR4_TIMING_H__
#define __LPDDR4_TIMING_H__
extern struct dram_timing_info dram_timing_1gb;
extern struct dram_timing_info dram_timing_2gb;
extern struct dram_timing_info dram_timing_3gb;
extern struct dram_timing_info dram_timing_4gb;
#endif /* __LPDDR4_TIMING_H__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
*/
#include <common.h>
#include <env.h>
#include <init.h>
#include <malloc.h>
#include <errno.h>
#include <asm/io.h>
#include <miiphy.h>
#include <netdev.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm-generic/gpio.h>
#include <fsl_esdhc_imx.h>
#include <mmc.h>
#include <asm/arch/imx8mq_pins.h>
#include <asm/arch/sys_proto.h>
#include <asm/mach-imx/gpio.h>
#include <asm/mach-imx/mxc_i2c.h>
#include <asm/arch/clock.h>
#include <spl.h>
#include <power/pmic.h>
DECLARE_GLOBAL_DATA_PTR;
#define UART_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_FSEL1)
#define WDOG_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_HYS | PAD_CTL_PUE)
static iomux_v3_cfg_t const wdog_pads[] = {
IMX8MQ_PAD_GPIO1_IO02__WDOG1_WDOG_B | MUX_PAD_CTRL(WDOG_PAD_CTRL),
};
static iomux_v3_cfg_t const uart_pads[] = {
IMX8MQ_PAD_UART1_RXD__UART1_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
IMX8MQ_PAD_UART1_TXD__UART1_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
};
int board_early_init_f(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR;
imx_iomux_v3_setup_multiple_pads(wdog_pads, ARRAY_SIZE(wdog_pads));
set_wdog_reset(wdog);
imx_iomux_v3_setup_multiple_pads(uart_pads, ARRAY_SIZE(uart_pads));
return 0;
}
int dram_init(void)
{
int ddr_size = readl(M4_BOOTROM_BASE_ADDR);
if (ddr_size == 0x4)
gd->ram_size = 0x100000000;
else if (ddr_size == 0x3)
gd->ram_size = 0xc0000000;
else if (ddr_size == 0x2)
gd->ram_size = 0x80000000;
else if (ddr_size == 0x1)
gd->ram_size = 0x40000000;
else
printf("Unknown DDR type!!!\n");
/* rom_pointer[1] contains the size of TEE occupies */
if (rom_pointer[1])
gd->ram_size -= rom_pointer[1];
return 0;
}
#ifdef CONFIG_FEC_MXC
#define FEC_RST_PAD IMX_GPIO_NR(1, 9)
#define FEC_PWR_PAD IMX_GPIO_NR(1, 0)
static iomux_v3_cfg_t const fec1_pads[] = {
/* Reset */
IMX8MQ_PAD_GPIO1_IO09__GPIO1_IO9 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* Power */
IMX8MQ_PAD_GPIO1_IO00__GPIO1_IO0 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
static void setup_iomux_fec(void)
{
imx_iomux_v3_setup_multiple_pads(fec1_pads, ARRAY_SIZE(fec1_pads));
gpio_request(IMX_GPIO_NR(1, 0), "fec1_pwr");
gpio_direction_output(IMX_GPIO_NR(1, 0), 1);
udelay(500);
gpio_request(IMX_GPIO_NR(1, 9), "fec1_rst");
gpio_direction_output(IMX_GPIO_NR(1, 9), 0);
udelay(500);
gpio_direction_output(IMX_GPIO_NR(1, 9), 1);
}
static int setup_fec(void)
{
struct iomuxc_gpr_base_regs *gpr =
(struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR;
setup_iomux_fec();
/* Use 125M anatop REF_CLK1 for ENET1, not from external */
clrsetbits_le32(&gpr->gpr[1], BIT(13) | BIT(17), 0);
return set_clk_enet(ENET_125MHZ);
}
int board_phy_config(struct phy_device *phydev)
{
/* enable rgmii rxc skew and phy mode select to RGMII copper */
phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x1f);
phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x8);
phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x05);
phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100);
if (phydev->drv->config)
phydev->drv->config(phydev);
return 0;
}
#endif
int board_init(void)
{
#ifdef CONFIG_FEC_MXC
setup_fec();
#endif
return 0;
}
int board_mmc_get_env_dev(int devno)
{
return devno;
}
int board_late_init(void)
{
#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
env_set("board_rev", "iMX8MQ");
#endif
return 0;
}

View File

@@ -0,0 +1,223 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
*/
#include <common.h>
#include <hang.h>
#include <asm/arch/clock.h>
#include <asm/arch/ddr.h>
#include <asm/arch/imx8mq_pins.h>
#include <asm/arch/sys_proto.h>
#include <asm/io.h>
#include <asm/mach-imx/gpio.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm/mach-imx/mxc_i2c.h>
#include <errno.h>
#include <fsl_esdhc_imx.h>
#include <mmc.h>
#include <spl.h>
#include "lpddr4_timing.h"
DECLARE_GLOBAL_DATA_PTR;
#define DDR_DET_1 IMX_GPIO_NR(3, 11)
#define DDR_DET_2 IMX_GPIO_NR(3, 12)
#define DDR_DET_3 IMX_GPIO_NR(3, 13)
static iomux_v3_cfg_t const verdet_pads[] = {
IMX8MQ_PAD_NAND_DATA01__GPIO3_IO7 | MUX_PAD_CTRL(NO_PAD_CTRL),
IMX8MQ_PAD_NAND_DATA02__GPIO3_IO8 | MUX_PAD_CTRL(NO_PAD_CTRL),
IMX8MQ_PAD_NAND_DATA03__GPIO3_IO9 | MUX_PAD_CTRL(NO_PAD_CTRL),
IMX8MQ_PAD_NAND_DATA04__GPIO3_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
IMX8MQ_PAD_NAND_DATA05__GPIO3_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),
IMX8MQ_PAD_NAND_DATA06__GPIO3_IO12 | MUX_PAD_CTRL(NO_PAD_CTRL),
IMX8MQ_PAD_NAND_DATA07__GPIO3_IO13 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
/*
* DDR_DET_1 DDR_DET_2 DDR_DET_3
* 0 0 1 4G LPDDR4
* 1 1 1 3G LPDDR4
* 1 1 0 2G LPDDR4
* 1 0 1 1G LPDDR4
*/
static void spl_dram_init(void)
{
struct dram_timing_info *dram_timing;
u8 ddr = 0, size;
imx_iomux_v3_setup_multiple_pads(verdet_pads, ARRAY_SIZE(verdet_pads));
gpio_request(DDR_DET_1, "ddr_det_1");
gpio_direction_input(DDR_DET_1);
gpio_request(DDR_DET_2, "ddr_det_2");
gpio_direction_input(DDR_DET_2);
gpio_request(DDR_DET_3, "ddr_det_3");
gpio_direction_input(DDR_DET_3);
ddr |= !!gpio_get_value(DDR_DET_3) << 0;
ddr |= !!gpio_get_value(DDR_DET_2) << 1;
ddr |= !!gpio_get_value(DDR_DET_1) << 2;
switch (ddr) {
case 0x1:
size = 4;
dram_timing = &dram_timing_4gb;
break;
case 0x7:
size = 3;
dram_timing = &dram_timing_3gb;
break;
case 0x6:
size = 2;
dram_timing = &dram_timing_2gb;
break;
case 0x5:
size = 1;
dram_timing = &dram_timing_1gb;
break;
default:
puts("Unknown DDR type!!!\n");
return;
}
printf("%s: LPDDR4 %d GiB\n", __func__, size);
ddr_init(dram_timing);
writel(size, M4_BOOTROM_BASE_ADDR);
}
#define USDHC2_CD_GPIO IMX_GPIO_NR(2, 12)
#define USDHC1_PWR_GPIO IMX_GPIO_NR(2, 10)
#define USDHC2_PWR_GPIO IMX_GPIO_NR(2, 19)
int board_mmc_getcd(struct mmc *mmc)
{
struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
int ret = 0;
switch (cfg->esdhc_base) {
case USDHC1_BASE_ADDR:
ret = 1;
break;
case USDHC2_BASE_ADDR:
ret = !gpio_get_value(USDHC2_CD_GPIO);
return ret;
}
return 1;
}
#define USDHC_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_HYS | PAD_CTL_PUE | \
PAD_CTL_FSEL2)
#define USDHC_GPIO_PAD_CTRL (PAD_CTL_PUE | PAD_CTL_DSE1)
static iomux_v3_cfg_t const usdhc1_pads[] = {
IMX8MQ_PAD_SD1_CLK__USDHC1_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_CMD__USDHC1_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA0__USDHC1_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA1__USDHC1_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA2__USDHC1_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA3__USDHC1_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA4__USDHC1_DATA4 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA5__USDHC1_DATA5 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA6__USDHC1_DATA6 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_DATA7__USDHC1_DATA7 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD1_RESET_B__GPIO2_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
static iomux_v3_cfg_t const usdhc2_pads[] = {
IMX8MQ_PAD_SD2_CLK__USDHC2_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD2_CMD__USDHC2_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD2_DATA0__USDHC2_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD2_DATA1__USDHC2_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD2_DATA2__USDHC2_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD2_DATA3__USDHC2_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL),
IMX8MQ_PAD_SD2_CD_B__GPIO2_IO12 | MUX_PAD_CTRL(USDHC_GPIO_PAD_CTRL),
IMX8MQ_PAD_SD2_RESET_B__GPIO2_IO19 | MUX_PAD_CTRL(USDHC_GPIO_PAD_CTRL),
};
static struct fsl_esdhc_cfg usdhc_cfg[2] = {
{USDHC1_BASE_ADDR, 0, 8},
{USDHC2_BASE_ADDR, 0, 4},
};
int board_mmc_init(bd_t *bis)
{
int ret;
/*
* According to the board_mmc_init() the following map is done:
* (U-Boot device node) (Physical Port)
* mmc0 USDHC1
* mmc1 USDHC2
*/
init_clk_usdhc(0);
usdhc_cfg[0].sdhc_clk = mxc_get_clock(USDHC1_CLK_ROOT);
imx_iomux_v3_setup_multiple_pads(usdhc1_pads, ARRAY_SIZE(usdhc1_pads));
gpio_request(USDHC1_PWR_GPIO, "usdhc1_reset");
gpio_direction_output(USDHC1_PWR_GPIO, 0);
udelay(500);
gpio_direction_output(USDHC1_PWR_GPIO, 1);
ret = fsl_esdhc_initialize(bis, &usdhc_cfg[0]);
if (ret)
return ret;
init_clk_usdhc(1);
usdhc_cfg[1].sdhc_clk = mxc_get_clock(USDHC2_CLK_ROOT);
imx_iomux_v3_setup_multiple_pads(usdhc2_pads, ARRAY_SIZE(usdhc2_pads));
gpio_request(USDHC2_PWR_GPIO, "usdhc2_reset");
gpio_direction_output(USDHC2_PWR_GPIO, 0);
udelay(500);
gpio_direction_output(USDHC2_PWR_GPIO, 1);
return fsl_esdhc_initialize(bis, &usdhc_cfg[1]);
}
void spl_board_init(void)
{
puts("Normal Boot\n");
}
#ifdef CONFIG_SPL_LOAD_FIT
int board_fit_config_name_match(const char *name)
{
/* Just empty function now - can't decide what to choose */
debug("%s: %s\n", __func__, name);
return 0;
}
#endif
void board_init_f(ulong dummy)
{
int ret;
/* Clear global data */
memset((void *)gd, 0, sizeof(gd_t));
arch_cpu_init();
init_uart_clk(0);
board_early_init_f();
timer_init();
preloader_console_init();
/* Clear the BSS. */
memset(__bss_start, 0, __bss_end - __bss_start);
ret = spl_init();
if (ret) {
debug("spl_init() failed: %d\n", ret);
hang();
}
enable_tzc380();
/* DDR initialization */
spl_dram_init();
board_init_r(NULL, 0);
}