From 64272efdaffc54171ddc9ef15119b2fc85fd58fa Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 2 Sep 2020 19:31:46 +0200 Subject: board: ge: b1x5v2: Add GE B1x5v2 and B1x5Pv2 GE B1x5v2 patient monitor series is similar to the CARESCAPE Monitor series (GE Bx50). It consists of a carrier PCB used in combination with a Congatec QMX6 SoM. This adds U-Boot support using device model everywhere and SPL for memory initialization. Proper configuration is provided as 'ge_b1x5v2_defconfig' and the combined image u-boot-with-spi.imx can be flashed directly to 1024 byte offset to /dev/mtdblock0. Alternatively SPL and u-boot.imx can be loaded separately via USB-OTG using e.g. imx_usb. Signed-off-by: Sebastian Reichel Reviewed-by: Tom Rini --- board/ge/b1x5v2/Kconfig | 14 + board/ge/b1x5v2/Makefile | 6 + board/ge/b1x5v2/b1x5v2.c | 698 +++++++++++++++++++++++++++++++++++++++++++++++ board/ge/b1x5v2/spl.c | 587 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 1305 insertions(+) create mode 100644 board/ge/b1x5v2/Kconfig create mode 100644 board/ge/b1x5v2/Makefile create mode 100644 board/ge/b1x5v2/b1x5v2.c create mode 100644 board/ge/b1x5v2/spl.c (limited to 'board') diff --git a/board/ge/b1x5v2/Kconfig b/board/ge/b1x5v2/Kconfig new file mode 100644 index 0000000000..80a5bcae7d --- /dev/null +++ b/board/ge/b1x5v2/Kconfig @@ -0,0 +1,14 @@ +if TARGET_GE_B1X5V2 + +config SYS_BOARD + default "b1x5v2" + +config SYS_VENDOR + default "ge" + +config SYS_CONFIG_NAME + default "ge_b1x5v2" + +source "board/ge/common/Kconfig" + +endif diff --git a/board/ge/b1x5v2/Makefile b/board/ge/b1x5v2/Makefile new file mode 100644 index 0000000000..8a27af52e1 --- /dev/null +++ b/board/ge/b1x5v2/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2018-2020 Collabora +# Copyright 2018-2020 GE + +obj-y := b1x5v2.o spl.o diff --git a/board/ge/b1x5v2/b1x5v2.c b/board/ge/b1x5v2/b1x5v2.c new file mode 100644 index 0000000000..1cb347fd9e --- /dev/null +++ b/board/ge/b1x5v2/b1x5v2.c @@ -0,0 +1,698 @@ +/* + * GE B105v2, B125v2, B155v2 + * + * Copyright 2018-2020 GE Inc. + * Copyright 2018-2020 Collabora Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common/vpd_reader.h" + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SPL_BUILD + +#define B1X5V2_GE_VPD_OFFSET 0x0100000 +#define B1X5V2_GE_VPD_SIZE 1022 + +#define VPD_TYPE_INVALID 0x00 +#define VPD_BLOCK_NETWORK 0x20 +#define VPD_BLOCK_HWID 0x44 +#define VPD_MAC_ADDRESS_LENGTH 6 + +#define VPD_FLAG_VALID_MAC BIT(1) + +#define AR8035_PHY_ID 0x004dd072 +#define AR8035_PHY_DEBUG_ADDR_REG 0x1d +#define AR8035_PHY_DEBUG_DATA_REG 0x1e +#define AR8035_HIB_CTRL_REG 0xb +#define AR8035_HIBERNATE_EN (1 << 15) + +static struct vpd_cache { + bool is_read; + u8 product_id; + unsigned char mac[VPD_MAC_ADDRESS_LENGTH]; + u32 flags; +} vpd; + +enum product_type { + PRODUCT_TYPE_B105V2 = 6, + PRODUCT_TYPE_B105PV2 = 7, + PRODUCT_TYPE_B125V2 = 8, + PRODUCT_TYPE_B125PV2 = 9, + PRODUCT_TYPE_B155V2 = 10, + + PRODUCT_TYPE_INVALID = 0, +}; + +int dram_init(void) { + gd->ram_size = imx_ddr_size(); + return 0; +} + +int power_init_board(void) +{ + /* all required PMIC configuration happens via DT */ + return 0; +} + +static int disable_phy_hibernation(struct phy_device *phydev) +{ + unsigned short val; + + if (phydev->drv->uid == AR8035_PHY_ID) { + /* Disable hibernation, other configuration has been done by PHY driver */ + phy_write(phydev, MDIO_DEVAD_NONE, AR8035_PHY_DEBUG_ADDR_REG, AR8035_HIB_CTRL_REG); + val = phy_read(phydev, MDIO_DEVAD_NONE, AR8035_PHY_DEBUG_DATA_REG); + val &= ~AR8035_HIBERNATE_EN; + phy_write(phydev, MDIO_DEVAD_NONE, AR8035_PHY_DEBUG_DATA_REG, val); + } else { + printf("Unknown PHY: %08x\n", phydev->drv->uid); + } + + return 0; +} + +int board_phy_config(struct phy_device *phydev) +{ + if (phydev->drv->config) + phydev->drv->config(phydev); + + disable_phy_hibernation(phydev); + + return 0; +} + +static int auo_g101evn01_detect(const struct display_info_t *info) +{ + char *dev = env_get("devicetype"); + return !strcmp(dev, "B105v2") || !strcmp(dev, "B105Pv2"); +} + +static int auo_g121ean01_detect(const struct display_info_t *info) +{ + char *dev = env_get("devicetype"); + return !strcmp(dev, "B125v2") || !strcmp(dev, "B125Pv2");; +} + +static int auo_g156xtn01_detect(const struct display_info_t *info) +{ + char *dev = env_get("devicetype"); + return !strcmp(dev, "B155v2"); +} + +static void b1x5v2_backlight_enable(int percent) +{ + struct udevice *panel; + int ret; + + ret = uclass_get_device(UCLASS_PANEL, 0, &panel); + if (ret) { + printf("Could not find panel: %d\n", ret); + return; + } + + panel_set_backlight(panel, percent); + panel_enable_backlight(panel); + +} + +static void lcd_enable(const struct display_info_t *info) +{ + printf("Enable backlight...\n"); + b1x5v2_backlight_enable(100); +} + +struct display_info_t const displays[] = { +{ + .di = 0, + .bus = -1, + .addr = -1, + .pixfmt = IPU_PIX_FMT_RGB24, + .detect = auo_g156xtn01_detect, + .enable = lcd_enable, + .mode = { + .name = "AUO G156XTN01", + .refresh = 60, + .xres = 1368, /* because of i.MX6 limitation, actually 1366 */ + .yres = 768, + .pixclock = 13158, /* 76 MHz in ps */ + .left_margin = 33, + .right_margin = 67, + .upper_margin = 4, + .lower_margin = 4, + .hsync_len = 94, + .vsync_len = 30, + .sync = FB_SYNC_EXT, + .vmode = FB_VMODE_NONINTERLACED + } +}, +{ + .di = 0, + .bus = -1, + .addr = -1, + .pixfmt = IPU_PIX_FMT_RGB24, + .detect = auo_g121ean01_detect, + .enable = lcd_enable, + .mode = { + .name = "AUO G121EAN01.4", + .refresh = 60, + .xres = 1280, + .yres = 800, + .pixclock = 14992, /* 66.7 MHz in ps */ + .left_margin = 8, + .right_margin = 58, + .upper_margin = 6, + .lower_margin = 4, + .hsync_len = 70, + .vsync_len = 10, + .sync = FB_SYNC_EXT, + .vmode = FB_VMODE_NONINTERLACED + } +}, +{ + .di = 0, + .bus = -1, + .addr = -1, + .pixfmt = IPU_PIX_FMT_RGB24, + .detect = auo_g101evn01_detect, + .enable = lcd_enable, + .mode = { + .name = "AUO G101EVN01.3", + .refresh = 60, + .xres = 1280, + .yres = 800, + .pixclock = 14992, /* 66.7 MHz in ps */ + .left_margin = 8, + .right_margin = 58, + .upper_margin = 6, + .lower_margin = 4, + .hsync_len = 70, + .vsync_len = 10, + .sync = FB_SYNC_EXT, + .vmode = FB_VMODE_NONINTERLACED + } +} +}; +size_t display_count = ARRAY_SIZE(displays); + +static void enable_videopll(void) +{ + struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + s32 timeout = 100000; + + setbits_le32(&ccm->analog_pll_video, BM_ANADIG_PLL_VIDEO_POWERDOWN); + + /* PLL_VIDEO 455MHz (24MHz * (37+11/12) / 2) + * | + * PLL5 + * | + * CS2CDR[LDB_DI0_CLK_SEL] + * | + * +----> LDB_DI0_SERIAL_CLK_ROOT + * | + * +--> CSCMR2[LDB_DI0_IPU_DIV] --> LDB_DI0_IPU 455 / 7 = 65 MHz + */ + + clrsetbits_le32(&ccm->analog_pll_video, + BM_ANADIG_PLL_VIDEO_DIV_SELECT | + BM_ANADIG_PLL_VIDEO_POST_DIV_SELECT, + BF_ANADIG_PLL_VIDEO_DIV_SELECT(37) | + BF_ANADIG_PLL_VIDEO_POST_DIV_SELECT(1)); + + writel(BF_ANADIG_PLL_VIDEO_NUM_A(11), &ccm->analog_pll_video_num); + writel(BF_ANADIG_PLL_VIDEO_DENOM_B(12), &ccm->analog_pll_video_denom); + + clrbits_le32(&ccm->analog_pll_video, BM_ANADIG_PLL_VIDEO_POWERDOWN); + + while (timeout--) + if (readl(&ccm->analog_pll_video) & BM_ANADIG_PLL_VIDEO_LOCK) + break; + + if (timeout < 0) + printf("Warning: video pll lock timeout!\n"); + + clrsetbits_le32(&ccm->analog_pll_video, + BM_ANADIG_PLL_VIDEO_BYPASS, + BM_ANADIG_PLL_VIDEO_ENABLE); +} + +static void setup_display(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR; + + enable_videopll(); + + /* When a reset/reboot is performed the display power needs to be turned + * off for atleast 500ms. The boot time is ~300ms, we need to wait for + * an additional 200ms here. Unfortunately we use external PMIC for + * doing the reset, so can not differentiate between POR vs soft reset + */ + mdelay(200); + + /* CCM_CSCMR2 -> ldb_di0_ipu_div [IMX6SDLRM page 839] */ + /* divide IPU clock by 7 */ + setbits_le32(&mxc_ccm->cscmr2, MXC_CCM_CSCMR2_LDB_DI0_IPU_DIV); + + /* CCM_CHSCCDR -> ipu1_di0_clk_sel [IMX6SDLRM page 849] */ + /* Set LDB_DI0 as clock source for IPU_DI0 */ + clrsetbits_le32(&mxc_ccm->chsccdr, + MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_MASK, + (CHSCCDR_CLK_SEL_LDB_DI0 << + MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_OFFSET)); + + /* Turn on IPU LDB DI0 clocks */ + setbits_le32(&mxc_ccm->CCGR3, MXC_CCM_CCGR3_LDB_DI0_MASK); + + enable_ipu_clock(); + + /* IOMUXC_GPR2 [IMX6SDLRM page 2049] */ + /* Set LDB Channel 0 in SPWG 24 Bit mode */ + writel(IOMUXC_GPR2_DI0_VS_POLARITY_ACTIVE_HIGH | + IOMUXC_GPR2_BIT_MAPPING_CH0_SPWG | + IOMUXC_GPR2_DATA_WIDTH_CH0_24BIT | + IOMUXC_GPR2_LVDS_CH0_MODE_ENABLED_DI0, + &iomux->gpr[2]); + + /* IOMUXC_GPR3 [IMX6SDLRM page 2051] */ + /* LVDS0 is connected to IPU DI0 */ + clrsetbits_le32(&iomux->gpr[3], + IOMUXC_GPR3_LVDS0_MUX_CTL_MASK, + (IOMUXC_GPR3_MUX_SRC_IPU1_DI0 << + IOMUXC_GPR3_LVDS0_MUX_CTL_OFFSET)); +} + +/* + * Do not overwrite the console + * Use always serial for U-Boot console + */ +int overwrite_console(void) +{ + return 1; +} + +int board_early_init_f(void) +{ + select_ldb_di_clock_source(MXC_PLL5_CLK); + + return 0; +} + +static int eeti_touch_get_model(struct udevice *dev, char *result) { + u8 query[68] = {0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00, 0x00, 0x00}; + struct i2c_msg qmsg = { + .addr = 0x2a, + .flags = 0, + .len = sizeof(query), + .buf = query, + }; + u8 reply[66] = {0}; + struct i2c_msg rmsg = { + .addr = 0x2a, + .flags = I2C_M_RD, + .len = sizeof(reply), + .buf = reply, + }; + int err; + + err = dm_i2c_xfer(dev, &qmsg, 1); + if (err) + return err; + + /* + * device sends IRQ when its ok to read. To keep the code + * simple we just wait an arbitrary, long enough time period. + */ + mdelay(10); + + err = dm_i2c_xfer(dev, &rmsg, 1); + if (err) + return err; + + if (reply[0] != 0x42 || reply[1] != 0x00 || + reply[2] != 0x03 || reply[4] != 'E') + return -EPROTO; + + memcpy(result, reply+5, 10); + return 0; +} + +static bool b1x5v2_board_is_p_model(void) +{ + struct udevice *bus = NULL; + struct udevice *dev = NULL; + int err; + + err = uclass_get_device_by_name(UCLASS_I2C, "i2c@21a0000", &bus); + if (err || !bus) { + printf("Could not get I2C bus: %d\n", err); + return true; + } + + /* The P models do not have this port expander */ + err = dm_i2c_probe(bus, 0x21, 0, &dev); + if (err || !dev) { + return true; + } + + return false; +} + +static enum product_type b1x5v2_board_type(void) +{ + struct udevice *bus = NULL; + struct udevice *dev = NULL; + char model[11] = {0}; + int err; + int retry; + + err = uclass_get_device_by_name(UCLASS_I2C, "i2c@21a8000", &bus); + if (err) { + printf("Could not get I2C bus: %d\n", err); + return PRODUCT_TYPE_INVALID; + } + + err = dm_i2c_probe(bus, 0x41, 0, &dev); + if (!err && dev) { /* Ilitek Touchscreen */ + if (b1x5v2_board_is_p_model()) { + return PRODUCT_TYPE_B105PV2; + } else { + return PRODUCT_TYPE_B105V2; + } + } + + err = dm_i2c_probe(bus, 0x2a, 0, &dev); + if (err || !dev) { + printf("Could not find touchscreen: %d\n", err); + return PRODUCT_TYPE_INVALID; + } + + for (retry = 0; retry < 3; ++retry) { + err = eeti_touch_get_model(dev, model); + if (!err) + break; + printf("Retry %d read EETI touchscreen model: %d\n", retry + 1, err); + } + if (err) { + printf("Could not read EETI touchscreen model: %d\n", err); + return PRODUCT_TYPE_INVALID; + } + + if (!strcmp(model, "Orion_1320")) { /* EETI EXC80H60 */ + if (b1x5v2_board_is_p_model()) { + return PRODUCT_TYPE_B125PV2; + } else { + return PRODUCT_TYPE_B125V2; + } + } else if (!strcmp(model, "Orion_1343")) { /* EETI EXC80H84 */ + return PRODUCT_TYPE_B155V2; + } + + printf("Unknown EETI touchscreen model: %s\n", model); + return PRODUCT_TYPE_INVALID; +} + +static void set_env_per_board_type(enum product_type type) +{ + switch (type) { + case PRODUCT_TYPE_B105V2: + env_set("resolution", "1280x800"); + env_set("devicetype", "B105v2"); + env_set("fdtfile", "imx6dl-b105v2.dtb"); + break; + case PRODUCT_TYPE_B105PV2: + env_set("resolution", "1280x800"); + env_set("devicetype", "B105Pv2"); + env_set("fdtfile", "imx6dl-b105pv2.dtb"); + break; + case PRODUCT_TYPE_B125V2: + env_set("resolution", "1280x800"); + env_set("devicetype", "B125v2"); + env_set("fdtfile", "imx6dl-b125v2.dtb"); + break; + case PRODUCT_TYPE_B125PV2: + env_set("resolution", "1280x800"); + env_set("devicetype", "B125Pv2"); + env_set("fdtfile", "imx6dl-b125pv2.dtb"); + break; + case PRODUCT_TYPE_B155V2: + env_set("resolution", "1366x768"); + env_set("devicetype", "B155v2"); + env_set("fdtfile", "imx6dl-b155v2.dtb"); + break; + default: + break; + } +} + +static int b1x5v2_board_type_autodetect(void) +{ + enum product_type product = b1x5v2_board_type(); + if (product != PRODUCT_TYPE_INVALID) { + set_env_per_board_type(product); + return 0; + } + return -1; +} + +/* + * Extracts MAC and product information from the VPD. + */ +static int vpd_callback(struct vpd_cache *vpd, u8 id, u8 version, u8 type, + size_t size, u8 const *data) +{ + if (type == VPD_TYPE_INVALID) + return 0; + + if (id == VPD_BLOCK_HWID && version == 1 && size >= 1) { + vpd->product_id = data[0]; + } else if (id == VPD_BLOCK_NETWORK && version == 1) { + if (size >= VPD_MAC_ADDRESS_LENGTH) { + memcpy(vpd->mac, data, VPD_MAC_ADDRESS_LENGTH); + vpd->flags |= VPD_FLAG_VALID_MAC; + } + } + + return 0; +} + +static int read_spi_vpd(struct vpd_cache *cache, + int (*process_block)(struct vpd_cache *, u8 id, u8 version, + u8 type, size_t size, u8 const *data)) +{ + static const int size = B1X5V2_GE_VPD_SIZE; + struct udevice *dev; + int ret; + u8 *data; + + ret = uclass_get_device_by_name(UCLASS_SPI_FLASH, "m25p80@0", &dev); + if (ret) + return ret; + + data = malloc(size); + if (!data) + return -ENOMEM; + + ret = spi_flash_read_dm(dev, B1X5V2_GE_VPD_OFFSET, size, data); + if (ret) { + free(data); + return ret; + } + + ret = vpd_reader(size, data, cache, process_block); + + free(data); + + return ret; +} + +int board_init(void) +{ + if (!read_spi_vpd(&vpd, vpd_callback)) { + vpd.is_read = true; + } + + /* address of boot parameters */ + gd->bd->bi_boot_params = PHYS_SDRAM + 0x100; + setup_display(); + + return 0; +} + +static void init_bootcause(void) +{ + const char *cause; + + /* We care about WDOG only, treating everything else as + * a power-on-reset. + */ + if (get_imx_reset_cause() & 0x0010) + cause = "WDOG"; + else + cause = "POR"; + + env_set("bootcause", cause); +} + +int misc_init_r(void) +{ + init_bootcause(); + + return 0; +} + +#define M41T62_REG_FLAGS 0xf +#define M41T62_FLAGS_OF (1 << 2) +static void check_time(void) +{ + struct udevice *rtc = NULL; + struct rtc_time tm; + u8 val; + int ret; + + ret = uclass_get_device_by_name(UCLASS_RTC, "m41t62@68", &rtc); + if (ret) { + printf("Could not get RTC: %d\n", ret); + env_set("rtc_status", "FAIL"); + return; + } + + ret = dm_i2c_read(rtc, M41T62_REG_FLAGS, &val, sizeof(val)); + if (ret) { + printf("Could not read RTC register: %d\n", ret); + env_set("rtc_status", "FAIL"); + return; + } + + ret = dm_rtc_reset(rtc); + if (ret) { + printf("Could not reset RTC: %d\n", ret); + env_set("rtc_status", "FAIL"); + return; + } + + if (val & M41T62_FLAGS_OF) { + env_set("rtc_status", "STOP"); + return; + } + + ret = dm_rtc_get(rtc, &tm); + if (ret) { + printf("Could not read RTC: %d\n", ret); + env_set("rtc_status", "FAIL"); + return; + } + + if (tm.tm_year > 2037) { + tm.tm_sec = 0; + tm.tm_min = 0; + tm.tm_hour = 0; + tm.tm_mday = 1; + tm.tm_wday = 2; + tm.tm_mon = 1; + tm.tm_year = 2036; + + ret = dm_rtc_set(rtc, &tm); + if (ret) { + printf("Could not update RTC: %d\n", ret); + env_set("rtc_status", "FAIL"); + return; + } + + printf("RTC behind 2037, capped to 2036 for userspace handling\n"); + env_set("rtc_status", "2038"); + return; + } + + env_set("rtc_status", "OK"); +} + +static void process_vpd(struct vpd_cache *vpd) +{ + if (!vpd->is_read) { + printf("VPD wasn't read\n"); + return; + } + + if (vpd->flags & VPD_FLAG_VALID_MAC) { + eth_env_set_enetaddr_by_index("eth", 0, vpd->mac); + env_set("ethact", "eth0"); + } +} + +int board_late_init(void) +{ + process_vpd(&vpd); + + if (vpd.product_id >= PRODUCT_TYPE_B105V2 && + vpd.product_id <= PRODUCT_TYPE_B155V2) { + set_env_per_board_type((enum product_type)vpd.product_id); + } else { + b1x5v2_board_type_autodetect(); + } + + printf("Board: GE %s\n", env_get("devicetype")); + + check_time(); + + return 0; +} + +#ifdef CONFIG_OF_BOARD_SETUP +int ft_board_setup(void *blob, struct bd_info *bd) +{ + char *rtc_status = env_get("rtc_status"); + + fdt_setprop(blob, 0, "ge,boot-ver", version_string, + strlen(version_string) + 1); + fdt_setprop(blob, 0, "ge,rtc-status", rtc_status, + strlen(rtc_status) + 1); + + return 0; +} +#endif + +static int do_b1x5v2_autodetect(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + int err; + + err = b1x5v2_board_type_autodetect(); + if (!err) + printf("Identified %s\n", env_get("devicetype")); + + return 0; +} + +U_BOOT_CMD( + autodetect_devtype, 1, 1, do_b1x5v2_autodetect, + "autodetect b1x5v2 device type", + "" +); + +#endif // CONFIG_SPL_BUILD diff --git a/board/ge/b1x5v2/spl.c b/board/ge/b1x5v2/spl.c new file mode 100644 index 0000000000..2e6f905219 --- /dev/null +++ b/board/ge/b1x5v2/spl.c @@ -0,0 +1,587 @@ +/* + * GE b1x5v2 - QMX6 SPL + * + * Copyright 2013, Adeneo Embedded + * Copyright 2018-2020 GE Inc. + * Copyright 2018-2020 Collabora Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SPL_BUILD) + +#include + +#define IMX6DQ_DRIVE_STRENGTH_40_OHM 0x30 +#define IMX6DQ_DRIVE_STRENGTH_48_OHM 0x28 +#define IMX6DQ_DRIVE_STRENGTH IMX6DQ_DRIVE_STRENGTH_40_OHM + +#define QMX6_DDR_PKE_DISABLED 0x00000000 +#define QMX6_DDR_ODT_60_OHM (2 << 16) +#define QMX6_DDR_TYPE_DDR3 0x000c0000 + +#define QMX6_DRAM_SDCKE_PULLUP_100K 0x00003000 +#define QMX6_DRAM_SDBA2_PULLUP_NONE 0x00000000 + +#define SPI_PAD_CTRL (PAD_CTL_HYS | \ + PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_40ohm | PAD_CTL_SRE_FAST) + +#define UART_PAD_CTRL (PAD_CTL_PUS_100K_UP | \ + PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \ + PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define SPI1_CS0 IMX_GPIO_NR(3, 19) +#define POWEROFF IMX_GPIO_NR(4, 25) + +static iomux_v3_cfg_t const poweroff_pads[] = { + IOMUX_PADS(PAD_DISP0_DAT4__GPIO4_IO25 | MUX_PAD_CTRL(NO_PAD_CTRL)), +}; + +static iomux_v3_cfg_t const uart2_pads[] = { + IOMUX_PADS(PAD_EIM_D26__UART2_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D27__UART2_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), +}; + +static iomux_v3_cfg_t const uart3_pads[] = { + IOMUX_PADS(PAD_EIM_D24__UART3_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D25__UART3_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), +}; + +static iomux_v3_cfg_t const ecspi1_pads[] = { + IOMUX_PADS(PAD_EIM_D16__ECSPI1_SCLK | MUX_PAD_CTRL(SPI_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D17__ECSPI1_MISO | MUX_PAD_CTRL(SPI_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D18__ECSPI1_MOSI | MUX_PAD_CTRL(SPI_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D19__GPIO3_IO19 | MUX_PAD_CTRL(NO_PAD_CTRL)), +}; + +static struct mx6dq_iomux_ddr_regs mx6q_ddr_ioregs = { + .dram_sdclk_0 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdclk_1 = IMX6DQ_DRIVE_STRENGTH, + .dram_cas = IMX6DQ_DRIVE_STRENGTH, + .dram_ras = IMX6DQ_DRIVE_STRENGTH, + .dram_reset = IMX6DQ_DRIVE_STRENGTH, + .dram_sdcke0 = QMX6_DRAM_SDCKE_PULLUP_100K, + .dram_sdcke1 = QMX6_DRAM_SDCKE_PULLUP_100K, + .dram_sdba2 = QMX6_DRAM_SDBA2_PULLUP_NONE, + .dram_sdodt0 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdodt1 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs0 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs1 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs2 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs3 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs4 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs5 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs6 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs7 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm0 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm1 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm2 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm3 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm4 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm5 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm6 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm7 = IMX6DQ_DRIVE_STRENGTH, +}; + +static const struct mx6sdl_iomux_ddr_regs mx6dl_ddr_ioregs = { + .dram_sdclk_0 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdclk_1 = IMX6DQ_DRIVE_STRENGTH, + .dram_cas = IMX6DQ_DRIVE_STRENGTH, + .dram_ras = IMX6DQ_DRIVE_STRENGTH, + .dram_reset = IMX6DQ_DRIVE_STRENGTH, + .dram_sdcke0 = QMX6_DRAM_SDCKE_PULLUP_100K, + .dram_sdcke1 = QMX6_DRAM_SDCKE_PULLUP_100K, + .dram_sdba2 = QMX6_DRAM_SDBA2_PULLUP_NONE, + .dram_sdodt0 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdodt1 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs0 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs1 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs2 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs3 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs4 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs5 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs6 = IMX6DQ_DRIVE_STRENGTH, + .dram_sdqs7 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm0 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm1 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm2 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm3 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm4 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm5 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm6 = IMX6DQ_DRIVE_STRENGTH, + .dram_dqm7 = IMX6DQ_DRIVE_STRENGTH, +}; + +static struct mx6dq_iomux_grp_regs mx6q_grp_ioregs = { + .grp_ddr_type = QMX6_DDR_TYPE_DDR3, + .grp_ddrmode_ctl = QMX6_DDR_ODT_60_OHM, + .grp_ddrpke = QMX6_DDR_PKE_DISABLED, + .grp_addds = IMX6DQ_DRIVE_STRENGTH, + .grp_ctlds = IMX6DQ_DRIVE_STRENGTH, + .grp_ddrmode = QMX6_DDR_ODT_60_OHM, + .grp_b0ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b1ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b2ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b3ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b4ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b5ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b6ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b7ds = IMX6DQ_DRIVE_STRENGTH, +}; + +static const struct mx6sdl_iomux_grp_regs mx6sdl_grp_ioregs = { + .grp_ddr_type = QMX6_DDR_TYPE_DDR3, + .grp_ddrmode_ctl = QMX6_DDR_ODT_60_OHM, + .grp_ddrpke = QMX6_DDR_PKE_DISABLED, + .grp_addds = IMX6DQ_DRIVE_STRENGTH, + .grp_ctlds = IMX6DQ_DRIVE_STRENGTH, + .grp_ddrmode = QMX6_DDR_ODT_60_OHM, + .grp_b0ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b1ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b2ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b3ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b4ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b5ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b6ds = IMX6DQ_DRIVE_STRENGTH, + .grp_b7ds = IMX6DQ_DRIVE_STRENGTH, +}; + +const struct mx6_mmdc_calibration mx6q_mmcd_calib = { + .p0_mpwldectrl0 = 0x0016001A, + .p0_mpwldectrl1 = 0x0023001C, + .p1_mpwldectrl0 = 0x0028003A, + .p1_mpwldectrl1 = 0x001F002C, + .p0_mpdgctrl0 = 0x43440354, + .p0_mpdgctrl1 = 0x033C033C, + .p1_mpdgctrl0 = 0x43300368, + .p1_mpdgctrl1 = 0x03500330, + .p0_mprddlctl = 0x3228242E, + .p1_mprddlctl = 0x2C2C2636, + .p0_mpwrdlctl = 0x36323A38, + .p1_mpwrdlctl = 0x42324440, +}; + +const struct mx6_mmdc_calibration mx6q_2g_mmcd_calib = { + .p0_mpwldectrl0 = 0x00080016, + .p0_mpwldectrl1 = 0x001D0016, + .p1_mpwldectrl0 = 0x0018002C, + .p1_mpwldectrl1 = 0x000D001D, + .p0_mpdgctrl0 = 0x43200334, + .p0_mpdgctrl1 = 0x0320031C, + .p1_mpdgctrl0 = 0x0344034C, + .p1_mpdgctrl1 = 0x03380314, + .p0_mprddlctl = 0x3E36383A, + .p1_mprddlctl = 0x38363240, + .p0_mpwrdlctl = 0x36364238, + .p1_mpwrdlctl = 0x4230423E, +}; + +const struct mx6_mmdc_calibration mx6q_4g_mmcd_calib = { + .p0_mpwldectrl0 = 0x00180018, + .p0_mpwldectrl1 = 0x00220018, + .p1_mpwldectrl0 = 0x00330046, + .p1_mpwldectrl1 = 0x002B003D, + .p0_mpdgctrl0 = 0x4344034C, + .p0_mpdgctrl1 = 0x033C033C, + .p1_mpdgctrl0 = 0x03700374, + .p1_mpdgctrl1 = 0x03600338, + .p0_mprddlctl = 0x443E3E40, + .p1_mprddlctl = 0x423E3E48, + .p0_mpwrdlctl = 0x3C3C4442, + .p1_mpwrdlctl = 0x46384C46, +}; + +static const struct mx6_mmdc_calibration mx6s_mmcd_calib = { + .p0_mpwldectrl0 = 0x00480049, + .p0_mpwldectrl1 = 0x00410044, + .p0_mpdgctrl0 = 0x42480248, + .p0_mpdgctrl1 = 0x023C023C, + .p0_mprddlctl = 0x40424644, + .p0_mpwrdlctl = 0x34323034, +}; + +static const struct mx6_mmdc_calibration mx6s_2g_mmcd_calib = { + .p0_mpwldectrl0 = 0x00450048, + .p0_mpwldectrl1 = 0x003B003F, + .p0_mpdgctrl0 = 0x424C0248, + .p0_mpdgctrl1 = 0x0234023C, + .p0_mprddlctl = 0x40444848, + .p0_mpwrdlctl = 0x38363232, +}; + +static const struct mx6_mmdc_calibration mx6dl_mmcd_calib = { + .p0_mpwldectrl0 = 0x0043004B, + .p0_mpwldectrl1 = 0x003A003E, + .p1_mpwldectrl0 = 0x0047004F, + .p1_mpwldectrl1 = 0x004E0061, + .p0_mpdgctrl0 = 0x42500250, + .p0_mpdgctrl1 = 0x0238023C, + .p1_mpdgctrl0 = 0x42640264, + .p1_mpdgctrl1 = 0x02500258, + .p0_mprddlctl = 0x40424846, + .p1_mprddlctl = 0x46484842, + .p0_mpwrdlctl = 0x38382C30, + .p1_mpwrdlctl = 0x34343430, +}; + +static const struct mx6_mmdc_calibration mx6dl_2g_mmcd_calib = { + .p0_mpwldectrl0 = 0x00450045, + .p0_mpwldectrl1 = 0x00390043, + .p1_mpwldectrl0 = 0x0049004D, + .p1_mpwldectrl1 = 0x004E0061, + .p0_mpdgctrl0 = 0x4240023C, + .p0_mpdgctrl1 = 0x0228022C, + .p1_mpdgctrl0 = 0x02400244, + .p1_mpdgctrl1 = 0x02340238, + .p0_mprddlctl = 0x42464648, + .p1_mprddlctl = 0x4446463C, + .p0_mpwrdlctl = 0x3C38323A, + .p1_mpwrdlctl = 0x34323430, +}; + +static struct mx6_ddr3_cfg mem_ddr_2g = { + .mem_speed = 1600, + .density = 2, + .width = 16, + .banks = 8, + .rowaddr = 14, + .coladdr = 10, + .pagesz = 2, + .trcd = 1310, + .trcmin = 4875, + .trasmin = 3500, +}; + +static struct mx6_ddr3_cfg mem_ddr_4g = { + .mem_speed = 1600, + .density = 4, + .width = 16, + .banks = 8, + .rowaddr = 15, + .coladdr = 10, + .pagesz = 2, + .trcd = 1310, + .trcmin = 4875, + .trasmin = 3500, +}; + +static struct mx6_ddr3_cfg mem_ddr_8g = { + .mem_speed = 1600, + .density = 8, + .width = 16, + .banks = 8, + .rowaddr = 16, + .coladdr = 10, + .pagesz = 2, + .trcd = 1310, + .trcmin = 4875, + .trasmin = 3500, +}; + +static void spl_dram_init(u8 width, u32 memsize) { + struct mx6_ddr_sysinfo sysinfo = { + /* width of data bus: 0=16, 1=32, 2=64 */ + .dsize = width / 32, + /* config for full 4GB range so that get_mem_size() works */ + .cs_density = 32, /* 32Gb per CS */ + + .ncs = 1, + .cs1_mirror = 0, + .rtt_wr = 2, + .rtt_nom = 2, + .walat = 0, + .ralat = 5, + .mif3_mode = 3, + .bi_on = 1, + .sde_to_rst = 0x0d, + .rst_to_cke = 0x20, + }; + + if (is_cpu_type(MXC_CPU_MX6SOLO)) { + sysinfo.walat = 1; + mx6sdl_dram_iocfg(width, &mx6dl_ddr_ioregs, &mx6sdl_grp_ioregs); + + switch(memsize) { + case 512: + mx6_dram_cfg(&sysinfo, &mx6s_2g_mmcd_calib, &mem_ddr_2g); + break; + default: + mx6_dram_cfg(&sysinfo, &mx6s_mmcd_calib, &mem_ddr_4g); + break; + } + } else if (is_cpu_type(MXC_CPU_MX6DL)) { + sysinfo.walat = 1; + mx6sdl_dram_iocfg(width, &mx6dl_ddr_ioregs, &mx6sdl_grp_ioregs); + + switch(memsize) { + case 2048: + mx6_dram_cfg(&sysinfo, &mx6dl_2g_mmcd_calib, &mem_ddr_4g); + break; + default: + mx6_dram_cfg(&sysinfo, &mx6dl_mmcd_calib, &mem_ddr_2g); + break; + } + } else if (is_cpu_type(MXC_CPU_MX6Q)) { + mx6dq_dram_iocfg(width, &mx6q_ddr_ioregs, &mx6q_grp_ioregs); + + switch(memsize) { + case 4096: + sysinfo.cs_density = 16; + sysinfo.ncs = 2; + mx6_dram_cfg(&sysinfo, &mx6q_4g_mmcd_calib, &mem_ddr_8g); + break; + case 2048: + mx6_dram_cfg(&sysinfo, &mx6q_2g_mmcd_calib, &mem_ddr_4g); + break; + default: + mx6_dram_cfg(&sysinfo, &mx6q_mmcd_calib, &mem_ddr_2g); + break; + } + } +} + +/* Define a minimal structure so that the part number can be read via SPL */ +#define CFG_MFG_ADDR_OFFSET (spi->size - SZ_16K) +struct mfgdata { + unsigned char tsize; + /* size of checksummed part in bytes */ + unsigned char ckcnt; + /* checksum corrected byte */ + unsigned char cksum; + /* decimal serial number, packed BCD */ + unsigned char serial[6]; + /* part number, right justified, ASCII */ + unsigned char pn[16]; +}; + +static void conv_ascii(unsigned char *dst, unsigned char *src, int len) +{ + int remain = len; + unsigned char *sptr = src; + unsigned char *dptr = dst; + + while (remain) { + if (*sptr) { + *dptr = *sptr; + dptr++; + } + sptr++; + remain--; + } + *dptr = 0x0; +} + +/* + * Returns the total size of the memory [in MB] the board is equipped with + * + * This is determined via the partnumber which is stored in the + * congatec manufacturing area + */ +static int get_boardmem_size(struct spi_flash *spi) +{ + int ret; + int i; + int arraysize; + char buf[sizeof(struct mfgdata)]; + struct mfgdata *data = (struct mfgdata *)buf; + unsigned char outbuf[32]; + char partnumbers_2g[4][7] = { "016104", "016105", "016304", "016305" }; + char partnumbers_4g[2][7] = { "016308", "016318" }; + char partnumbers_512m[2][7] = { "016203", "616300" }; + + ret = spi_flash_read(spi, CFG_MFG_ADDR_OFFSET, sizeof(struct mfgdata), + buf); + if (ret) + return 1024; /* default to 1GByte in case of error */ + + conv_ascii(outbuf, data->pn, sizeof(data->pn)); + + printf("Detected Congatec QMX6 SOM: %s\n", outbuf); + + /* congatec PN 016104, 016105, 016304, 016305 have 2GiB of RAM */ + arraysize = sizeof(partnumbers_2g) / sizeof(partnumbers_2g[0]); + for (i=0; i < arraysize; i++) { + if (!memcmp(outbuf,partnumbers_2g[i],6)) + return 2048; + } + + /* congatec PN 016308, 016318 have 4GiB of RAM */ + arraysize = sizeof(partnumbers_4g) / sizeof(partnumbers_4g[0]); + for (i=0; i < arraysize; i++) { + if (!memcmp(outbuf,partnumbers_4g[i],6)) + return 4096; + } + + /* congatec PN 016203, 616300 has 512MiB of RAM */ + arraysize = sizeof(partnumbers_512m) / sizeof(partnumbers_512m[0]); + for (i=0; i < arraysize; i++) { + if (!memcmp(outbuf,partnumbers_512m[i],6)) + return 512; + } + + /* default to 1GByte */ + return 1024; +} + +void reset_cpu(ulong addr) +{ +} + +int board_spi_cs_gpio(unsigned bus, unsigned cs) +{ + if (bus == 0 && cs == 0) + return (SPI1_CS0); + else + return -1; +} + +static void memory_init(void) { + struct spi_flash *spi; + u8 width; + u32 size; + + SETUP_IOMUX_PADS(ecspi1_pads); + gpio_direction_output(SPI1_CS0, 0); + + spi = spi_flash_probe(CONFIG_ENV_SPI_BUS, + CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!spi) + panic("Cannot identify board type: SPI-NOR flash module not detected\n"); + + /* lock manufacturer area */ + spi_flash_protect(spi, CFG_MFG_ADDR_OFFSET, SZ_16K, true); + + width = is_cpu_type(MXC_CPU_MX6SOLO) ? 32 : 64; + size = get_boardmem_size(spi); + printf("Detected Memory Size: %u\n", size); + + spl_dram_init(width, size); +} + +static void ccgr_init(void) +{ + struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + static const uint32_t ccgr0 = + MXC_CCM_CCGR0_AIPS_TZ1_MASK | + MXC_CCM_CCGR0_AIPS_TZ2_MASK | + MXC_CCM_CCGR0_APBHDMA_MASK | + MXC_CCM_CCGR0_CAAM_SECURE_MEM_MASK | + MXC_CCM_CCGR0_CAAM_WRAPPER_ACLK_MASK | + MXC_CCM_CCGR0_CAAM_WRAPPER_IPG_MASK | + MXC_CCM_CCGR0_CHEETAH_DBG_CLK_MASK; + + static const uint32_t ccgr1 = + MXC_CCM_CCGR1_ECSPI1S_MASK | + MXC_CCM_CCGR1_ENET_MASK | + MXC_CCM_CCGR1_EPIT1S_MASK | + MXC_CCM_CCGR1_EPIT2S_MASK | + MXC_CCM_CCGR1_GPT_BUS_MASK; + + static const uint32_t ccgr2 = + MXC_CCM_CCGR2_IOMUX_IPT_CLK_IO_MASK | + MXC_CCM_CCGR2_IPMUX1_MASK | + MXC_CCM_CCGR2_IPMUX2_MASK | + MXC_CCM_CCGR2_IPMUX3_MASK | + MXC_CCM_CCGR2_IPSYNC_IP2APB_TZASC1_IPGS_MASK | + MXC_CCM_CCGR2_IPSYNC_IP2APB_TZASC2_IPG_MASK | + MXC_CCM_CCGR2_IPSYNC_VDOA_IPG_MASTER_CLK_MASK; + + static const uint32_t ccgr3 = + MXC_CCM_CCGR3_MMDC_CORE_ACLK_FAST_CORE_P0_MASK | + MXC_CCM_CCGR3_MMDC_CORE_ACLK_FAST_CORE_P1_MASK | + MXC_CCM_CCGR3_MMDC_CORE_IPG_CLK_P0_MASK | + MXC_CCM_CCGR3_MMDC_CORE_IPG_CLK_P1_MASK | + MXC_CCM_CCGR3_OCRAM_MASK; + + static const uint32_t ccgr4 = + MXC_CCM_CCGR4_PL301_MX6QFAST1_S133_MASK | + MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_MASK | + MXC_CCM_CCGR4_PL301_MX6QPER2_MAINCLK_ENABLE_MASK | + MXC_CCM_CCGR4_PWM1_MASK | + MXC_CCM_CCGR4_PWM2_MASK | + MXC_CCM_CCGR4_PWM3_MASK | + MXC_CCM_CCGR4_PWM4_MASK; + + static const uint32_t ccgr5 = + MXC_CCM_CCGR5_ROM_MASK | + MXC_CCM_CCGR5_SDMA_MASK | + MXC_CCM_CCGR5_UART_MASK | + MXC_CCM_CCGR5_UART_SERIAL_MASK; + + static const uint32_t ccgr6 = + MXC_CCM_CCGR6_USBOH3_MASK | + MXC_CCM_CCGR6_USDHC1_MASK | + MXC_CCM_CCGR6_USDHC2_MASK | + MXC_CCM_CCGR6_SIM1_CLK_MASK | + MXC_CCM_CCGR6_SIM2_CLK_MASK; + + writel(ccgr0, &ccm->CCGR0); + writel(ccgr1, &ccm->CCGR1); + writel(ccgr2, &ccm->CCGR2); + writel(ccgr3, &ccm->CCGR3); + writel(ccgr4, &ccm->CCGR4); + writel(ccgr5, &ccm->CCGR5); + writel(ccgr6, &ccm->CCGR6); +} + +void board_init_f(ulong dummy) +{ + /* setup clock gating */ + ccgr_init(); + + /* setup AIPS and disable watchdog */ + arch_cpu_init(); + + /* setup AXI */ + gpr_init(); + + /* + * setup poweroff GPIO. This controls system power regulator. Once + * the power button is released this must be enabled to keep system + * running. Not enabling it (or disabling it later) will turn off + * the main system regulator and instantly poweroff the system. We + * do this very early, to reduce the time users have to press the + * power button. + */ + SETUP_IOMUX_PADS(poweroff_pads); + gpio_direction_output(POWEROFF, 1); + + /* setup GP timer */ + timer_init(); + + /* iomux */ + if (CONFIG_MXC_UART_BASE == UART2_BASE) + SETUP_IOMUX_PADS(uart2_pads); + else if (CONFIG_MXC_UART_BASE == UART3_BASE) + SETUP_IOMUX_PADS(uart3_pads); + + /* UART clocks enabled and gd valid - init serial console */ + preloader_console_init(); + + /* Needed for malloc() [used by SPI] to work in SPL prior to board_init_r() */ + spl_init(); + + /* DDR initialization */ + memory_init(); +} + +void spl_board_prepare_for_boot(void) +{ + printf("Load normal U-Boot...\n"); +} +#endif -- cgit v1.2.3