mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [RFC PATCH] ARM: phytec-som-imx6: phycard DRAM autodetection
@ 2021-01-20 12:52 Andrej Picej
  2021-01-21 10:04 ` Sascha Hauer
  2021-02-05 11:09 ` Stefan Riedmüller
  0 siblings, 2 replies; 3+ messages in thread
From: Andrej Picej @ 2021-01-20 12:52 UTC (permalink / raw)
  To: barebox

Add support for automatic DRAM size detection on phytec phycard imx6q
machines. Currently supported SoM variants are 1 GiB (1 bank) and 2 GiB
variant. Machines now boot from internal SRAM (see DCD table loadaddr
0x00907000) and then do DRAM size detection and configuration in plain
C code (instead of hardcoded DCD table values). After initializing DRAM,
remaining barebox image is xloaded from GPMI NAND to DRAM and started
from there.

Booting process / strategy on phycard machines:
 1) lowlevel in SRAM (PBL)
  1.1) detect DRAM size
  1.2) configure DRAM accordingly
  1.3) copy remaining barebox image from NAND to DRAM (addr 0x10000000)
  1.4) jump to DRAM (addr 0x10000000)
 2) lowlevel in DRAM (PBL)
  2.1) skip DRAM detection since already running from DRAM
  2.2) main barebox entry
 3) board code
 4) ...

Signed-off-by: Primoz Fiser <primoz.fiser@norik.com>
Signed-off-by: Andrej Picej <andrej.picej@norik.com>
---
 .../flash-header-phytec-pcaaxl3.imxcfg        |   4 +
 arch/arm/boards/phytec-som-imx6/lowlevel.c    | 255 ++++++++++++++++++
 images/Makefile.imx                           |   2 +
 3 files changed, 261 insertions(+)
 create mode 100644 arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg

diff --git a/arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg b/arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg
new file mode 100644
index 000000000..fcfef9c23
--- /dev/null
+++ b/arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg
@@ -0,0 +1,4 @@
+soc imx6
+loadaddr 0x00907000
+max_load_size 0x31000
+ivtofs 0x400
diff --git a/arch/arm/boards/phytec-som-imx6/lowlevel.c b/arch/arm/boards/phytec-som-imx6/lowlevel.c
index 62a1c8de7..7bc9d123a 100644
--- a/arch/arm/boards/phytec-som-imx6/lowlevel.c
+++ b/arch/arm/boards/phytec-som-imx6/lowlevel.c
@@ -16,6 +16,261 @@
 #include <asm/cache.h>
 #include <asm/mmu.h>
 #include <mach/imx6.h>
+#include <mach/imx6-mmdc.h>
+#include <mach/esdctl.h>
+#include <mach/xload.h>
+
+/* udelay() is not available in PBL, need to improvise */
+static void __udelay(int us)
+{
+	volatile int i;
+
+	for (i = 0; i < us * 4; i++);
+}
+
+#define	IMX6Q_UART2			2
+#define	IMX6Q_UART3			3
+#define	DRIVE_STRENGTH_40_OHM		0x30
+#define	DRIVE_STRENGTH_48_OHM		0x28
+
+/* mmdc DDR io registers */
+static struct mx6dq_iomux_ddr_regs mx6dq_ddr_ioregs = {
+	.dram_sdclk_0 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdclk_1 = DRIVE_STRENGTH_40_OHM,
+	.dram_cas = DRIVE_STRENGTH_40_OHM,
+	.dram_ras = DRIVE_STRENGTH_40_OHM,
+	.dram_reset = DRIVE_STRENGTH_40_OHM,
+	.dram_sdcke0 = 0x00003000,
+	.dram_sdcke1 = 0x00003000,
+	.dram_sdba2 = 0x00000000,
+	.dram_sdodt0 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdodt1 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdqs0 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdqs1 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdqs2 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdqs3 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdqs4 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdqs5 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdqs6 = DRIVE_STRENGTH_40_OHM,
+	.dram_sdqs7 = DRIVE_STRENGTH_40_OHM,
+	.dram_dqm0 = DRIVE_STRENGTH_40_OHM,
+	.dram_dqm1 = DRIVE_STRENGTH_40_OHM,
+	.dram_dqm2 = DRIVE_STRENGTH_40_OHM,
+	.dram_dqm3 = DRIVE_STRENGTH_40_OHM,
+	.dram_dqm4 = DRIVE_STRENGTH_40_OHM,
+	.dram_dqm5 = DRIVE_STRENGTH_40_OHM,
+	.dram_dqm6 = DRIVE_STRENGTH_40_OHM,
+	.dram_dqm7 = DRIVE_STRENGTH_40_OHM,
+};
+
+static struct mx6_mmdc_calibration mx6q_mmdc_calib = {
+	.p0_mpwldectrl0 = 0x0013001b,
+	.p0_mpwldectrl1 = 0x003b0034,
+	.p1_mpwldectrl0 = 0x0037004b,
+	.p1_mpwldectrl1 = 0x004b0055,
+	.p0_mpdgctrl0 = 0x4350035e,
+	.p0_mpdgctrl1 = 0x035c0358,
+	.p1_mpdgctrl0 = 0x436e0376,
+	.p1_mpdgctrl1 = 0x03770352,
+	.p0_mprddlctl = 0x3c333436,
+	.p1_mprddlctl = 0x35332f3b,
+	.p0_mpwrdlctl = 0x37363e39,
+	.p1_mpwrdlctl = 0x432f433d,
+};
+
+/* MT41K128M16JT-125 IT */
+static struct mx6_ddr3_cfg mt41k128m16jt = {
+	.mem_speed = 1600,
+	.density = 2,
+	.width = 16,
+	.banks = 8,
+	.rowaddr = 14,
+	.coladdr = 10,
+	.pagesz = 2,
+	.trcd = 1375,
+	.trcmin = 4875,
+	.trasmin = 3500,
+};
+
+/* DDR 64bit 1GB */
+static struct mx6_ddr_sysinfo mem1g_q = {
+	.dsize		= 2,
+	.cs1_mirror	= 1,
+	.cs_density	= 8,
+	.ncs		= 1,
+	.bi_on		= 1,
+	.rtt_nom	= 1,
+	.rtt_wr		= 0,
+	.ralat		= 5,
+	.walat		= 1,
+	.mif3_mode	= 3,
+	.rst_to_cke	= 0x23,
+	.sde_to_rst	= 0x10,
+};
+
+/* DDR 64bit 2GB */
+static struct mx6_ddr_sysinfo mem2g_q = {
+	.dsize		= 2,
+	.cs1_mirror	= 1,
+	.cs_density	= 8,
+	.ncs		= 2,
+	.bi_on		= 1,
+	.rtt_nom	= 1,
+	.rtt_wr		= 0,
+	.ralat		= 5,
+	.walat		= 1,
+	.mif3_mode	= 3,
+	.rst_to_cke	= 0x23,
+	.sde_to_rst	= 0x10,
+};
+
+/* mmdc GRP io registers */
+static struct mx6dq_iomux_grp_regs mx6dq_grp_ioregs = {
+	.grp_ddr_type = 0x000c0000,
+	.grp_ddrmode_ctl = 0x00020000,
+	.grp_ddrpke = 0x00000000,
+	.grp_addds = DRIVE_STRENGTH_40_OHM,
+	.grp_ctlds = DRIVE_STRENGTH_40_OHM,
+	.grp_ddrmode = 0x00020000,
+	.grp_b0ds = DRIVE_STRENGTH_40_OHM,
+	.grp_b1ds = DRIVE_STRENGTH_40_OHM,
+	.grp_b2ds = DRIVE_STRENGTH_40_OHM,
+	.grp_b3ds = DRIVE_STRENGTH_40_OHM,
+	.grp_b4ds = DRIVE_STRENGTH_40_OHM,
+	.grp_b5ds = DRIVE_STRENGTH_40_OHM,
+	.grp_b6ds = DRIVE_STRENGTH_40_OHM,
+	.grp_b7ds = DRIVE_STRENGTH_40_OHM,
+};
+
+static inline void setup_uart_phycard_imx6q(int UARTn)
+{
+	void __iomem *iomuxbase = (void *)MX6_IOMUXC_BASE_ADDR;
+
+	switch (UARTn) {
+	case IMX6Q_UART2:
+		/* Setup UART2 pin muxing */
+		writel(0x4, iomuxbase + 0xc0);
+		writel(0x4, iomuxbase + 0xbc);
+		writel(0x1, iomuxbase + 0x928);
+
+		imx6_ungate_all_peripherals();
+		imx6_uart_setup((void *)MX6_UART2_BASE_ADDR);
+		pbl_set_putc(imx_uart_putc, (void *)MX6_UART2_BASE_ADDR);
+		break;
+	case IMX6Q_UART3:
+		/* Setup UART3 pin muxing */
+		writel(0x2, iomuxbase + 0xb4);
+		writel(0x2, iomuxbase + 0xb8);
+		writel(0x1, iomuxbase + 0x930);
+
+		imx6_ungate_all_peripherals();
+		imx6_uart_setup((void *)MX6_UART3_BASE_ADDR);
+		pbl_set_putc(imx_uart_putc, (void *)MX6_UART3_BASE_ADDR);
+		break;
+	default:
+		return;
+	}
+
+	putc_ll('>');
+}
+
+#define DRAM_CONFIG_DELAY	5000
+#define DRAM_CALIB_DELAY	50000
+
+static unsigned long phycard_imx6q_ll_autodetect_dram_init(void)
+{
+	unsigned long memsize = SZ_2G;
+	unsigned long dram_size = 0;
+
+	pr_debug("DRAM init & size detection\n");
+
+	/* First try with 2 GiB DRAM configuration */
+	mx6dq_dram_iocfg(64, &mx6dq_ddr_ioregs, &mx6dq_grp_ioregs);
+	mx6_dram_cfg(&mem2g_q, &mx6q_mmdc_calib, &mt41k128m16jt);
+	__udelay(DRAM_CONFIG_DELAY);
+
+	mmdc_do_write_level_calibration();
+	mmdc_do_dqs_calibration();
+	__udelay(DRAM_CALIB_DELAY);
+
+	pr_debug("DRAM probe with 2 GiB config\n");
+
+	dram_size = get_ram_size((volatile long *)0x10000000, (long)memsize);
+	pr_debug("DRAM detected size: 0x%lx\n", dram_size);
+
+	if (dram_size != SZ_2G) {
+		/* Next try with 1 GiB DRAM configuration */
+		memsize = SZ_1G;
+
+		mx6dq_dram_iocfg(64, &mx6dq_ddr_ioregs, &mx6dq_grp_ioregs);
+		mx6_dram_cfg(&mem1g_q, &mx6q_mmdc_calib, &mt41k128m16jt);
+		__udelay(DRAM_CONFIG_DELAY);
+
+		mmdc_do_write_level_calibration();
+		mmdc_do_dqs_calibration();
+		__udelay(DRAM_CALIB_DELAY);
+
+		pr_debug("DRAM probe with 1 GiB config\n");
+	} else {
+		pr_debug("DRAM size 0x%lx already detected, " \
+			"skipping DRAM re-configuration\n", dram_size);
+	}
+
+	dram_size = get_ram_size((volatile long *)0x10000000, (long)memsize);
+	pr_debug("DRAM final detected size: 0x%lx\n", dram_size);
+
+#ifdef DEBUG
+	pr_debug("DRAM calibration results:\n");
+	mmdc_print_calibration_results();
+#endif
+	return memsize;
+}
+
+static void phycard_imx6q_ll_autodetect_init(void)
+{
+	if (IS_ENABLED(CONFIG_DEBUG_LL))
+		setup_uart_phycard_imx6q(IMX6Q_UART3);
+
+	/* Already running from DRAM? */
+	if (get_pc() > 0x10000000) {
+		pr_debug("DRAM already initialized, skipping...\n");
+		return;
+	}
+
+	/* Running from SRAM, need to configure DRAM */
+	phycard_imx6q_ll_autodetect_dram_init();
+
+	/* Now xload remaining part of barebox image from NAND */
+	pr_debug("xloading barebox image from NAND\n");
+	imx6_nand_start_image();
+
+	pr_err("Booting failed!\n");
+	hang();
+}
+
+extern char __dtb_imx6q_phytec_phycard_start[];
+
+static noinline void phycard_imx6q_ll_autodetect_start(void)
+{
+	void *fdt = __dtb_imx6q_phytec_phycard_start + get_runtime_offset();
+
+	phycard_imx6q_ll_autodetect_init();
+
+	imx6q_barebox_entry(fdt);
+}
+
+ENTRY_FUNCTION(start_phycard_imx6q_ll_autodetect, r0, r1, r2)
+{
+	imx6_cpu_lowlevel_init();
+
+	arm_setup_stack(0x00940000);
+
+	relocate_to_current_adr();
+	setup_c();
+	barrier();
+
+	phycard_imx6q_ll_autodetect_start();
+}
 
 static inline void setup_uart(void)
 {
diff --git a/images/Makefile.imx b/images/Makefile.imx
index 514db326b..80d2fd805 100644
--- a/images/Makefile.imx
+++ b/images/Makefile.imx
@@ -205,6 +205,8 @@ $(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6, start_phytec_phyboard_su
 
 $(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6, start_phytec_phyboard_subra_1gib_1bank, phytec-som-imx6/flash-header-phytec-pfla02-1gib-1bank, phytec-phyboard-subra-1gib-1bank)
 
+$(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6, start_phycard_imx6q_ll_autodetect, phytec-som-imx6/flash-header-phytec-pcaaxl3, phytec-phycard-imx6q-som-nand)
+
 $(call build_imx_habv4img, CONFIG_MACH_DFI_FS700_M60, start_imx6dl_dfi_fs700_m60_6s, dfi-fs700-m60/flash-header-fs700-m60-6s, dfi-fs700-m60-6s)
 
 $(call build_imx_habv4img, CONFIG_MACH_DFI_FS700_M60, start_imx6q_dfi_fs700_m60_6q_micron, dfi-fs700-m60/flash-header-fs700-m60-6q-micron, dfi-fs700-m60-6q-micron)
-- 
2.25.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC PATCH] ARM: phytec-som-imx6: phycard DRAM autodetection
  2021-01-20 12:52 [RFC PATCH] ARM: phytec-som-imx6: phycard DRAM autodetection Andrej Picej
@ 2021-01-21 10:04 ` Sascha Hauer
  2021-02-05 11:09 ` Stefan Riedmüller
  1 sibling, 0 replies; 3+ messages in thread
From: Sascha Hauer @ 2021-01-21 10:04 UTC (permalink / raw)
  To: Andrej Picej; +Cc: Yunus Bas, barebox, Stefan Christ, Stefan Riedmueller

+Cc some Phytec guys

On Wed, Jan 20, 2021 at 01:52:03PM +0100, Andrej Picej wrote:
> Add support for automatic DRAM size detection on phytec phycard imx6q
> machines. Currently supported SoM variants are 1 GiB (1 bank) and 2 GiB
> variant. Machines now boot from internal SRAM (see DCD table loadaddr
> 0x00907000) and then do DRAM size detection and configuration in plain
> C code (instead of hardcoded DCD table values). After initializing DRAM,
> remaining barebox image is xloaded from GPMI NAND to DRAM and started
> from there.
> 
> Booting process / strategy on phycard machines:
>  1) lowlevel in SRAM (PBL)
>   1.1) detect DRAM size
>   1.2) configure DRAM accordingly
>   1.3) copy remaining barebox image from NAND to DRAM (addr 0x10000000)
>   1.4) jump to DRAM (addr 0x10000000)
>  2) lowlevel in DRAM (PBL)
>   2.1) skip DRAM detection since already running from DRAM
>   2.2) main barebox entry
>  3) board code
>  4) ...
> 
> Signed-off-by: Primoz Fiser <primoz.fiser@norik.com>
> Signed-off-by: Andrej Picej <andrej.picej@norik.com>
> ---
>  .../flash-header-phytec-pcaaxl3.imxcfg        |   4 +
>  arch/arm/boards/phytec-som-imx6/lowlevel.c    | 255 ++++++++++++++++++
>  images/Makefile.imx                           |   2 +
>  3 files changed, 261 insertions(+)
>  create mode 100644 arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg
> 
> diff --git a/arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg b/arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg
> new file mode 100644
> index 000000000..fcfef9c23
> --- /dev/null
> +++ b/arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg
> @@ -0,0 +1,4 @@
> +soc imx6
> +loadaddr 0x00907000
> +max_load_size 0x31000
> +ivtofs 0x400
> diff --git a/arch/arm/boards/phytec-som-imx6/lowlevel.c b/arch/arm/boards/phytec-som-imx6/lowlevel.c
> index 62a1c8de7..7bc9d123a 100644
> --- a/arch/arm/boards/phytec-som-imx6/lowlevel.c
> +++ b/arch/arm/boards/phytec-som-imx6/lowlevel.c
> @@ -16,6 +16,261 @@
>  #include <asm/cache.h>
>  #include <asm/mmu.h>
>  #include <mach/imx6.h>
> +#include <mach/imx6-mmdc.h>
> +#include <mach/esdctl.h>
> +#include <mach/xload.h>
> +
> +/* udelay() is not available in PBL, need to improvise */
> +static void __udelay(int us)
> +{
> +	volatile int i;
> +
> +	for (i = 0; i < us * 4; i++);
> +}
> +
> +#define	IMX6Q_UART2			2
> +#define	IMX6Q_UART3			3
> +#define	DRIVE_STRENGTH_40_OHM		0x30
> +#define	DRIVE_STRENGTH_48_OHM		0x28
> +
> +/* mmdc DDR io registers */
> +static struct mx6dq_iomux_ddr_regs mx6dq_ddr_ioregs = {
> +	.dram_sdclk_0 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdclk_1 = DRIVE_STRENGTH_40_OHM,
> +	.dram_cas = DRIVE_STRENGTH_40_OHM,
> +	.dram_ras = DRIVE_STRENGTH_40_OHM,
> +	.dram_reset = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdcke0 = 0x00003000,
> +	.dram_sdcke1 = 0x00003000,
> +	.dram_sdba2 = 0x00000000,
> +	.dram_sdodt0 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdodt1 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs0 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs1 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs2 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs3 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs4 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs5 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs6 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs7 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm0 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm1 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm2 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm3 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm4 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm5 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm6 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm7 = DRIVE_STRENGTH_40_OHM,
> +};
> +
> +static struct mx6_mmdc_calibration mx6q_mmdc_calib = {
> +	.p0_mpwldectrl0 = 0x0013001b,
> +	.p0_mpwldectrl1 = 0x003b0034,
> +	.p1_mpwldectrl0 = 0x0037004b,
> +	.p1_mpwldectrl1 = 0x004b0055,
> +	.p0_mpdgctrl0 = 0x4350035e,
> +	.p0_mpdgctrl1 = 0x035c0358,
> +	.p1_mpdgctrl0 = 0x436e0376,
> +	.p1_mpdgctrl1 = 0x03770352,
> +	.p0_mprddlctl = 0x3c333436,
> +	.p1_mprddlctl = 0x35332f3b,
> +	.p0_mpwrdlctl = 0x37363e39,
> +	.p1_mpwrdlctl = 0x432f433d,
> +};
> +
> +/* MT41K128M16JT-125 IT */
> +static struct mx6_ddr3_cfg mt41k128m16jt = {
> +	.mem_speed = 1600,
> +	.density = 2,
> +	.width = 16,
> +	.banks = 8,
> +	.rowaddr = 14,
> +	.coladdr = 10,
> +	.pagesz = 2,
> +	.trcd = 1375,
> +	.trcmin = 4875,
> +	.trasmin = 3500,
> +};
> +
> +/* DDR 64bit 1GB */
> +static struct mx6_ddr_sysinfo mem1g_q = {
> +	.dsize		= 2,
> +	.cs1_mirror	= 1,
> +	.cs_density	= 8,
> +	.ncs		= 1,
> +	.bi_on		= 1,
> +	.rtt_nom	= 1,
> +	.rtt_wr		= 0,
> +	.ralat		= 5,
> +	.walat		= 1,
> +	.mif3_mode	= 3,
> +	.rst_to_cke	= 0x23,
> +	.sde_to_rst	= 0x10,
> +};
> +
> +/* DDR 64bit 2GB */
> +static struct mx6_ddr_sysinfo mem2g_q = {
> +	.dsize		= 2,
> +	.cs1_mirror	= 1,
> +	.cs_density	= 8,
> +	.ncs		= 2,
> +	.bi_on		= 1,
> +	.rtt_nom	= 1,
> +	.rtt_wr		= 0,
> +	.ralat		= 5,
> +	.walat		= 1,
> +	.mif3_mode	= 3,
> +	.rst_to_cke	= 0x23,
> +	.sde_to_rst	= 0x10,
> +};
> +
> +/* mmdc GRP io registers */
> +static struct mx6dq_iomux_grp_regs mx6dq_grp_ioregs = {
> +	.grp_ddr_type = 0x000c0000,
> +	.grp_ddrmode_ctl = 0x00020000,
> +	.grp_ddrpke = 0x00000000,
> +	.grp_addds = DRIVE_STRENGTH_40_OHM,
> +	.grp_ctlds = DRIVE_STRENGTH_40_OHM,
> +	.grp_ddrmode = 0x00020000,
> +	.grp_b0ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b1ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b2ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b3ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b4ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b5ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b6ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b7ds = DRIVE_STRENGTH_40_OHM,
> +};
> +
> +static inline void setup_uart_phycard_imx6q(int UARTn)
> +{
> +	void __iomem *iomuxbase = (void *)MX6_IOMUXC_BASE_ADDR;
> +
> +	switch (UARTn) {
> +	case IMX6Q_UART2:
> +		/* Setup UART2 pin muxing */
> +		writel(0x4, iomuxbase + 0xc0);
> +		writel(0x4, iomuxbase + 0xbc);
> +		writel(0x1, iomuxbase + 0x928);
> +
> +		imx6_ungate_all_peripherals();
> +		imx6_uart_setup((void *)MX6_UART2_BASE_ADDR);
> +		pbl_set_putc(imx_uart_putc, (void *)MX6_UART2_BASE_ADDR);
> +		break;
> +	case IMX6Q_UART3:
> +		/* Setup UART3 pin muxing */
> +		writel(0x2, iomuxbase + 0xb4);
> +		writel(0x2, iomuxbase + 0xb8);
> +		writel(0x1, iomuxbase + 0x930);
> +
> +		imx6_ungate_all_peripherals();
> +		imx6_uart_setup((void *)MX6_UART3_BASE_ADDR);
> +		pbl_set_putc(imx_uart_putc, (void *)MX6_UART3_BASE_ADDR);
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	putc_ll('>');
> +}
> +
> +#define DRAM_CONFIG_DELAY	5000
> +#define DRAM_CALIB_DELAY	50000
> +
> +static unsigned long phycard_imx6q_ll_autodetect_dram_init(void)
> +{
> +	unsigned long memsize = SZ_2G;
> +	unsigned long dram_size = 0;
> +
> +	pr_debug("DRAM init & size detection\n");
> +
> +	/* First try with 2 GiB DRAM configuration */
> +	mx6dq_dram_iocfg(64, &mx6dq_ddr_ioregs, &mx6dq_grp_ioregs);
> +	mx6_dram_cfg(&mem2g_q, &mx6q_mmdc_calib, &mt41k128m16jt);
> +	__udelay(DRAM_CONFIG_DELAY);
> +
> +	mmdc_do_write_level_calibration();
> +	mmdc_do_dqs_calibration();
> +	__udelay(DRAM_CALIB_DELAY);
> +
> +	pr_debug("DRAM probe with 2 GiB config\n");
> +
> +	dram_size = get_ram_size((volatile long *)0x10000000, (long)memsize);
> +	pr_debug("DRAM detected size: 0x%lx\n", dram_size);
> +
> +	if (dram_size != SZ_2G) {
> +		/* Next try with 1 GiB DRAM configuration */
> +		memsize = SZ_1G;
> +
> +		mx6dq_dram_iocfg(64, &mx6dq_ddr_ioregs, &mx6dq_grp_ioregs);
> +		mx6_dram_cfg(&mem1g_q, &mx6q_mmdc_calib, &mt41k128m16jt);
> +		__udelay(DRAM_CONFIG_DELAY);
> +
> +		mmdc_do_write_level_calibration();
> +		mmdc_do_dqs_calibration();
> +		__udelay(DRAM_CALIB_DELAY);
> +
> +		pr_debug("DRAM probe with 1 GiB config\n");
> +	} else {
> +		pr_debug("DRAM size 0x%lx already detected, " \
> +			"skipping DRAM re-configuration\n", dram_size);
> +	}
> +
> +	dram_size = get_ram_size((volatile long *)0x10000000, (long)memsize);
> +	pr_debug("DRAM final detected size: 0x%lx\n", dram_size);
> +
> +#ifdef DEBUG
> +	pr_debug("DRAM calibration results:\n");
> +	mmdc_print_calibration_results();
> +#endif
> +	return memsize;
> +}
> +
> +static void phycard_imx6q_ll_autodetect_init(void)
> +{
> +	if (IS_ENABLED(CONFIG_DEBUG_LL))
> +		setup_uart_phycard_imx6q(IMX6Q_UART3);
> +
> +	/* Already running from DRAM? */
> +	if (get_pc() > 0x10000000) {
> +		pr_debug("DRAM already initialized, skipping...\n");
> +		return;
> +	}
> +
> +	/* Running from SRAM, need to configure DRAM */
> +	phycard_imx6q_ll_autodetect_dram_init();
> +
> +	/* Now xload remaining part of barebox image from NAND */
> +	pr_debug("xloading barebox image from NAND\n");
> +	imx6_nand_start_image();

You should detect where we are booting from and load the rest from the
same device. Speaking of which, we should finally add some
imx6_xload_start_image() function which does that generically for i.MX6.

> +
> +	pr_err("Booting failed!\n");
> +	hang();
> +}
> +
> +extern char __dtb_imx6q_phytec_phycard_start[];
> +
> +static noinline void phycard_imx6q_ll_autodetect_start(void)
> +{
> +	void *fdt = __dtb_imx6q_phytec_phycard_start + get_runtime_offset();
> +
> +	phycard_imx6q_ll_autodetect_init();
> +
> +	imx6q_barebox_entry(fdt);
> +}
> +
> +ENTRY_FUNCTION(start_phycard_imx6q_ll_autodetect, r0, r1, r2)
> +{
> +	imx6_cpu_lowlevel_init();
> +
> +	arm_setup_stack(0x00940000);
> +
> +	relocate_to_current_adr();
> +	setup_c();
> +	barrier();
> +
> +	phycard_imx6q_ll_autodetect_start();
> +}
>  
>  static inline void setup_uart(void)
>  {
> diff --git a/images/Makefile.imx b/images/Makefile.imx
> index 514db326b..80d2fd805 100644
> --- a/images/Makefile.imx
> +++ b/images/Makefile.imx
> @@ -205,6 +205,8 @@ $(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6, start_phytec_phyboard_su
>  
>  $(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6, start_phytec_phyboard_subra_1gib_1bank, phytec-som-imx6/flash-header-phytec-pfla02-1gib-1bank, phytec-phyboard-subra-1gib-1bank)
>  
> +$(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6, start_phycard_imx6q_ll_autodetect, phytec-som-imx6/flash-header-phytec-pcaaxl3, phytec-phycard-imx6q-som-nand)
> +
>  $(call build_imx_habv4img, CONFIG_MACH_DFI_FS700_M60, start_imx6dl_dfi_fs700_m60_6s, dfi-fs700-m60/flash-header-fs700-m60-6s, dfi-fs700-m60-6s)
>  
>  $(call build_imx_habv4img, CONFIG_MACH_DFI_FS700_M60, start_imx6q_dfi_fs700_m60_6q_micron, dfi-fs700-m60/flash-header-fs700-m60-6q-micron, dfi-fs700-m60-6q-micron)
> -- 
> 2.25.1
> 
> 
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC PATCH] ARM: phytec-som-imx6: phycard DRAM autodetection
  2021-01-20 12:52 [RFC PATCH] ARM: phytec-som-imx6: phycard DRAM autodetection Andrej Picej
  2021-01-21 10:04 ` Sascha Hauer
@ 2021-02-05 11:09 ` Stefan Riedmüller
  1 sibling, 0 replies; 3+ messages in thread
From: Stefan Riedmüller @ 2021-02-05 11:09 UTC (permalink / raw)
  To: andrej.picej; +Cc: barebox

Hi Andrej,

I like this idea. It is nice to have a working example of DRAM size detection
available aside from the DCD table setups.

Please see my notes below.

On Wed, 2021-01-20 at 13:52 +0100, Andrej Picej wrote:
> Add support for automatic DRAM size detection on phytec phycard imx6q
> machines. Currently supported SoM variants are 1 GiB (1 bank) and 2 GiB
> variant. Machines now boot from internal SRAM (see DCD table loadaddr
> 0x00907000) and then do DRAM size detection and configuration in plain
> C code (instead of hardcoded DCD table values). After initializing DRAM,
> remaining barebox image is xloaded from GPMI NAND to DRAM and started
> from there.
> 
> Booting process / strategy on phycard machines:
>  1) lowlevel in SRAM (PBL)
>   1.1) detect DRAM size
>   1.2) configure DRAM accordingly
>   1.3) copy remaining barebox image from NAND to DRAM (addr 0x10000000)
>   1.4) jump to DRAM (addr 0x10000000)
>  2) lowlevel in DRAM (PBL)
>   2.1) skip DRAM detection since already running from DRAM
>   2.2) main barebox entry
>  3) board code
>  4) ...
> 
> Signed-off-by: Primoz Fiser <primoz.fiser@norik.com>
> Signed-off-by: Andrej Picej <andrej.picej@norik.com>
> ---
>  .../flash-header-phytec-pcaaxl3.imxcfg        |   4 +
>  arch/arm/boards/phytec-som-imx6/lowlevel.c    | 255 ++++++++++++++++++
>  images/Makefile.imx                           |   2 +
>  3 files changed, 261 insertions(+)
>  create mode 100644 arch/arm/boards/phytec-som-imx6/flash-header-phytec-
> pcaaxl3.imxcfg
> 
> diff --git a/arch/arm/boards/phytec-som-imx6/flash-header-phytec-
> pcaaxl3.imxcfg b/arch/arm/boards/phytec-som-imx6/flash-header-phytec-
> pcaaxl3.imxcfg
> new file mode 100644
> index 000000000..fcfef9c23
> --- /dev/null
> +++ b/arch/arm/boards/phytec-som-imx6/flash-header-phytec-pcaaxl3.imxcfg
> @@ -0,0 +1,4 @@
> +soc imx6
> +loadaddr 0x00907000
> +max_load_size 0x31000
> +ivtofs 0x400
> diff --git a/arch/arm/boards/phytec-som-imx6/lowlevel.c
> b/arch/arm/boards/phytec-som-imx6/lowlevel.c
> index 62a1c8de7..7bc9d123a 100644
> --- a/arch/arm/boards/phytec-som-imx6/lowlevel.c
> +++ b/arch/arm/boards/phytec-som-imx6/lowlevel.c
> @@ -16,6 +16,261 @@
>  #include <asm/cache.h>
>  #include <asm/mmu.h>
>  #include <mach/imx6.h>
> +#include <mach/imx6-mmdc.h>
> +#include <mach/esdctl.h>
> +#include <mach/xload.h>
> +
> +/* udelay() is not available in PBL, need to improvise */
> +static void __udelay(int us)
> +{
> +	volatile int i;
> +
> +	for (i = 0; i < us * 4; i++);
> +}
> +
> +#define	IMX6Q_UART2			2
> +#define	IMX6Q_UART3			3
> +#define	DRIVE_STRENGTH_40_OHM		0x30
> +#define	DRIVE_STRENGTH_48_OHM		0x28
> +
> +/* mmdc DDR io registers */
> +static struct mx6dq_iomux_ddr_regs mx6dq_ddr_ioregs = {
> +	.dram_sdclk_0 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdclk_1 = DRIVE_STRENGTH_40_OHM,
> +	.dram_cas = DRIVE_STRENGTH_40_OHM,
> +	.dram_ras = DRIVE_STRENGTH_40_OHM,
> +	.dram_reset = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdcke0 = 0x00003000,
> +	.dram_sdcke1 = 0x00003000,
> +	.dram_sdba2 = 0x00000000,
> +	.dram_sdodt0 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdodt1 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs0 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs1 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs2 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs3 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs4 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs5 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs6 = DRIVE_STRENGTH_40_OHM,
> +	.dram_sdqs7 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm0 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm1 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm2 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm3 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm4 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm5 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm6 = DRIVE_STRENGTH_40_OHM,
> +	.dram_dqm7 = DRIVE_STRENGTH_40_OHM,
> +};
> +
> +static struct mx6_mmdc_calibration mx6q_mmdc_calib = {
> +	.p0_mpwldectrl0 = 0x0013001b,
> +	.p0_mpwldectrl1 = 0x003b0034,
> +	.p1_mpwldectrl0 = 0x0037004b,
> +	.p1_mpwldectrl1 = 0x004b0055,
> +	.p0_mpdgctrl0 = 0x4350035e,
> +	.p0_mpdgctrl1 = 0x035c0358,
> +	.p1_mpdgctrl0 = 0x436e0376,
> +	.p1_mpdgctrl1 = 0x03770352,
> +	.p0_mprddlctl = 0x3c333436,
> +	.p1_mprddlctl = 0x35332f3b,
> +	.p0_mpwrdlctl = 0x37363e39,
> +	.p1_mpwrdlctl = 0x432f433d,
> +};
> +
> +/* MT41K128M16JT-125 IT */
> +static struct mx6_ddr3_cfg mt41k128m16jt = {
> +	.mem_speed = 1600,
> +	.density = 2,
> +	.width = 16,
> +	.banks = 8,
> +	.rowaddr = 14,
> +	.coladdr = 10,
> +	.pagesz = 2,
> +	.trcd = 1375,
> +	.trcmin = 4875,
> +	.trasmin = 3500,
> +};
> +
> +/* DDR 64bit 1GB */
> +static struct mx6_ddr_sysinfo mem1g_q = {
> +	.dsize		= 2,
> +	.cs1_mirror	= 1,
> +	.cs_density	= 8,
> +	.ncs		= 1,
> +	.bi_on		= 1,
> +	.rtt_nom	= 1,
> +	.rtt_wr		= 0,
> +	.ralat		= 5,
> +	.walat		= 1,
> +	.mif3_mode	= 3,
> +	.rst_to_cke	= 0x23,
> +	.sde_to_rst	= 0x10,
> +};
> +
> +/* DDR 64bit 2GB */
> +static struct mx6_ddr_sysinfo mem2g_q = {
> +	.dsize		= 2,
> +	.cs1_mirror	= 1,
> +	.cs_density	= 8,
> +	.ncs		= 2,
> +	.bi_on		= 1,
> +	.rtt_nom	= 1,
> +	.rtt_wr		= 0,
> +	.ralat		= 5,
> +	.walat		= 1,
> +	.mif3_mode	= 3,
> +	.rst_to_cke	= 0x23,
> +	.sde_to_rst	= 0x10,
> +};
> +
> +/* mmdc GRP io registers */
> +static struct mx6dq_iomux_grp_regs mx6dq_grp_ioregs = {
> +	.grp_ddr_type = 0x000c0000,
> +	.grp_ddrmode_ctl = 0x00020000,
> +	.grp_ddrpke = 0x00000000,
> +	.grp_addds = DRIVE_STRENGTH_40_OHM,
> +	.grp_ctlds = DRIVE_STRENGTH_40_OHM,
> +	.grp_ddrmode = 0x00020000,
> +	.grp_b0ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b1ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b2ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b3ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b4ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b5ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b6ds = DRIVE_STRENGTH_40_OHM,
> +	.grp_b7ds = DRIVE_STRENGTH_40_OHM,
> +};

Wouldn't it make sense to put these structs into a separate header file. If
there will be more variants in the future, e.g. phyCORE or phyFLEX it would
fast get confusing.

> +
> +static inline void setup_uart_phycard_imx6q(int UARTn)
> +{
> +	void __iomem *iomuxbase = (void *)MX6_IOMUXC_BASE_ADDR;
> +
> +	switch (UARTn) {
> +	case IMX6Q_UART2:
> +		/* Setup UART2 pin muxing */
> +		writel(0x4, iomuxbase + 0xc0);
> +		writel(0x4, iomuxbase + 0xbc);
> +		writel(0x1, iomuxbase + 0x928);
> +
> +		imx6_ungate_all_peripherals();
> +		imx6_uart_setup((void *)MX6_UART2_BASE_ADDR);
> +		pbl_set_putc(imx_uart_putc, (void *)MX6_UART2_BASE_ADDR);
> +		break;
> +	case IMX6Q_UART3:
> +		/* Setup UART3 pin muxing */
> +		writel(0x2, iomuxbase + 0xb4);
> +		writel(0x2, iomuxbase + 0xb8);
> +		writel(0x1, iomuxbase + 0x930);
> +
> +		imx6_ungate_all_peripherals();
> +		imx6_uart_setup((void *)MX6_UART3_BASE_ADDR);
> +		pbl_set_putc(imx_uart_putc, (void *)MX6_UART3_BASE_ADDR);
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	putc_ll('>');
> +}
> +
> +#define DRAM_CONFIG_DELAY	5000
> +#define DRAM_CALIB_DELAY	50000
> +
> +static unsigned long phycard_imx6q_ll_autodetect_dram_init(void)
> +{
> +	unsigned long memsize = SZ_2G;
> +	unsigned long dram_size = 0;
> +
> +	pr_debug("DRAM init & size detection\n");
> +
> +	/* First try with 2 GiB DRAM configuration */
> +	mx6dq_dram_iocfg(64, &mx6dq_ddr_ioregs, &mx6dq_grp_ioregs);
> +	mx6_dram_cfg(&mem2g_q, &mx6q_mmdc_calib, &mt41k128m16jt);
> +	__udelay(DRAM_CONFIG_DELAY);
> +
> +	mmdc_do_write_level_calibration();
> +	mmdc_do_dqs_calibration();
> +	__udelay(DRAM_CALIB_DELAY);
> +
> +	pr_debug("DRAM probe with 2 GiB config\n");
> +
> +	dram_size = get_ram_size((volatile long *)0x10000000, (long)memsize);
> +	pr_debug("DRAM detected size: 0x%lx\n", dram_size);
> +
> +	if (dram_size != SZ_2G) {
> +		/* Next try with 1 GiB DRAM configuration */
> +		memsize = SZ_1G;
> +
> +		mx6dq_dram_iocfg(64, &mx6dq_ddr_ioregs, &mx6dq_grp_ioregs);
> +		mx6_dram_cfg(&mem1g_q, &mx6q_mmdc_calib, &mt41k128m16jt);
> +		__udelay(DRAM_CONFIG_DELAY);
> +
> +		mmdc_do_write_level_calibration();
> +		mmdc_do_dqs_calibration();
> +		__udelay(DRAM_CALIB_DELAY);
> +
> +		pr_debug("DRAM probe with 1 GiB config\n");
> +	} else {
> +		pr_debug("DRAM size 0x%lx already detected, " \
> +			"skipping DRAM re-configuration\n", dram_size);
> +	}
> +
> +	dram_size = get_ram_size((volatile long *)0x10000000, (long)memsize);
> +	pr_debug("DRAM final detected size: 0x%lx\n", dram_size);
> +
> +#ifdef DEBUG
> +	pr_debug("DRAM calibration results:\n");
> +	mmdc_print_calibration_results();
> +#endif
> +	return memsize;
> +}

If get_ram_size returns the actual DRAM size in the specified region, wouldn't
it make sense to detect it with the maximum possible size (SZ_4G) and use the
actual size returned from get_ram_size to select the correct setting? This
would make it easier to add support for additional DRAM configurations.

Or am I missing something here?

> +
> +static void phycard_imx6q_ll_autodetect_init(void)
> +{
> +	if (IS_ENABLED(CONFIG_DEBUG_LL))
> +		setup_uart_phycard_imx6q(IMX6Q_UART3);
> +
> +	/* Already running from DRAM? */
> +	if (get_pc() > 0x10000000) {
> +		pr_debug("DRAM already initialized, skipping...\n");
> +		return;
> +	}
> +
> +	/* Running from SRAM, need to configure DRAM */
> +	phycard_imx6q_ll_autodetect_dram_init();
> +
> +	/* Now xload remaining part of barebox image from NAND */
> +	pr_debug("xloading barebox image from NAND\n");
> +	imx6_nand_start_image();
> +
> +	pr_err("Booting failed!\n");
> +	hang();
> +}
> +
> +extern char __dtb_imx6q_phytec_phycard_start[];
> +
> +static noinline void phycard_imx6q_ll_autodetect_start(void)
> +{
> +	void *fdt = __dtb_imx6q_phytec_phycard_start + get_runtime_offset();
> +
> +	phycard_imx6q_ll_autodetect_init();
> +
> +	imx6q_barebox_entry(fdt);
> +}
> +
> +ENTRY_FUNCTION(start_phycard_imx6q_ll_autodetect, r0, r1, r2)
> +{
> +	imx6_cpu_lowlevel_init();
> +
> +	arm_setup_stack(0x00940000);
> +
> +	relocate_to_current_adr();
> +	setup_c();
> +	barrier();
> +
> +	phycard_imx6q_ll_autodetect_start();
> +}

It would be nice to have this included in PHYTEC_ENTRY instead of creating a
new entry function.

Best regards,
Stefan

>  
>  static inline void setup_uart(void)
>  {
> diff --git a/images/Makefile.imx b/images/Makefile.imx
> index 514db326b..80d2fd805 100644
> --- a/images/Makefile.imx
> +++ b/images/Makefile.imx
> @@ -205,6 +205,8 @@ $(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6,
> start_phytec_phyboard_su
>  
>  $(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6,
> start_phytec_phyboard_subra_1gib_1bank, phytec-som-imx6/flash-header-phytec-
> pfla02-1gib-1bank, phytec-phyboard-subra-1gib-1bank)
>  
> +$(call build_imx_habv4img, CONFIG_MACH_PHYTEC_SOM_IMX6,
> start_phycard_imx6q_ll_autodetect, phytec-som-imx6/flash-header-phytec-
> pcaaxl3, phytec-phycard-imx6q-som-nand)
> +
>  $(call build_imx_habv4img, CONFIG_MACH_DFI_FS700_M60,
> start_imx6dl_dfi_fs700_m60_6s, dfi-fs700-m60/flash-header-fs700-m60-6s, dfi-
> fs700-m60-6s)
>  
>  $(call build_imx_habv4img, CONFIG_MACH_DFI_FS700_M60,
> start_imx6q_dfi_fs700_m60_6q_micron, dfi-fs700-m60/flash-header-fs700-m60-
> 6q-micron, dfi-fs700-m60-6q-micron)
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-02-05 11:09 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-20 12:52 [RFC PATCH] ARM: phytec-som-imx6: phycard DRAM autodetection Andrej Picej
2021-01-21 10:04 ` Sascha Hauer
2021-02-05 11:09 ` Stefan Riedmüller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox