summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2017-09-01 14:40:59 (GMT)
committerTom Rini <trini@konsulko.com>2017-09-01 14:40:59 (GMT)
commit6aee2ab68c362ace5a59f89a63abed82e0bf19e5 (patch)
tree0b76045a178e9c0ed61e141bd8dfb3e34e4e86a9
parent4109d702934c361b2ae2a5ad34f4dfee708cc214 (diff)
parent63af4b0ad0666e0797306593813b6272e1995bde (diff)
downloadu-boot-6aee2ab68c362ace5a59f89a63abed82e0bf19e5.tar.xz
Merge git://git.denx.de/u-boot-imx
Signed-off-by: Tom Rini <trini@konsulko.com> Conflicts: configs/imx6qdl_icore_mmc_defconfig configs/imx6qdl_icore_rqs_defconfig
-rw-r--r--arch/arm/include/asm/arch-mx35/mmc_host_def.h18
-rw-r--r--arch/arm/include/asm/arch-mx5/iomux-mx51.h7
-rw-r--r--arch/arm/include/asm/arch-mxs/iomux-mx23.h7
-rw-r--r--arch/arm/include/asm/arch-mxs/iomux-mx28.h7
-rw-r--r--arch/arm/include/asm/mach-imx/sys_proto.h9
-rw-r--r--arch/arm/mach-imx/mx6/soc.c19
-rw-r--r--arch/arm/mach-imx/mx7/psci-mx7.c7
-rw-r--r--arch/arm/mach-imx/mx7/psci.S7
-rw-r--r--arch/arm/mach-imx/spl.c20
-rw-r--r--arch/arm/mach-imx/timer.c3
-rw-r--r--board/bachmann/ot1200/ot1200.c11
-rw-r--r--board/barco/platinum/platinum.h11
-rw-r--r--board/congatec/cgtqmx6eval/cgtqmx6eval.c11
-rw-r--r--board/el/el6x/el6x.c11
-rw-r--r--board/engicam/common/spl.c22
-rw-r--r--board/freescale/mx6sabreauto/mx6sabreauto.c17
-rw-r--r--board/freescale/mx6sabresd/mx6sabresd.c17
-rw-r--r--board/gateworks/gw_ventana/gw_ventana_spl.c11
-rw-r--r--board/kosagi/novena/novena_spl.c11
-rw-r--r--board/liebherr/mccmon6/spl.c11
-rw-r--r--board/phytec/pcm058/pcm058.c12
-rw-r--r--board/phytec/pfla02/Kconfig6
-rw-r--r--board/phytec/pfla02/pfla02.c98
-rw-r--r--board/solidrun/mx6cuboxi/mx6cuboxi.c11
-rw-r--r--board/toradex/apalis_imx6/apalis_imx6.c24
-rw-r--r--board/toradex/colibri_imx6/colibri_imx6.c24
-rw-r--r--board/udoo/udoo_spl.c11
-rw-r--r--board/wandboard/spl.c11
-rw-r--r--cmd/Kconfig7
-rw-r--r--cmd/Makefile1
-rw-r--r--cmd/usb_gadget_sdp.c50
-rw-r--r--common/spl/Kconfig6
-rw-r--r--common/spl/Makefile1
-rw-r--r--common/spl/spl.c2
-rw-r--r--common/spl/spl_sdp.c37
-rw-r--r--configs/apalis_imx6_defconfig4
-rw-r--r--configs/colibri_imx6_defconfig4
-rw-r--r--configs/imx6qdl_icore_mmc_defconfig1
-rw-r--r--doc/README.sdp100
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx.c2
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx7ulp.c10
-rw-r--r--drivers/usb/gadget/Kconfig7
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/f_sdp.c737
-rw-r--r--include/configs/imx6-engicam.h14
-rw-r--r--include/imximage.h (renamed from tools/imximage.h)0
-rw-r--r--include/sdp.h16
47 files changed, 1156 insertions, 279 deletions
diff --git a/arch/arm/include/asm/arch-mx35/mmc_host_def.h b/arch/arm/include/asm/arch-mx35/mmc_host_def.h
index 775b955..0775511 100644
--- a/arch/arm/include/asm/arch-mx35/mmc_host_def.h
+++ b/arch/arm/include/asm/arch-mx35/mmc_host_def.h
@@ -3,23 +3,7 @@
* Texas Instruments, <www.ti.com>
* Syed Mohammed Khasim <khasim@ti.com>
*
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation's version 2 of
- * the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier: GPL-2.0
*/
#ifndef MMC_HOST_DEF_H
diff --git a/arch/arm/include/asm/arch-mx5/iomux-mx51.h b/arch/arm/include/asm/arch-mx5/iomux-mx51.h
index 5c636ac..522512e 100644
--- a/arch/arm/include/asm/arch-mx5/iomux-mx51.h
+++ b/arch/arm/include/asm/arch-mx5/iomux-mx51.h
@@ -3,12 +3,7 @@
* Copyright (C) 2010 Freescale Semiconductor, Inc.
* Copyright (C) 2009-2012 Genesi USA, Inc.
*
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
+ * SPDX-License-Identifier: GPL-2.0+
*/
/*
diff --git a/arch/arm/include/asm/arch-mxs/iomux-mx23.h b/arch/arm/include/asm/arch-mxs/iomux-mx23.h
index 7cb5e71..690929c 100644
--- a/arch/arm/include/asm/arch-mxs/iomux-mx23.h
+++ b/arch/arm/include/asm/arch-mxs/iomux-mx23.h
@@ -2,12 +2,7 @@
* Copyright (C) 2009-2010 Amit Kucheria <amit.kucheria@canonical.com>
* Copyright (C) 2010 Freescale Semiconductor, Inc.
*
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
+ * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __MACH_IOMUX_MX23_H__
diff --git a/arch/arm/include/asm/arch-mxs/iomux-mx28.h b/arch/arm/include/asm/arch-mxs/iomux-mx28.h
index b42820d..39ea74b 100644
--- a/arch/arm/include/asm/arch-mxs/iomux-mx28.h
+++ b/arch/arm/include/asm/arch-mxs/iomux-mx28.h
@@ -2,12 +2,7 @@
* Copyright (C) 2009-2010 Amit Kucheria <amit.kucheria@canonical.com>
* Copyright (C) 2010 Freescale Semiconductor, Inc.
*
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
+ * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __MACH_IOMUX_MX28_H__
diff --git a/arch/arm/include/asm/mach-imx/sys_proto.h b/arch/arm/include/asm/mach-imx/sys_proto.h
index 046df62..970c4ca 100644
--- a/arch/arm/include/asm/mach-imx/sys_proto.h
+++ b/arch/arm/include/asm/mach-imx/sys_proto.h
@@ -69,8 +69,13 @@ enum imx6_bmode_emi {
enum imx6_bmode {
IMX6_BMODE_EMI,
- IMX6_BMODE_UART,
+#if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL)
+ IMX6_BMODE_QSPI,
+ IMX6_BMODE_RESERVED,
+#else
+ IMX6_BMODE_RESERVED,
IMX6_BMODE_SATA,
+#endif
IMX6_BMODE_SERIAL_ROM,
IMX6_BMODE_SD,
IMX6_BMODE_ESD,
@@ -85,6 +90,8 @@ static inline u8 imx6_is_bmode_from_gpr9(void)
}
u32 imx6_src_get_boot_mode(void);
+void gpr_init(void);
+
#endif /* CONFIG_MX6 */
u32 get_nr_cpus(void);
diff --git a/arch/arm/mach-imx/mx6/soc.c b/arch/arm/mach-imx/mx6/soc.c
index 9ede1f5..b724668 100644
--- a/arch/arm/mach-imx/mx6/soc.c
+++ b/arch/arm/mach-imx/mx6/soc.c
@@ -551,6 +551,7 @@ void imx_get_mac_from_fuse(int dev_id, unsigned char *mac)
}
#endif
+#ifndef CONFIG_SPL_BUILD
/*
* cfg_val will be used for
* Boot_cfg4[7:0]:Boot_cfg3[7:0]:Boot_cfg2[7:0]:Boot_cfg1[7:0]
@@ -577,6 +578,7 @@ const struct boot_mode soc_boot_modes[] = {
{"esdhc4", MAKE_CFGVAL(0x40, 0x38, 0x00, 0x00)},
{NULL, 0},
};
+#endif
void reset_misc(void)
{
@@ -681,6 +683,23 @@ void imx_setup_hdmi(void)
}
#endif
+void gpr_init(void)
+{
+ struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
+
+ /* enable AXI cache for VDOA/VPU/IPU */
+ writel(0xF00000CF, &iomux->gpr[4]);
+ if (is_mx6dqp()) {
+ /* set IPU AXI-id1 Qos=0x1 AXI-id0/2/3 Qos=0x7 */
+ writel(0x77177717, &iomux->gpr[6]);
+ writel(0x77177717, &iomux->gpr[7]);
+ } else {
+ /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
+ writel(0x007F007F, &iomux->gpr[6]);
+ writel(0x007F007F, &iomux->gpr[7]);
+ }
+}
+
#ifdef CONFIG_IMX_BOOTAUX
int arch_auxiliary_core_up(u32 core_id, u32 boot_private_data)
{
diff --git a/arch/arm/mach-imx/mx7/psci-mx7.c b/arch/arm/mach-imx/mx7/psci-mx7.c
index 502552d..7f429b0 100644
--- a/arch/arm/mach-imx/mx7/psci-mx7.c
+++ b/arch/arm/mach-imx/mx7/psci-mx7.c
@@ -1,3 +1,10 @@
+/*
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
#include <asm/io.h>
#include <asm/psci.h>
#include <asm/secure.h>
diff --git a/arch/arm/mach-imx/mx7/psci.S b/arch/arm/mach-imx/mx7/psci.S
index 96e88d6..fc5eb34 100644
--- a/arch/arm/mach-imx/mx7/psci.S
+++ b/arch/arm/mach-imx/mx7/psci.S
@@ -1,3 +1,10 @@
+/*
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
#include <config.h>
#include <linux/linkage.h>
diff --git a/arch/arm/mach-imx/spl.c b/arch/arm/mach-imx/spl.c
index 75698c4..258578a 100644
--- a/arch/arm/mach-imx/spl.c
+++ b/arch/arm/mach-imx/spl.c
@@ -15,6 +15,8 @@
#include <spl.h>
#include <asm/mach-imx/hab.h>
+DECLARE_GLOBAL_DATA_PTR;
+
#if defined(CONFIG_MX6)
/* determine boot device from SRC_SBMR1 (BOOT_CFG[4:1]) or SRC_GPR9 register */
u32 spl_boot_device(void)
@@ -27,7 +29,7 @@ u32 spl_boot_device(void)
* BOOT_MODE - see IMX6DQRM Table 8-1
*/
if (((bmode >> 24) & 0x03) == 0x01) /* Serial Downloader */
- return BOOT_DEVICE_UART;
+ return BOOT_DEVICE_BOARD;
/* BOOT_CFG1[7:4] - see IMX6DQRM Table 8-8 */
switch ((reg & IMX6_BMODE_MASK) >> IMX6_BMODE_SHIFT) {
@@ -42,11 +44,13 @@ u32 spl_boot_device(void)
break;
}
/* Reserved: Used to force Serial Downloader */
- case IMX6_BMODE_UART:
- return BOOT_DEVICE_UART;
+ case IMX6_BMODE_RESERVED:
+ return BOOT_DEVICE_BOARD;
/* SATA: See 8.5.4, Table 8-20 */
+#if !defined(CONFIG_MX6UL) && !defined(CONFIG_MX6ULL)
case IMX6_BMODE_SATA:
return BOOT_DEVICE_SATA;
+#endif
/* Serial ROM: See 8.5.5.1, Table 8-22 */
case IMX6_BMODE_SERIAL_ROM:
/* BOOT_CFG4[2:0] */
@@ -126,3 +130,13 @@ __weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
}
#endif
+
+#if defined(CONFIG_MX6) && defined(CONFIG_SPL_OS_BOOT)
+int dram_init_banksize(void)
+{
+ gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
+ gd->bd->bi_dram[0].size = imx_ddr_size();
+
+ return 0;
+}
+#endif
diff --git a/arch/arm/mach-imx/timer.c b/arch/arm/mach-imx/timer.c
index 9b01114..69dbf3c 100644
--- a/arch/arm/mach-imx/timer.c
+++ b/arch/arm/mach-imx/timer.c
@@ -74,8 +74,7 @@ int timer_init(void)
__raw_writel(GPTCR_SWR, &cur_gpt->control);
/* We have no udelay by now */
- for (i = 0; i < 100; i++)
- __raw_writel(0, &cur_gpt->control);
+ __raw_writel(0, &cur_gpt->control);
i = __raw_readl(&cur_gpt->control);
i &= ~GPTCR_CLKSOURCE_MASK;
diff --git a/board/bachmann/ot1200/ot1200.c b/board/bachmann/ot1200/ot1200.c
index df10d6a..9465cea 100644
--- a/board/bachmann/ot1200/ot1200.c
+++ b/board/bachmann/ot1200/ot1200.c
@@ -169,17 +169,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
int board_early_init_f(void)
{
ccgr_init();
diff --git a/board/barco/platinum/platinum.h b/board/barco/platinum/platinum.h
index d3ea8bd..3013ed9 100644
--- a/board/barco/platinum/platinum.h
+++ b/board/barco/platinum/platinum.h
@@ -75,15 +75,4 @@ static inline void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static inline void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
#endif /* _PLATINUM_H_ */
diff --git a/board/congatec/cgtqmx6eval/cgtqmx6eval.c b/board/congatec/cgtqmx6eval/cgtqmx6eval.c
index 8cd0090..2ed66d3 100644
--- a/board/congatec/cgtqmx6eval/cgtqmx6eval.c
+++ b/board/congatec/cgtqmx6eval/cgtqmx6eval.c
@@ -955,17 +955,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
/* Define a minimal structure so that the part number can be read via SPL */
struct mfgdata {
unsigned char tsize;
diff --git a/board/el/el6x/el6x.c b/board/el/el6x/el6x.c
index 6b98b5c..fb128f5 100644
--- a/board/el/el6x/el6x.c
+++ b/board/el/el6x/el6x.c
@@ -570,17 +570,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
/*
* This section requires the differentiation between iMX6 Sabre boards, but
* for now, it will configure only for the mx6q variant.
diff --git a/board/engicam/common/spl.c b/board/engicam/common/spl.c
index a8a7cf3..6964c13 100644
--- a/board/engicam/common/spl.c
+++ b/board/engicam/common/spl.c
@@ -39,6 +39,17 @@ static iomux_v3_cfg_t const uart_pads[] = {
#endif
};
+#ifdef CONFIG_SPL_OS_BOOT
+int spl_start_uboot(void)
+{
+ /* break into full u-boot on 'c' */
+ if (serial_tstc() && serial_getc() == 'c')
+ return 1;
+
+ return 0;
+}
+#endif
+
#ifdef CONFIG_MX6QDL
/*
* Driving strength:
@@ -332,17 +343,6 @@ static void ccgr_init(void)
#endif
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
static void spl_dram_init(void)
{
#ifdef CONFIG_MX6QDL
diff --git a/board/freescale/mx6sabreauto/mx6sabreauto.c b/board/freescale/mx6sabreauto/mx6sabreauto.c
index f8f77f6..15ca029 100644
--- a/board/freescale/mx6sabreauto/mx6sabreauto.c
+++ b/board/freescale/mx6sabreauto/mx6sabreauto.c
@@ -798,23 +798,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- if (is_mx6dqp()) {
- /* set IPU AXI-id1 Qos=0x1 AXI-id0/2/3 Qos=0x7 */
- writel(0x77177717, &iomux->gpr[6]);
- writel(0x77177717, &iomux->gpr[7]);
- } else {
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
- }
-}
-
static int mx6q_dcd_table[] = {
0x020e0798, 0x000C0000,
0x020e0758, 0x00000000,
diff --git a/board/freescale/mx6sabresd/mx6sabresd.c b/board/freescale/mx6sabresd/mx6sabresd.c
index 9a562b3..5b50bc8 100644
--- a/board/freescale/mx6sabresd/mx6sabresd.c
+++ b/board/freescale/mx6sabresd/mx6sabresd.c
@@ -747,23 +747,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- if (is_mx6dqp()) {
- /* set IPU AXI-id1 Qos=0x1 AXI-id0/2/3 Qos=0x7 */
- writel(0x77177717, &iomux->gpr[6]);
- writel(0x77177717, &iomux->gpr[7]);
- } else {
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
- }
-}
-
static int mx6q_dcd_table[] = {
0x020e0798, 0x000C0000,
0x020e0758, 0x00000000,
diff --git a/board/gateworks/gw_ventana/gw_ventana_spl.c b/board/gateworks/gw_ventana/gw_ventana_spl.c
index 9524da7..c2e370b 100644
--- a/board/gateworks/gw_ventana/gw_ventana_spl.c
+++ b/board/gateworks/gw_ventana/gw_ventana_spl.c
@@ -583,17 +583,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
/*
* called from C runtime startup code (arch/arm/lib/crt0.S:_main)
* - we have a stack and a place to store GD, both in SRAM
diff --git a/board/kosagi/novena/novena_spl.c b/board/kosagi/novena/novena_spl.c
index 3645b75..512f06d 100644
--- a/board/kosagi/novena/novena_spl.c
+++ b/board/kosagi/novena/novena_spl.c
@@ -550,17 +550,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
/*
* called from C runtime startup code (arch/arm/lib/crt0.S:_main)
* - we have a stack and a place to store GD, both in SRAM
diff --git a/board/liebherr/mccmon6/spl.c b/board/liebherr/mccmon6/spl.c
index 15844ef..a2f804d 100644
--- a/board/liebherr/mccmon6/spl.c
+++ b/board/liebherr/mccmon6/spl.c
@@ -260,17 +260,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
static void spl_dram_init(void)
{
if (is_cpu_type(MXC_CPU_MX6SOLO)) {
diff --git a/board/phytec/pcm058/pcm058.c b/board/phytec/pcm058/pcm058.c
index 4257fbc..1538158 100644
--- a/board/phytec/pcm058/pcm058.c
+++ b/board/phytec/pcm058/pcm058.c
@@ -487,18 +487,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
-
static void spl_dram_init(void)
{
struct mx6_ddr_sysinfo sysinfo = {
diff --git a/board/phytec/pfla02/Kconfig b/board/phytec/pfla02/Kconfig
index 142a122..f4da68b 100644
--- a/board/phytec/pfla02/Kconfig
+++ b/board/phytec/pfla02/Kconfig
@@ -9,4 +9,10 @@ config SYS_VENDOR
config SYS_CONFIG_NAME
default "pfla02"
+config SPL_DRAM_1_BANK
+ bool "DRAM on just one bank"
+ help
+ activate, if the module has just one bank
+ of RAM
+
endif
diff --git a/board/phytec/pfla02/pfla02.c b/board/phytec/pfla02/pfla02.c
index 8d2ce63..136f1d5 100644
--- a/board/phytec/pfla02/pfla02.c
+++ b/board/phytec/pfla02/pfla02.c
@@ -485,9 +485,9 @@ static const struct mx6_mmdc_calibration mx6_mmcd_calib = {
/* Index in RAM Chip array */
enum {
- RAM_1GB,
- RAM_2GB,
- RAM_4GB
+ RAM_MT64K,
+ RAM_MT128K,
+ RAM_MT256K
};
static struct mx6_ddr3_cfg mt41k_xx[] = {
@@ -550,42 +550,11 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
+static void spl_dram_init(struct mx6_ddr_sysinfo *sysinfo,
+ struct mx6_ddr3_cfg *mem_ddr)
{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
-static void spl_dram_init(struct mx6_ddr3_cfg *mem_ddr)
-{
- struct mx6_ddr_sysinfo sysinfo = {
- /* width of data bus:0=16,1=32,2=64 */
- .dsize = 2,
- /* config for full 4GB range so that get_mem_size() works */
- .cs_density = 32, /* 32Gb per CS */
- /* single chip select */
- .ncs = 2,
- .cs1_mirror = 0,
- .rtt_wr = 1 /*DDR3_RTT_60_OHM*/, /* RTT_Wr = RZQ/4 */
- .rtt_nom = 1 /*DDR3_RTT_60_OHM*/, /* RTT_Nom = RZQ/4 */
- .walat = 1, /* Write additional latency */
- .ralat = 5, /* Read additional latency */
- .mif3_mode = 3, /* Command prediction working mode */
- .bi_on = 1, /* Bank interleaving enabled */
- .sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */
- .rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */
- .ddr_type = DDR_TYPE_DDR3,
- .refsel = 1, /* Refresh cycles at 32KHz */
- .refr = 7, /* 8 refresh commands per refresh cycle */
- };
-
mx6dq_dram_iocfg(64, &mx6_ddr_ioregs, &mx6_grp_ioregs);
- mx6_dram_cfg(&sysinfo, &mx6_mmcd_calib, mem_ddr);
+ mx6_dram_cfg(sysinfo, &mx6_mmcd_calib, mem_ddr);
}
int board_mmc_init(bd_t *bis)
@@ -627,10 +596,12 @@ void board_boot_order(u32 *spl_boot_list)
* Function checks for mirrors in the first CS
*/
#define RAM_TEST_PATTERN 0xaa5555aa
-static unsigned int pfla02_detect_ramsize(void)
+#define MIN_BANK_SIZE (512 * 1024 * 1024)
+
+static unsigned int pfla02_detect_chiptype(void)
{
u32 *p, *p1;
- unsigned int offset = 512 * 1024 * 1024;
+ unsigned int offset = MIN_BANK_SIZE;
int i;
for (i = 0; i < 2; i++) {
@@ -649,12 +620,38 @@ static unsigned int pfla02_detect_ramsize(void)
if (*p == *p1)
return i;
}
- return RAM_4GB;
+ return RAM_MT256K;
}
void board_init_f(ulong dummy)
{
unsigned int ramchip;
+
+ struct mx6_ddr_sysinfo sysinfo = {
+ /* width of data bus:0=16,1=32,2=64 */
+ .dsize = 2,
+ /* config for full 4GB range so that get_mem_size() works */
+ .cs_density = 32, /* 512 MB */
+ /* single chip select */
+#if IS_ENABLED(CONFIG_SPL_DRAM_1_BANK)
+ .ncs = 1,
+#else
+ .ncs = 2,
+#endif
+ .cs1_mirror = 1,
+ .rtt_wr = 1 /*DDR3_RTT_60_OHM*/, /* RTT_Wr = RZQ/4 */
+ .rtt_nom = 1 /*DDR3_RTT_60_OHM*/, /* RTT_Nom = RZQ/4 */
+ .walat = 1, /* Write additional latency */
+ .ralat = 5, /* Read additional latency */
+ .mif3_mode = 3, /* Command prediction working mode */
+ .bi_on = 1, /* Bank interleaving enabled */
+ .sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */
+ .rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */
+ .ddr_type = DDR_TYPE_DDR3,
+ .refsel = 1, /* Refresh cycles at 32KHz */
+ .refr = 7, /* 8 refresh commands per refresh cycle */
+ };
+
#ifdef CONFIG_CMD_NAND
/* Enable NAND */
setup_gpmi_nand();
@@ -682,10 +679,23 @@ void board_init_f(ulong dummy)
setup_gpios();
/* DDR initialization */
- spl_dram_init(&mt41k_xx[RAM_4GB]);
- ramchip = pfla02_detect_ramsize();
- if (ramchip != RAM_4GB)
- spl_dram_init(&mt41k_xx[ramchip]);
+ spl_dram_init(&sysinfo, &mt41k_xx[RAM_MT256K]);
+ ramchip = pfla02_detect_chiptype();
+ debug("Detected chip %d\n", ramchip);
+#if !IS_ENABLED(CONFIG_SPL_DRAM_1_BANK)
+ switch (ramchip) {
+ case RAM_MT64K:
+ sysinfo.cs_density = 6;
+ break;
+ case RAM_MT128K:
+ sysinfo.cs_density = 10;
+ break;
+ case RAM_MT256K:
+ sysinfo.cs_density = 18;
+ break;
+ }
+#endif
+ spl_dram_init(&sysinfo, &mt41k_xx[ramchip]);
/* Clear the BSS. */
memset(__bss_start, 0, __bss_end - __bss_start);
diff --git a/board/solidrun/mx6cuboxi/mx6cuboxi.c b/board/solidrun/mx6cuboxi/mx6cuboxi.c
index 7e59fb2..986abc5 100644
--- a/board/solidrun/mx6cuboxi/mx6cuboxi.c
+++ b/board/solidrun/mx6cuboxi/mx6cuboxi.c
@@ -581,17 +581,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
static void spl_dram_init(int width)
{
struct mx6_ddr_sysinfo sysinfo = {
diff --git a/board/toradex/apalis_imx6/apalis_imx6.c b/board/toradex/apalis_imx6/apalis_imx6.c
index 7a3e493..ebc6c12 100644
--- a/board/toradex/apalis_imx6/apalis_imx6.c
+++ b/board/toradex/apalis_imx6/apalis_imx6.c
@@ -29,6 +29,7 @@
#include <dm/platform_data/serial_mxc.h>
#include <dm/platdata.h>
#include <fsl_esdhc.h>
+#include <g_dnl.h>
#include <i2c.h>
#include <imx_thermal.h>
#include <linux/errno.h>
@@ -1159,17 +1160,6 @@ static void ccgr_init(void)
writel(0x000000FB, &ccm->ccosr);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
static void ddr_init(int *table, int size)
{
int i;
@@ -1234,6 +1224,18 @@ void reset_cpu(ulong addr)
{
}
+#ifdef CONFIG_SPL_USB_GADGET_SUPPORT
+int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
+{
+ unsigned short usb_pid;
+
+ usb_pid = TORADEX_USB_PRODUCT_NUM_OFFSET + 0xfff;
+ put_unaligned(usb_pid, &dev->idProduct);
+
+ return 0;
+}
+#endif
+
#endif
static struct mxc_serial_platdata mxc_serial_plat = {
diff --git a/board/toradex/colibri_imx6/colibri_imx6.c b/board/toradex/colibri_imx6/colibri_imx6.c
index dbcd233..669d912 100644
--- a/board/toradex/colibri_imx6/colibri_imx6.c
+++ b/board/toradex/colibri_imx6/colibri_imx6.c
@@ -28,6 +28,7 @@
#include <dm/platform_data/serial_mxc.h>
#include <dm/platdata.h>
#include <fsl_esdhc.h>
+#include <g_dnl.h>
#include <i2c.h>
#include <imx_thermal.h>
#include <linux/errno.h>
@@ -1036,17 +1037,6 @@ static void ccgr_init(void)
writel(0x000000FB, &ccm->ccosr);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
static void ddr_init(int *table, int size)
{
int i;
@@ -1118,6 +1108,18 @@ void reset_cpu(ulong addr)
{
}
+#ifdef CONFIG_SPL_USB_GADGET_SUPPORT
+int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
+{
+ unsigned short usb_pid;
+
+ usb_pid = TORADEX_USB_PRODUCT_NUM_OFFSET + 0xfff;
+ put_unaligned(usb_pid, &dev->idProduct);
+
+ return 0;
+}
+#endif
+
#endif
static struct mxc_serial_platdata mxc_serial_plat = {
diff --git a/board/udoo/udoo_spl.c b/board/udoo/udoo_spl.c
index e83e7c3..3645969 100644
--- a/board/udoo/udoo_spl.c
+++ b/board/udoo/udoo_spl.c
@@ -211,17 +211,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000FF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
static void spl_dram_init(void)
{
if (is_cpu_type(MXC_CPU_MX6DL)) {
diff --git a/board/wandboard/spl.c b/board/wandboard/spl.c
index 47082a8..99a0286 100644
--- a/board/wandboard/spl.c
+++ b/board/wandboard/spl.c
@@ -266,17 +266,6 @@ static void ccgr_init(void)
writel(0x000003FF, &ccm->CCGR6);
}
-static void gpr_init(void)
-{
- struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- /* enable AXI cache for VDOA/VPU/IPU */
- writel(0xF00000CF, &iomux->gpr[4]);
- /* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */
- writel(0x007F007F, &iomux->gpr[6]);
- writel(0x007F007F, &iomux->gpr[7]);
-}
-
static void spl_dram_init(void)
{
if (is_cpu_type(MXC_CPU_MX6SOLO)) {
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 42d955c..d6d130e 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -895,6 +895,13 @@ config CMD_USB
help
USB support.
+config CMD_USB_SDP
+ bool "sdp"
+ select USB_FUNCTION_SDP
+ help
+ Enables the command "sdp" which is used to have U-Boot emulating the
+ Serial Download Protocol (SDP) via USB.
+
config CMD_USB_MASS_STORAGE
bool "UMS usb mass storage"
help
diff --git a/cmd/Makefile b/cmd/Makefile
index 4a865bd..2a5b8ce 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_CMD_FASTBOOT) += fastboot.o
obj-$(CONFIG_CMD_FS_UUID) += fs_uuid.o
obj-$(CONFIG_CMD_USB_MASS_STORAGE) += usb_mass_storage.o
+obj-$(CONFIG_CMD_USB_SDP) += usb_gadget_sdp.o
obj-$(CONFIG_CMD_THOR_DOWNLOAD) += thordown.o
obj-$(CONFIG_CMD_XIMG) += ximg.o
obj-$(CONFIG_CMD_YAFFS2) += yaffs2.o
diff --git a/cmd/usb_gadget_sdp.c b/cmd/usb_gadget_sdp.c
new file mode 100644
index 0000000..b1d8b28
--- /dev/null
+++ b/cmd/usb_gadget_sdp.c
@@ -0,0 +1,50 @@
+/*
+ * cmd_sdp.c -- sdp command
+ *
+ * Copyright (C) 2016 Toradex
+ * Author: Stefan Agner <stefan.agner@toradex.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <g_dnl.h>
+#include <sdp.h>
+#include <usb.h>
+
+static int do_sdp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ int ret = CMD_RET_FAILURE;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ char *usb_controller = argv[1];
+ int controller_index = simple_strtoul(usb_controller, NULL, 0);
+ board_usb_init(controller_index, USB_INIT_DEVICE);
+
+ g_dnl_clear_detach();
+ g_dnl_register("usb_dnl_sdp");
+
+ ret = sdp_init(controller_index);
+ if (ret) {
+ error("SDP init failed: %d", ret);
+ goto exit;
+ }
+
+ /* This command typically does not return but jumps to an image */
+ sdp_handle(controller_index);
+ error("SDP ended");
+
+exit:
+ g_dnl_unregister();
+ board_usb_cleanup(controller_index, USB_INIT_DEVICE);
+
+ return ret;
+}
+
+U_BOOT_CMD(sdp, 2, 1, do_sdp,
+ "Serial Downloader Protocol",
+ "<USB_controller>\n"
+ " - serial downloader protocol via <USB_controller>\n"
+);
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 582b685..9446cd9 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -668,6 +668,12 @@ config SPL_DFU_RAM
endchoice
+config SPL_USB_SDP_SUPPORT
+ bool "Support SDP (Serial Download Protocol)"
+ help
+ Enable Serial Download Protocol (SDP) device support in SPL. This
+ allows to download images into memory and execute (jump to) them
+ using the same protocol as implemented by the i.MX family's boot ROM.
endif
config SPL_WATCHDOG_SUPPORT
diff --git a/common/spl/Makefile b/common/spl/Makefile
index fde0d09..e229947 100644
--- a/common/spl/Makefile
+++ b/common/spl/Makefile
@@ -30,4 +30,5 @@ obj-$(CONFIG_$(SPL_TPL_)SATA_SUPPORT) += spl_sata.o
obj-$(CONFIG_$(SPL_TPL_)DFU_SUPPORT) += spl_dfu.o
obj-$(CONFIG_$(SPL_TPL_)SPI_LOAD) += spl_spi.o
obj-$(CONFIG_$(SPL_TPL_)RAM_SUPPORT) += spl_ram.o
+obj-$(CONFIG_$(SPL_TPL_)USB_SDP_SUPPORT) += spl_sdp.o
endif
diff --git a/common/spl/spl.c b/common/spl/spl.c
index d245cfc..8b219ba 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -379,7 +379,7 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
if (spl_init())
hang();
}
-#ifndef CONFIG_PPC
+#if !defined(CONFIG_PPC) && !defined(CONFIG_ARCH_MX6)
/*
* timer_init() does not exist on PPC systems. The timer is initialized
* and enabled (decrementer) in interrupt_init() here.
diff --git a/common/spl/spl_sdp.c b/common/spl/spl_sdp.c
new file mode 100644
index 0000000..350bcdb
--- /dev/null
+++ b/common/spl/spl_sdp.c
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 2016 Toradex
+ * Author: Stefan Agner <stefan.agner@toradex.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <spl.h>
+#include <usb.h>
+#include <g_dnl.h>
+#include <sdp.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int spl_sdp_load_image(struct spl_image_info *spl_image,
+ struct spl_boot_device *bootdev)
+{
+ int ret;
+ const int controller_index = 0;
+
+ g_dnl_clear_detach();
+ g_dnl_register("usb_dnl_sdp");
+
+ ret = sdp_init(controller_index);
+ if (ret) {
+ error("SDP init failed: %d", ret);
+ return -ENODEV;
+ }
+
+ /* This command typically does not return but jumps to an image */
+ sdp_handle(controller_index);
+ error("SDP ended");
+
+ return -EINVAL;
+}
+SPL_LOAD_IMAGE_METHOD("USB SDP", 0, BOOT_DEVICE_BOARD, spl_sdp_load_image);
diff --git a/configs/apalis_imx6_defconfig b/configs/apalis_imx6_defconfig
index 89b6670..ba906c6 100644
--- a/configs/apalis_imx6_defconfig
+++ b/configs/apalis_imx6_defconfig
@@ -16,6 +16,9 @@ CONFIG_BOARD_EARLY_INIT_F=y
CONFIG_SPL=y
CONFIG_SPL_DMA_SUPPORT=y
CONFIG_SPL_I2C_SUPPORT=y
+CONFIG_SPL_USB_HOST_SUPPORT=y
+CONFIG_SPL_USB_GADGET_SUPPORT=y
+CONFIG_SPL_USB_SDP_SUPPORT=y
CONFIG_HUSH_PARSER=y
CONFIG_SYS_PROMPT="Apalis iMX6 # "
CONFIG_CMD_BOOTZ=y
@@ -32,6 +35,7 @@ CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_USB=y
+CONFIG_CMD_USB_SDP=y
CONFIG_CMD_USB_MASS_STORAGE=y
CONFIG_CMD_DHCP=y
CONFIG_CMD_MII=y
diff --git a/configs/colibri_imx6_defconfig b/configs/colibri_imx6_defconfig
index 35e881b..0637383 100644
--- a/configs/colibri_imx6_defconfig
+++ b/configs/colibri_imx6_defconfig
@@ -16,6 +16,9 @@ CONFIG_BOARD_EARLY_INIT_F=y
CONFIG_SPL=y
CONFIG_SPL_DMA_SUPPORT=y
CONFIG_SPL_I2C_SUPPORT=y
+CONFIG_SPL_USB_HOST_SUPPORT=y
+CONFIG_SPL_USB_GADGET_SUPPORT=y
+CONFIG_SPL_USB_SDP_SUPPORT=y
CONFIG_HUSH_PARSER=y
CONFIG_SYS_PROMPT="Colibri iMX6 # "
CONFIG_CMD_BOOTZ=y
@@ -32,6 +35,7 @@ CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_USB=y
+CONFIG_CMD_USB_SDP=y
CONFIG_CMD_USB_MASS_STORAGE=y
CONFIG_CMD_DHCP=y
CONFIG_CMD_MII=y
diff --git a/configs/imx6qdl_icore_mmc_defconfig b/configs/imx6qdl_icore_mmc_defconfig
index 2a0679f..5a84c63 100644
--- a/configs/imx6qdl_icore_mmc_defconfig
+++ b/configs/imx6qdl_icore_mmc_defconfig
@@ -49,3 +49,4 @@ CONFIG_DEBUG_UART_CLOCK=24000000
CONFIG_MXC_UART=y
CONFIG_IMX_THERMAL=y
CONFIG_VIDEO_IPUV3=y
+CONFIG_SPL_OS_BOOT=y
diff --git a/doc/README.sdp b/doc/README.sdp
new file mode 100644
index 0000000..9b438c0
--- /dev/null
+++ b/doc/README.sdp
@@ -0,0 +1,100 @@
+-------------
+SDP in U-Boot
+-------------
+
+SDP stands for serial download protocol. It is the protocol used in NXP's
+i.MX SoCs ROM Serial Downloader and provides means to download a program
+image to the chip over USB and UART serial connection.
+
+The implementation in U-Boot uses the USB Downloader Gadget (g_dnl) to
+provide a SDP implementation over USB. This allows to download program
+images to the target in SPL/U-Boot using the same protocol/tooling the
+SoC's recovery mechanism is using.
+
+The SDP protocol over USB is a USB HID class protocol. USB HID class
+protocols allow to access a USB device without OS specific drivers. The
+U-Boot implementation has primarly been tested using the open source
+imx_loader utility (https://github.com/toradex/imx_loader).
+
+The host side utilities are typically capable to interpret the i.MX
+specific image header (see doc/README.imximage). There are extensions
+for imx_loader's imx_usb utility which allow to interpret the U-Boot
+specific legacy image format (see mkimage(1)). Also the U-Boot side
+support beside the i.MX specific header the U-Boot legacy header.
+
+Usage
+-----
+
+This implementation can be started in U-Boot using the sdp command
+(CONFIG_CMD_USB_SDP) or in SPL if Serial Downloader boot mode has been
+detected (CONFIG_SPL_USB_SDP_SUPPORT).
+
+A typical use case is downloading full U-Boot after SPL has been
+downloaded through the boot ROM's Serial Downloader. Using boot mode
+detection the SPL will run the SDP implementation automatically in
+this case:
+
+ # imx_usb SPL
+
+Targets Serial Console:
+
+ Trying to boot from USB SDP
+ SDP: initialize...
+ SDP: handle requests...
+
+At this point the SPL reenumerated as a new HID device and emulating
+the boot ROM's SDP protocol. The USB VID/PID will depend on standard
+U-Boot configurations CONFIG_G_DNL_(VENDOR|PRODUCT)_NUM. Make sure
+imx_usb is aware of the USB VID/PID for your device by adding a
+configuration entry in imx_usb.conf:
+
+ 0x1b67:0x4fff, mx6_usb_sdp_spl.conf
+
+And the device specific configuration file mx6_usb_sdp_spl.conf:
+
+ mx6_spl_sdp
+ hid,uboot_header,1024,0x910000,0x10000000,1G,0x00900000,0x40000
+
+This allows to download the regular U-Boot with legacy image headers
+(u-boot.img) using a second invocation of imx_usb:
+
+ # imx_usb u-boot.img
+
+Furthermore, when U-Boot is running the sdp command can be used to
+download and run scripts:
+
+ # imx_usb script.scr
+
+imx_usb configuration files can be also used to download multiple
+files and of arbitrary types, e.g.
+
+ mx6_usb_sdp_uboot
+ hid,1024,0x10000000,1G,0x00907000,0x31000
+ full.itb:load 0x12100000
+ boot.scr:load 0x12000000,jump 0x12000000
+
+There is also a batch mode which allows imx_usb to handle multiple
+consecutive reenumerations by adding multiple VID/PID specifications
+in imx_usb.conf:
+
+ 0x15a2:0x0061, mx6_usb_rom.conf, 0x1b67:0x4fff, mx6_usb_sdp_spl.conf
+
+In this mode the file to download (imx_usb job) needs to be specified
+in the configuration files.
+
+mx6_usb_rom.conf:
+
+ mx6_qsb
+ hid,1024,0x910000,0x10000000,1G,0x00900000,0x40000
+ SPL:jump header2
+
+mx6_usb_sdp_spl.conf:
+
+ mx6_spl_sdp
+ hid,uboot_header,1024,0x10000000,1G,0x00907000,0x31000
+ u-boot.img:jump header2
+
+With that SPL and U-Boot can be downloaded with a single invocation
+of imx_usb without arguments:
+
+ # imx_usb
diff --git a/drivers/pinctrl/nxp/pinctrl-imx.c b/drivers/pinctrl/nxp/pinctrl-imx.c
index 1b6107f..32cbac9 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx.c
@@ -158,7 +158,7 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config)
if (!(config_val & IMX_NO_PAD_CTL)) {
if (info->flags & SHARE_MUX_CONF_REG) {
clrsetbits_le32(info->base + conf_reg,
- info->mux_mask, config_val);
+ ~info->mux_mask, config_val);
} else {
writel(config_val, info->base + conf_reg);
}
diff --git a/drivers/pinctrl/nxp/pinctrl-imx7ulp.c b/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
index 4a893e5..618ce6a 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx7ulp.c
@@ -12,7 +12,11 @@
#include "pinctrl-imx.h"
-static struct imx_pinctrl_soc_info imx7ulp_pinctrl_soc_info = {
+static struct imx_pinctrl_soc_info imx7ulp_pinctrl_soc_info0 = {
+ .flags = ZERO_OFFSET_VALID | SHARE_MUX_CONF_REG | CONFIG_IBE_OBE,
+};
+
+static struct imx_pinctrl_soc_info imx7ulp_pinctrl_soc_info1 = {
.flags = ZERO_OFFSET_VALID | SHARE_MUX_CONF_REG | CONFIG_IBE_OBE,
};
@@ -25,8 +29,8 @@ static int imx7ulp_pinctrl_probe(struct udevice *dev)
}
static const struct udevice_id imx7ulp_pinctrl_match[] = {
- { .compatible = "fsl,imx7ulp-iomuxc-0", .data = (ulong)&imx7ulp_pinctrl_soc_info },
- { .compatible = "fsl,imx7ulp-iomuxc-1", .data = (ulong)&imx7ulp_pinctrl_soc_info },
+ { .compatible = "fsl,imx7ulp-iomuxc-0", .data = (ulong)&imx7ulp_pinctrl_soc_info0 },
+ { .compatible = "fsl,imx7ulp-iomuxc-1", .data = (ulong)&imx7ulp_pinctrl_soc_info1 },
{ /* sentinel */ }
};
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 261ed12..225b66b 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -103,6 +103,13 @@ config USB_GADGET_DOWNLOAD
if USB_GADGET_DOWNLOAD
+config USB_FUNCTION_SDP
+ bool "Enable USB SDP (Serial Download Protocol)"
+ help
+ Enable Serial Download Protocol (SDP) device support in U-Boot. This
+ allows to download images into memory and execute (jump to) them
+ using the same protocol as implemented by the i.MX family's boot ROM.
+
config G_DNL_MANUFACTURER
string "Vendor name of USB device"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5e316a7..7258099 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_USB_GADGET_SUPPORT) += g_dnl.o
obj-$(CONFIG_SPL_DFU_SUPPORT) += f_dfu.o
+obj-$(CONFIG_SPL_USB_SDP_SUPPORT) += f_sdp.o
endif
# new USB gadget layer dependencies
@@ -28,6 +29,7 @@ obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o
obj-$(CONFIG_USB_FUNCTION_DFU) += f_dfu.o
obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o
obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o
+obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o
endif
endif
ifdef CONFIG_USB_ETHER
diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c
new file mode 100644
index 0000000..0fae66b
--- /dev/null
+++ b/drivers/usb/gadget/f_sdp.c
@@ -0,0 +1,737 @@
+/*
+ * f_sdp.c -- USB HID Serial Download Protocol
+ *
+ * Copyright (C) 2017 Toradex
+ * Author: Stefan Agner <stefan.agner@toradex.com>
+ *
+ * This file implements the Serial Download Protocol (SDP) as specified in
+ * the i.MX 6 Reference Manual. The SDP is a USB HID based protocol and
+ * allows to download images directly to memory. The implementation
+ * works with the imx_loader (imx_usb) USB client software on host side.
+ *
+ * Not all commands are implemented, e.g. WRITE_REGISTER, DCD_WRITE and
+ * SKIP_DCD_HEADER are only stubs.
+ *
+ * Parts of the implementation are based on f_dfu and f_thor.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <errno.h>
+#include <common.h>
+#include <console.h>
+#include <malloc.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include <asm/io.h>
+#include <g_dnl.h>
+#include <sdp.h>
+#include <spl.h>
+#include <image.h>
+#include <imximage.h>
+
+#define HID_REPORT_ID_MASK 0x000000ff
+
+/*
+ * HID class requests
+ */
+#define HID_REQ_GET_REPORT 0x01
+#define HID_REQ_GET_IDLE 0x02
+#define HID_REQ_GET_PROTOCOL 0x03
+#define HID_REQ_SET_REPORT 0x09
+#define HID_REQ_SET_IDLE 0x0A
+#define HID_REQ_SET_PROTOCOL 0x0B
+
+#define HID_USAGE_PAGE_LEN 76
+
+struct hid_report {
+ u8 usage_page[HID_USAGE_PAGE_LEN];
+} __packed;
+
+#define SDP_READ_REGISTER 0x0101
+#define SDP_WRITE_REGISTER 0x0202
+#define SDP_WRITE_FILE 0x0404
+#define SDP_ERROR_STATUS 0x0505
+#define SDP_DCD_WRITE 0x0a0a
+#define SDP_JUMP_ADDRESS 0x0b0b
+#define SDP_SKIP_DCD_HEADER 0x0c0c
+
+#define SDP_SECURITY_CLOSED 0x12343412
+#define SDP_SECURITY_OPEN 0x56787856
+
+#define SDP_WRITE_FILE_COMPLETE 0x88888888
+#define SDP_WRITE_REGISTER_COMPLETE 0x128A8A12
+#define SDP_SKIP_DCD_HEADER_COMPLETE 0x900DD009
+#define SDP_ERROR_IMXHEADER 0x000a0533
+
+#define SDP_COMMAND_LEN 16
+
+struct sdp_command {
+ u16 cmd;
+ u32 addr;
+ u8 format;
+ u32 cnt;
+ u32 data;
+ u8 rsvd;
+} __packed;
+
+enum sdp_state {
+ SDP_STATE_IDLE,
+ SDP_STATE_RX_DCD_DATA,
+ SDP_STATE_RX_FILE_DATA,
+ SDP_STATE_TX_SEC_CONF,
+ SDP_STATE_TX_SEC_CONF_BUSY,
+ SDP_STATE_TX_REGISTER,
+ SDP_STATE_TX_REGISTER_BUSY,
+ SDP_STATE_TX_STATUS,
+ SDP_STATE_TX_STATUS_BUSY,
+ SDP_STATE_JUMP,
+};
+
+struct f_sdp {
+ struct usb_function usb_function;
+
+ struct usb_descriptor_header **function;
+
+ u8 altsetting;
+ enum sdp_state state;
+ enum sdp_state next_state;
+ u32 dnl_address;
+ u32 dnl_bytes_remaining;
+ u32 jmp_address;
+ bool always_send_status;
+ u32 error_status;
+
+ /* EP0 request */
+ struct usb_request *req;
+
+ /* EP1 IN */
+ struct usb_ep *in_ep;
+ struct usb_request *in_req;
+
+ bool configuration_done;
+};
+
+static struct f_sdp *sdp_func;
+
+static inline struct f_sdp *func_to_sdp(struct usb_function *f)
+{
+ return container_of(f, struct f_sdp, usb_function);
+}
+
+static struct usb_interface_descriptor sdp_intf_runtime = {
+ .bLength = sizeof(sdp_intf_runtime),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+/* HID configuration */
+static struct usb_class_hid_descriptor sdp_hid_desc = {
+ .bLength = sizeof(sdp_hid_desc),
+ .bDescriptorType = USB_DT_CS_DEVICE,
+
+ .bcdCDC = __constant_cpu_to_le16(0x0110),
+ .bCountryCode = 0,
+ .bNumDescriptors = 1,
+
+ .bDescriptorType0 = USB_DT_HID_REPORT,
+ .wDescriptorLength0 = HID_USAGE_PAGE_LEN,
+};
+
+static struct usb_endpoint_descriptor in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
+
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ .bInterval = 1,
+};
+
+static struct usb_descriptor_header *sdp_runtime_descs[] = {
+ (struct usb_descriptor_header *)&sdp_intf_runtime,
+ (struct usb_descriptor_header *)&sdp_hid_desc,
+ (struct usb_descriptor_header *)&in_desc,
+ NULL,
+};
+
+/* This is synchronized with what the SoC implementation reports */
+static struct hid_report sdp_hid_report = {
+ .usage_page = {
+ 0x06, 0x00, 0xff, /* Usage Page */
+ 0x09, 0x01, /* Usage (Pointer?) */
+ 0xa1, 0x01, /* Collection */
+
+ 0x85, 0x01, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size */
+ 0x95, 0x10, /* Report Count */
+ 0x91, 0x02, /* Output Data */
+
+ 0x85, 0x02, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x80, /* Report Size 128 */
+ 0x95, 0x40, /* Report Count */
+ 0x91, 0x02, /* Output Data */
+
+ 0x85, 0x03, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size 8 */
+ 0x95, 0x04, /* Report Count */
+ 0x81, 0x02, /* Input Data */
+
+ 0x85, 0x04, /* Report ID */
+ 0x19, 0x01, /* Usage Minimum */
+ 0x29, 0x01, /* Usage Maximum */
+ 0x15, 0x00, /* Local Minimum */
+ 0x26, 0xFF, 0x00, /* Local Maximum? */
+ 0x75, 0x08, /* Report Size 8 */
+ 0x95, 0x40, /* Report Count */
+ 0x81, 0x02, /* Input Data */
+ 0xc0
+ },
+};
+
+static const char sdp_name[] = "Serial Downloader Protocol";
+
+/*
+ * static strings, in UTF-8
+ */
+static struct usb_string strings_sdp_generic[] = {
+ [0].s = sdp_name,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_sdp_generic = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_sdp_generic,
+};
+
+static struct usb_gadget_strings *sdp_generic_strings[] = {
+ &stringtab_sdp_generic,
+ NULL,
+};
+
+static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+ u8 *data = req->buf;
+ u8 report = data[0];
+
+ if (status != 0) {
+ error("Status: %d", status);
+ return;
+ }
+
+ if (report != 1) {
+ error("Unexpected report %d", report);
+ return;
+ }
+
+ struct sdp_command *cmd = req->buf + 1;
+
+ debug("%s: command: %04x, addr: %08x, cnt: %u\n",
+ __func__, be16_to_cpu(cmd->cmd),
+ be32_to_cpu(cmd->addr), be32_to_cpu(cmd->cnt));
+
+ switch (be16_to_cpu(cmd->cmd)) {
+ case SDP_READ_REGISTER:
+ sdp->always_send_status = false;
+ sdp->error_status = 0x0;
+
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->dnl_address = be32_to_cpu(cmd->addr);
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->next_state = SDP_STATE_TX_REGISTER;
+ printf("Reading %d registers at 0x%08x... ",
+ sdp->dnl_bytes_remaining, sdp->dnl_address);
+ break;
+ case SDP_WRITE_FILE:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_WRITE_FILE_COMPLETE;
+
+ sdp->state = SDP_STATE_RX_FILE_DATA;
+ sdp->dnl_address = be32_to_cpu(cmd->addr);
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->next_state = SDP_STATE_IDLE;
+
+ printf("Downloading file of size %d to 0x%08x... ",
+ sdp->dnl_bytes_remaining, sdp->dnl_address);
+
+ break;
+ case SDP_ERROR_STATUS:
+ sdp->always_send_status = true;
+ sdp->error_status = 0;
+
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ case SDP_DCD_WRITE:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_WRITE_REGISTER_COMPLETE;
+
+ sdp->state = SDP_STATE_RX_DCD_DATA;
+ sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ case SDP_JUMP_ADDRESS:
+ sdp->always_send_status = false;
+ sdp->error_status = 0;
+
+ sdp->jmp_address = be32_to_cpu(cmd->addr);
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_JUMP;
+ break;
+ case SDP_SKIP_DCD_HEADER:
+ sdp->always_send_status = true;
+ sdp->error_status = SDP_SKIP_DCD_HEADER_COMPLETE;
+
+ /* Ignore command, DCD not supported anyway */
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ sdp->next_state = SDP_STATE_IDLE;
+ break;
+ default:
+ error("Unknown command: %04x\n", be16_to_cpu(cmd->cmd));
+ }
+}
+
+static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+ u8 *data = req->buf;
+ u8 report = data[0];
+ int datalen = req->length - 1;
+
+ if (status != 0) {
+ error("Status: %d", status);
+ return;
+ }
+
+ if (report != 2) {
+ error("Unexpected report %d", report);
+ return;
+ }
+
+ if (sdp->dnl_bytes_remaining < datalen) {
+ /*
+ * Some USB stacks require to send a complete buffer as
+ * specified in the HID descriptor. This leads to longer
+ * transfers than the file length, no problem for us.
+ */
+ sdp->dnl_bytes_remaining = 0;
+ } else {
+ sdp->dnl_bytes_remaining -= datalen;
+ }
+
+ if (sdp->state == SDP_STATE_RX_FILE_DATA) {
+ memcpy((void *)sdp->dnl_address, req->buf + 1, datalen);
+ sdp->dnl_address += datalen;
+ }
+
+ if (sdp->dnl_bytes_remaining)
+ return;
+
+ printf("done\n");
+
+ switch (sdp->state) {
+ case SDP_STATE_RX_FILE_DATA:
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ break;
+ case SDP_STATE_RX_DCD_DATA:
+ sdp->state = SDP_STATE_TX_SEC_CONF;
+ break;
+ default:
+ error("Invalid state: %d", sdp->state);
+ }
+}
+
+static void sdp_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sdp *sdp = req->context;
+ int status = req->status;
+
+ if (status != 0) {
+ error("Status: %d", status);
+ return;
+ }
+
+ switch (sdp->state) {
+ case SDP_STATE_TX_SEC_CONF_BUSY:
+ /* Not all commands require status report */
+ if (sdp->always_send_status || sdp->error_status)
+ sdp->state = SDP_STATE_TX_STATUS;
+ else
+ sdp->state = sdp->next_state;
+
+ break;
+ case SDP_STATE_TX_STATUS_BUSY:
+ sdp->state = sdp->next_state;
+ break;
+ case SDP_STATE_TX_REGISTER_BUSY:
+ if (sdp->dnl_bytes_remaining)
+ sdp->state = SDP_STATE_TX_REGISTER;
+ else
+ sdp->state = SDP_STATE_IDLE;
+ break;
+ default:
+ error("Wrong State: %d", sdp->state);
+ sdp->state = SDP_STATE_IDLE;
+ break;
+ }
+ debug("%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+}
+
+static int sdp_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_gadget *gadget = f->config->cdev->gadget;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_sdp *sdp = f->config->cdev->req->context;
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ int value = 0;
+ u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
+
+ debug("w_value: 0x%04x len: 0x%04x\n", w_value, len);
+ debug("req_type: 0x%02x ctrl->bRequest: 0x%02x sdp->state: %d\n",
+ req_type, ctrl->bRequest, sdp->state);
+
+ if (req_type == USB_TYPE_STANDARD) {
+ if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
+ /* Send HID report descriptor */
+ value = min(len, (u16) sizeof(sdp_hid_report));
+ memcpy(req->buf, &sdp_hid_report, value);
+ sdp->configuration_done = true;
+ }
+ }
+
+ if (req_type == USB_TYPE_CLASS) {
+ int report = w_value & HID_REPORT_ID_MASK;
+
+ /* HID (SDP) request */
+ switch (ctrl->bRequest) {
+ case HID_REQ_SET_REPORT:
+ switch (report) {
+ case 1:
+ value = SDP_COMMAND_LEN + 1;
+ req->complete = sdp_rx_command_complete;
+ break;
+ case 2:
+ value = len;
+ req->complete = sdp_rx_data_complete;
+ break;
+ }
+ }
+ }
+
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < len;
+ value = usb_ep_queue(gadget->ep0, req, 0);
+ if (value < 0) {
+ debug("ep_queue --> %d\n", value);
+ req->status = 0;
+ }
+ }
+
+ return value;
+}
+
+static int sdp_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_sdp *sdp = func_to_sdp(f);
+ int rv = 0, id;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ sdp_intf_runtime.bInterfaceNumber = id;
+
+ struct usb_ep *ep;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(gadget, &in_desc);
+ if (!ep) {
+ rv = -ENODEV;
+ goto error;
+ }
+
+ sdp->in_ep = ep; /* Store IN EP for enabling @ setup */
+
+ cdev->req->context = sdp;
+
+error:
+ return rv;
+}
+
+static void sdp_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ free(sdp_func);
+ sdp_func = NULL;
+}
+
+static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, 0);
+ if (!req)
+ return req;
+
+ req->length = length;
+ req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ req = NULL;
+ }
+
+ return req;
+}
+
+
+static struct usb_request *sdp_start_ep(struct usb_ep *ep)
+{
+ struct usb_request *req;
+
+ req = alloc_ep_req(ep, 64);
+ debug("%s: ep:%p req:%p\n", __func__, ep, req);
+
+ if (!req)
+ return NULL;
+
+ memset(req->buf, 0, req->length);
+ req->complete = sdp_tx_complete;
+
+ return req;
+}
+static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int result;
+
+ debug("%s: intf: %d alt: %d\n", __func__, intf, alt);
+
+ result = usb_ep_enable(sdp->in_ep, &in_desc);
+ if (result)
+ return result;
+ sdp->in_req = sdp_start_ep(sdp->in_ep);
+ sdp->in_req->context = sdp;
+
+ sdp->in_ep->driver_data = cdev; /* claim */
+
+ sdp->altsetting = alt;
+ sdp->state = SDP_STATE_IDLE;
+
+ return 0;
+}
+
+static int sdp_get_alt(struct usb_function *f, unsigned intf)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+
+ return sdp->altsetting;
+}
+
+static void sdp_disable(struct usb_function *f)
+{
+ struct f_sdp *sdp = func_to_sdp(f);
+
+ usb_ep_disable(sdp->in_ep);
+
+ if (sdp->in_req) {
+ free(sdp->in_req);
+ sdp->in_req = NULL;
+ }
+}
+
+static int sdp_bind_config(struct usb_configuration *c)
+{
+ int status;
+
+ if (!sdp_func) {
+ sdp_func = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*sdp_func));
+ if (!sdp_func)
+ return -ENOMEM;
+ }
+
+ memset(sdp_func, 0, sizeof(*sdp_func));
+
+ sdp_func->usb_function.name = "sdp";
+ sdp_func->usb_function.hs_descriptors = sdp_runtime_descs;
+ sdp_func->usb_function.descriptors = sdp_runtime_descs;
+ sdp_func->usb_function.bind = sdp_bind;
+ sdp_func->usb_function.unbind = sdp_unbind;
+ sdp_func->usb_function.set_alt = sdp_set_alt;
+ sdp_func->usb_function.get_alt = sdp_get_alt;
+ sdp_func->usb_function.disable = sdp_disable;
+ sdp_func->usb_function.strings = sdp_generic_strings;
+ sdp_func->usb_function.setup = sdp_setup;
+
+ status = usb_add_function(c, &sdp_func->usb_function);
+
+ return status;
+}
+
+int sdp_init(int controller_index)
+{
+ printf("SDP: initialize...\n");
+ while (!sdp_func->configuration_done) {
+ if (ctrlc()) {
+ puts("\rCTRL+C - Operation aborted.\n");
+ return 1;
+ }
+ usb_gadget_handle_interrupts(controller_index);
+ }
+
+ return 0;
+}
+
+static u32 sdp_jump_imxheader(void *address)
+{
+ flash_header_v2_t *headerv2 = address;
+ ulong (*entry)(void);
+
+ if (headerv2->header.tag != IVT_HEADER_TAG) {
+ printf("Header Tag is not an IMX image\n");
+ return SDP_ERROR_IMXHEADER;
+ }
+
+ printf("Jumping to 0x%08x\n", headerv2->entry);
+ entry = (void *)headerv2->entry;
+ entry();
+
+ /* The image probably never returns hence we won't reach that point */
+ return 0;
+}
+
+static void sdp_handle_in_ep(void)
+{
+ u8 *data = sdp_func->in_req->buf;
+ u32 status;
+ int datalen;
+
+ switch (sdp_func->state) {
+ case SDP_STATE_TX_SEC_CONF:
+ debug("Report 3: HAB security\n");
+ data[0] = 3;
+
+ status = SDP_SECURITY_OPEN;
+ memcpy(&data[1], &status, 4);
+ sdp_func->in_req->length = 5;
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_SEC_CONF_BUSY;
+ break;
+
+ case SDP_STATE_TX_STATUS:
+ debug("Report 4: Status\n");
+ data[0] = 4;
+
+ memcpy(&data[1], &sdp_func->error_status, 4);
+ sdp_func->in_req->length = 65;
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_STATUS_BUSY;
+ break;
+ case SDP_STATE_TX_REGISTER:
+ debug("Report 4: Register Values\n");
+ data[0] = 4;
+
+ datalen = sdp_func->dnl_bytes_remaining;
+
+ if (datalen > 64)
+ datalen = 64;
+
+ memcpy(&data[1], (void *)sdp_func->dnl_address, datalen);
+ sdp_func->in_req->length = 65;
+
+ sdp_func->dnl_bytes_remaining -= datalen;
+ sdp_func->dnl_address += datalen;
+
+ usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
+ sdp_func->state = SDP_STATE_TX_REGISTER_BUSY;
+ break;
+ case SDP_STATE_JUMP:
+ printf("Jumping to header at 0x%08x\n", sdp_func->jmp_address);
+ status = sdp_jump_imxheader((void *)sdp_func->jmp_address);
+
+ /* If imx header fails, try some U-Boot specific headers */
+ if (status) {
+#ifdef CONFIG_SPL_BUILD
+ /* In SPL, allow jumps to U-Boot images */
+ struct spl_image_info spl_image = {};
+ spl_parse_image_header(&spl_image,
+ (struct image_header *)sdp_func->jmp_address);
+ jump_to_image_no_args(&spl_image);
+#else
+ /* In U-Boot, allow jumps to scripts */
+ source(sdp_func->jmp_address, "script@1");
+#endif
+ }
+
+ sdp_func->next_state = SDP_STATE_IDLE;
+ sdp_func->error_status = status;
+
+ /* Only send Report 4 if there was an error */
+ if (status)
+ sdp_func->state = SDP_STATE_TX_STATUS;
+ else
+ sdp_func->state = SDP_STATE_IDLE;
+ break;
+ default:
+ break;
+ };
+}
+
+void sdp_handle(int controller_index)
+{
+ printf("SDP: handle requests...\n");
+ while (1) {
+ if (ctrlc()) {
+ puts("\rCTRL+C - Operation aborted.\n");
+ return;
+ }
+
+ usb_gadget_handle_interrupts(controller_index);
+
+ sdp_handle_in_ep();
+ }
+}
+
+int sdp_add(struct usb_configuration *c)
+{
+ int id;
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+ strings_sdp_generic[0].id = id;
+ sdp_intf_runtime.iInterface = id;
+
+ debug("%s: cdev: %p gadget: %p gadget->ep0: %p\n", __func__,
+ c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
+
+ return sdp_bind_config(c);
+}
+
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_sdp, sdp_add);
diff --git a/include/configs/imx6-engicam.h b/include/configs/imx6-engicam.h
index e0bdb656..a1b7036 100644
--- a/include/configs/imx6-engicam.h
+++ b/include/configs/imx6-engicam.h
@@ -183,6 +183,20 @@
# define CONFIG_MII
#endif
+/* Falcon Mode */
+#ifdef CONFIG_SPL_OS_BOOT
+# define CONFIG_SPL_FS_LOAD_ARGS_NAME "args"
+# define CONFIG_SPL_FS_LOAD_KERNEL_NAME "uImage"
+# define CONFIG_CMD_SPL
+# define CONFIG_SYS_SPL_ARGS_ADDR 0x18000000
+# define CONFIG_CMD_SPL_WRITE_SIZE (128 * SZ_1K)
+
+/* MMC support: args@1MB kernel@2MB */
+# define CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTOR 0x800 /* 1MB */
+# define CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTORS (CONFIG_CMD_SPL_WRITE_SIZE / 512)
+# define CONFIG_SYS_MMCSD_RAW_MODE_KERNEL_SECTOR 0x1000 /* 2MB */
+#endif
+
/* Framebuffer */
#ifdef CONFIG_VIDEO_IPUV3
# define CONFIG_IPUV3_CLK 260000000
diff --git a/tools/imximage.h b/include/imximage.h
index de1ea8f..de1ea8f 100644
--- a/tools/imximage.h
+++ b/include/imximage.h
diff --git a/include/sdp.h b/include/sdp.h
new file mode 100644
index 0000000..f476bab
--- /dev/null
+++ b/include/sdp.h
@@ -0,0 +1,16 @@
+/*
+ * sdp.h - Serial Download Protocol
+ *
+ * Copyright (C) 2017 Toradex
+ * Author: Stefan Agner <stefan.agner@toradex.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __SDP_H_
+#define __SDP_H_
+
+int sdp_init(int controller_index);
+void sdp_handle(int controller_index);
+
+#endif /* __SDP_H_ */