[RFC PATCH] ARM: phytec-som-imx6: phycard DRAM autodetection

Stefan Riedmüller S.Riedmueller at phytec.de
Fri Feb 5 06:09:52 EST 2021


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 at norik.com>
> Signed-off-by: Andrej Picej <andrej.picej at 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)


More information about the barebox mailing list