diff --git a/arch/arm/cpu/armv7/mx6/Makefile b/arch/arm/cpu/armv7/mx6/Makefile
index d7285fc..6dc9f8e 100644
--- a/arch/arm/cpu/armv7/mx6/Makefile
+++ b/arch/arm/cpu/armv7/mx6/Makefile
@@ -8,4 +8,5 @@
 #
 
 obj-y	:= soc.o clock.o
+obj-$(CONFIG_SPL_BUILD)	     += ddr.o
 obj-$(CONFIG_SECURE_BOOT)    += hab.o
diff --git a/arch/arm/cpu/armv7/mx6/ddr.c b/arch/arm/cpu/armv7/mx6/ddr.c
new file mode 100644
index 0000000..a910a93
--- /dev/null
+++ b/arch/arm/cpu/armv7/mx6/ddr.c
@@ -0,0 +1,542 @@
+/*
+ * Author: Tim Harvey <tharvey@gateworks.com>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <asm/arch/mx6-ddr.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/io.h>
+#include <asm/types.h>
+
+#if defined(CONFIG_MX6QDL) || defined(CONFIG_MX6Q)
+struct mx6_mmdc_ioregs const mx6dq_ctrl = {
+	.mmdc_dram_dqm0 = MX6Q_IOM_DRAM_DQM0,
+	.mmdc_dram_dqm1 = MX6Q_IOM_DRAM_DQM1,
+	.mmdc_dram_dqm2 = MX6Q_IOM_DRAM_DQM2,
+	.mmdc_dram_dqm3 = MX6Q_IOM_DRAM_DQM3,
+	.mmdc_dram_dqm4 = MX6Q_IOM_DRAM_DQM4,
+	.mmdc_dram_dqm5 = MX6Q_IOM_DRAM_DQM5,
+	.mmdc_dram_dqm6 = MX6Q_IOM_DRAM_DQM6,
+	.mmdc_dram_dqm7 = MX6Q_IOM_DRAM_DQM7,
+	.mmdc_dram_cas = MX6Q_IOM_DRAM_CAS,
+	.mmdc_dram_ras = MX6Q_IOM_DRAM_RAS,
+	.mmdc_dram_reset = MX6Q_IOM_DRAM_RESET,
+	.mmdc_dram_sdclk_0 = MX6Q_IOM_DRAM_SDCLK_0,
+	.mmdc_dram_sdclk_1 = MX6Q_IOM_DRAM_SDCLK_1,
+	.mmdc_dram_sdba2 = MX6Q_IOM_DRAM_SDBA2,
+	.mmdc_dram_sdcke0 = MX6Q_IOM_DRAM_SDCKE0,
+	.mmdc_dram_sdcke1 = MX6Q_IOM_DRAM_SDCKE1,
+	.mmdc_dram_sdodt0 = MX6Q_IOM_DRAM_SDODT0,
+	.mmdc_dram_sdodt1 = MX6Q_IOM_DRAM_SDODT1,
+	.mmdc_dram_sdqs0 = MX6Q_IOM_DRAM_SDQS0,
+	.mmdc_dram_sdqs1 = MX6Q_IOM_DRAM_SDQS1,
+	.mmdc_dram_sdqs2 = MX6Q_IOM_DRAM_SDQS2,
+	.mmdc_dram_sdqs3 = MX6Q_IOM_DRAM_SDQS3,
+	.mmdc_dram_sdqs4 = MX6Q_IOM_DRAM_SDQS4,
+	.mmdc_dram_sdqs5 = MX6Q_IOM_DRAM_SDQS5,
+	.mmdc_dram_sdqs6 = MX6Q_IOM_DRAM_SDQS6,
+	.mmdc_dram_sdqs7 = MX6Q_IOM_DRAM_SDQS7,
+	.mmdc_grp_b0ds = MX6Q_IOM_GRP_B0DS,
+	.mmdc_grp_b1ds = MX6Q_IOM_GRP_B1DS,
+	.mmdc_grp_b2ds = MX6Q_IOM_GRP_B2DS,
+	.mmdc_grp_b3ds = MX6Q_IOM_GRP_B3DS,
+	.mmdc_grp_b4ds = MX6Q_IOM_GRP_B4DS,
+	.mmdc_grp_b5ds = MX6Q_IOM_GRP_B5DS,
+	.mmdc_grp_b6ds = MX6Q_IOM_GRP_B6DS,
+	.mmdc_grp_b7ds = MX6Q_IOM_GRP_B7DS,
+	.mmdc_grp_addds = MX6Q_IOM_GRP_ADDDS,
+	.mmdc_grp_ddrpke = MX6Q_IOM_GRP_DDRPKE,
+	.mmdc_grp_ctlds = MX6Q_IOM_GRP_CTLDS,
+	.mmdc_grp_ddr_type = MX6Q_IOM_GRP_DDR_TYPE,
+	.mmdc_ddrmode_ctl = MX6Q_IOM_DDRMODE_CTL,
+	.mmdc_grp_ddrmode = MX6Q_IOM_GRP_DDRMODE,
+};
+#endif
+
+#if defined(CONFIG_MX6QDL) || defined(CONFIG_MX6DL)
+struct mx6_mmdc_ioregs const mx6sdl_ctrl = {
+	.mmdc_dram_dqm0 = MX6DL_IOM_DRAM_DQM0,
+	.mmdc_dram_dqm1 = MX6DL_IOM_DRAM_DQM1,
+	.mmdc_dram_dqm2 = MX6DL_IOM_DRAM_DQM2,
+	.mmdc_dram_dqm3 = MX6DL_IOM_DRAM_DQM3,
+	.mmdc_dram_dqm4 = MX6DL_IOM_DRAM_DQM4,
+	.mmdc_dram_dqm5 = MX6DL_IOM_DRAM_DQM5,
+	.mmdc_dram_dqm6 = MX6DL_IOM_DRAM_DQM6,
+	.mmdc_dram_dqm7 = MX6DL_IOM_DRAM_DQM7,
+	.mmdc_dram_cas = MX6DL_IOM_DRAM_CAS,
+	.mmdc_dram_ras = MX6DL_IOM_DRAM_RAS,
+	.mmdc_dram_reset = MX6DL_IOM_DRAM_RESET,
+	.mmdc_dram_sdclk_0 = MX6DL_IOM_DRAM_SDCLK_0,
+	.mmdc_dram_sdclk_1 = MX6DL_IOM_DRAM_SDCLK_1,
+	.mmdc_dram_sdba2 = MX6DL_IOM_DRAM_SDBA2,
+	.mmdc_dram_sdcke0 = MX6DL_IOM_DRAM_SDCKE0,
+	.mmdc_dram_sdcke1 = MX6DL_IOM_DRAM_SDCKE1,
+	.mmdc_dram_sdodt0 = MX6DL_IOM_DRAM_SDODT0,
+	.mmdc_dram_sdodt1 = MX6DL_IOM_DRAM_SDODT1,
+	.mmdc_dram_sdqs0 = MX6DL_IOM_DRAM_SDQS0,
+	.mmdc_dram_sdqs1 = MX6DL_IOM_DRAM_SDQS1,
+	.mmdc_dram_sdqs2 = MX6DL_IOM_DRAM_SDQS2,
+	.mmdc_dram_sdqs3 = MX6DL_IOM_DRAM_SDQS3,
+	.mmdc_dram_sdqs4 = MX6DL_IOM_DRAM_SDQS4,
+	.mmdc_dram_sdqs5 = MX6DL_IOM_DRAM_SDQS5,
+	.mmdc_dram_sdqs6 = MX6DL_IOM_DRAM_SDQS6,
+	.mmdc_dram_sdqs7 = MX6DL_IOM_DRAM_SDQS7,
+	.mmdc_grp_b0ds = MX6DL_IOM_GRP_B0DS,
+	.mmdc_grp_b1ds = MX6DL_IOM_GRP_B1DS,
+	.mmdc_grp_b2ds = MX6DL_IOM_GRP_B2DS,
+	.mmdc_grp_b3ds = MX6DL_IOM_GRP_B3DS,
+	.mmdc_grp_b4ds = MX6DL_IOM_GRP_B4DS,
+	.mmdc_grp_b5ds = MX6DL_IOM_GRP_B5DS,
+	.mmdc_grp_b6ds = MX6DL_IOM_GRP_B6DS,
+	.mmdc_grp_b7ds = MX6DL_IOM_GRP_B7DS,
+	.mmdc_grp_addds = MX6DL_IOM_GRP_ADDDS,
+	.mmdc_grp_ddrpke = MX6DL_IOM_GRP_DDRPKE,
+	.mmdc_grp_ctlds = MX6DL_IOM_GRP_CTLDS,
+	.mmdc_grp_ddr_type = MX6DL_IOM_GRP_DDR_TYPE,
+	.mmdc_ddrmode_ctl = MX6DL_IOM_DDRMODE_CTL,
+	.mmdc_grp_ddrmode = MX6Q_IOM_GRP_DDRMODE,
+};
+#endif
+
+struct mx6_mmdc_cfgregs const mx6_ddrcfg = {
+	.mmdc_p0_mdctl = MX6_MMDC_P0_MDCTL,
+	.mmdc_p0_mdpdc = MX6_MMDC_P0_MDPDC,
+	.mmdc_p0_mdotc = MX6_MMDC_P0_MDOTC,
+	.mmdc_p0_mdcfg0 = MX6_MMDC_P0_MDCFG0,
+	.mmdc_p0_mdcfg1 = MX6_MMDC_P0_MDCFG1,
+	.mmdc_p0_mdcfg2 = MX6_MMDC_P0_MDCFG2,
+	.mmdc_p0_mdmisc = MX6_MMDC_P0_MDMISC,
+	.mmdc_p0_mdscr = MX6_MMDC_P0_MDSCR,
+	.mmdc_p0_mdref = MX6_MMDC_P0_MDREF,
+	.mmdc_p0_mdrwd = MX6_MMDC_P0_MDRWD,
+	.mmdc_p0_mdor = MX6_MMDC_P0_MDOR,
+	.mmdc_p0_mdasp = MX6_MMDC_P0_MDASP,
+	.mmdc_p0_mapsr = MX6_MMDC_P0_MAPSR,
+	.mmdc_p0_mpzqhwctrl = MX6_MMDC_P0_MPZQHWCTRL,
+	.mmdc_p0_mpwldectrl0 = MX6_MMDC_P0_MPWLDECTRL0,
+	.mmdc_p0_mpwldectrl1 = MX6_MMDC_P0_MPWLDECTRL1,
+	.mmdc_p0_mpodtctrl = MX6_MMDC_P0_MPODTCTRL,
+	.mmdc_p0_mprddqby0dl = MX6_MMDC_P0_MPRDDQBY0DL,
+	.mmdc_p0_mprddqby1dl = MX6_MMDC_P0_MPRDDQBY1DL,
+	.mmdc_p0_mprddqby2dl = MX6_MMDC_P0_MPRDDQBY2DL,
+	.mmdc_p0_mprddqby3dl = MX6_MMDC_P0_MPRDDQBY3DL,
+	.mmdc_p0_mpdgctrl0 = MX6_MMDC_P0_MPDGCTRL0,
+	.mmdc_p0_mpdgctrl1 = MX6_MMDC_P0_MPDGCTRL1,
+	.mmdc_p0_mprddlctl = MX6_MMDC_P0_MPRDDLCTL,
+	.mmdc_p0_mpwrdlctl = MX6_MMDC_P0_MPWRDLCTL,
+	.mmdc_p0_mpmur0 = MX6_MMDC_P0_MPMUR0,
+
+	.mmdc_p1_mdctl = MX6_MMDC_P1_MDCTL,
+	.mmdc_p1_mdpdc = MX6_MMDC_P1_MDPDC,
+	.mmdc_p1_mdotc = MX6_MMDC_P1_MDOTC,
+	.mmdc_p1_mdcfg0 = MX6_MMDC_P1_MDCFG0,
+	.mmdc_p1_mdcfg1 = MX6_MMDC_P1_MDCFG1,
+	.mmdc_p1_mdcfg2 = MX6_MMDC_P1_MDCFG2,
+	.mmdc_p1_mdmisc = MX6_MMDC_P1_MDMISC,
+	.mmdc_p1_mdscr = MX6_MMDC_P1_MDSCR,
+	.mmdc_p1_mdref = MX6_MMDC_P1_MDREF,
+	.mmdc_p1_mdrwd = MX6_MMDC_P1_MDRWD,
+	.mmdc_p1_mdor = MX6_MMDC_P1_MDOR,
+	.mmdc_p1_mdasp = MX6_MMDC_P1_MDASP,
+	.mmdc_p1_mapsr = MX6_MMDC_P1_MAPSR,
+	.mmdc_p1_mpzqhwctrl = MX6_MMDC_P1_MPZQHWCTRL,
+	.mmdc_p1_mpwldectrl0 = MX6_MMDC_P1_MPWLDECTRL0,
+	.mmdc_p1_mpwldectrl1 = MX6_MMDC_P1_MPWLDECTRL1,
+	.mmdc_p1_mpodtctrl = MX6_MMDC_P1_MPODTCTRL,
+	.mmdc_p1_mprddqby0dl = MX6_MMDC_P1_MPRDDQBY0DL,
+	.mmdc_p1_mprddqby1dl = MX6_MMDC_P1_MPRDDQBY1DL,
+	.mmdc_p1_mprddqby2dl = MX6_MMDC_P1_MPRDDQBY2DL,
+	.mmdc_p1_mprddqby3dl = MX6_MMDC_P1_MPRDDQBY3DL,
+	.mmdc_p1_mpdgctrl0 = MX6_MMDC_P1_MPDGCTRL0,
+	.mmdc_p1_mpdgctrl1 = MX6_MMDC_P1_MPDGCTRL1,
+	.mmdc_p1_mprddlctl = MX6_MMDC_P1_MPRDDLCTL,
+	.mmdc_p1_mpwrdlctl = MX6_MMDC_P1_MPWRDLCTL,
+	.mmdc_p1_mpmur0 = MX6_MMDC_P1_MPMUR0,
+};
+
+/*
+ * Configure mmdc iomux with board-specific register set
+ * and a bus width
+ */
+void mx6_dram_iocfg(unsigned width,
+		    const struct mx6_mmdc_ioregs *a,
+		    const struct mx6_mmdc_ioregs *d)
+{
+	/* DDR IO Type */
+	writel(d->mmdc_grp_ddr_type, a->mmdc_grp_ddr_type);
+	writel(d->mmdc_grp_ddrpke, a->mmdc_grp_ddrpke);
+
+	/* Clock */
+	writel(d->mmdc_dram_sdclk_0, a->mmdc_dram_sdclk_0);
+	writel(d->mmdc_dram_sdclk_1, a->mmdc_dram_sdclk_1);
+
+	/* Address */
+	writel(d->mmdc_dram_cas, a->mmdc_dram_cas);
+	writel(d->mmdc_dram_ras, a->mmdc_dram_ras);
+	writel(d->mmdc_grp_addds, a->mmdc_grp_addds);
+
+	/* Control */
+	writel(d->mmdc_dram_reset, a->mmdc_dram_reset);
+	writel(d->mmdc_dram_sdcke0, a->mmdc_dram_sdcke0);
+	writel(d->mmdc_dram_sdcke1, a->mmdc_dram_sdcke1);
+	writel(d->mmdc_dram_sdba2, a->mmdc_dram_sdba2);
+	writel(d->mmdc_dram_sdodt0, a->mmdc_dram_sdodt0);
+	writel(d->mmdc_dram_sdodt1, a->mmdc_dram_sdodt1);
+	writel(d->mmdc_grp_ctlds, a->mmdc_grp_ctlds);
+
+	/* Data Strobes */
+	writel(d->mmdc_ddrmode_ctl, a->mmdc_ddrmode_ctl);
+	writel(d->mmdc_dram_sdqs0, a->mmdc_dram_sdqs0);
+	writel(d->mmdc_dram_sdqs1, a->mmdc_dram_sdqs1);
+	if (width >= 32) {
+		writel(d->mmdc_dram_sdqs2, a->mmdc_dram_sdqs2);
+		writel(d->mmdc_dram_sdqs3, a->mmdc_dram_sdqs3);
+	}
+	if (width >= 64) {
+		writel(d->mmdc_dram_sdqs4, a->mmdc_dram_sdqs4);
+		writel(d->mmdc_dram_sdqs5, a->mmdc_dram_sdqs5);
+		writel(d->mmdc_dram_sdqs6, a->mmdc_dram_sdqs6);
+		writel(d->mmdc_dram_sdqs7, a->mmdc_dram_sdqs7);
+	}
+
+	/* Data */
+	writel(d->mmdc_grp_ddrmode, a->mmdc_grp_ddrmode);
+	writel(d->mmdc_grp_b0ds, a->mmdc_grp_b0ds);
+	writel(d->mmdc_grp_b1ds, a->mmdc_grp_b1ds);
+	if (width >= 32) {
+		writel(d->mmdc_grp_b2ds, a->mmdc_grp_b2ds);
+		writel(d->mmdc_grp_b3ds, a->mmdc_grp_b3ds);
+	}
+	if (width >= 64) {
+		writel(d->mmdc_grp_b4ds, a->mmdc_grp_b4ds);
+		writel(d->mmdc_grp_b5ds, a->mmdc_grp_b5ds);
+		writel(d->mmdc_grp_b6ds, a->mmdc_grp_b6ds);
+		writel(d->mmdc_grp_b7ds, a->mmdc_grp_b7ds);
+	}
+	writel(d->mmdc_dram_dqm0, a->mmdc_dram_dqm0);
+	writel(d->mmdc_dram_dqm1, a->mmdc_dram_dqm1);
+	if (width >= 32) {
+		writel(d->mmdc_dram_dqm2, a->mmdc_dram_dqm2);
+		writel(d->mmdc_dram_dqm3, a->mmdc_dram_dqm3);
+	}
+	if (width >= 64) {
+		writel(d->mmdc_dram_dqm4, a->mmdc_dram_dqm4);
+		writel(d->mmdc_dram_dqm5, a->mmdc_dram_dqm5);
+		writel(d->mmdc_dram_dqm6, a->mmdc_dram_dqm6);
+		writel(d->mmdc_dram_dqm7, a->mmdc_dram_dqm7);
+	}
+}
+
+
+/* configure DDR3 mode registers */
+static inline void write_mr(u16 val, u8 ba, u8 cmd, bool cs1, u32 mdscr)
+{
+	u32 reg = val << 16;
+	reg |= (1 << 15);	/* Configuration request */
+	reg |= (cmd << 4);	/* Command */
+	reg |= (cs1 << 3);	/* CS0 | CS1 */
+	reg |= ba;		/* mode register addr */
+	debug("MR%d: val=%04x ba=%d cmd=%d cs1=%d\n", ba, val, ba, cmd, cs1);
+	writel(reg, mdscr);
+}
+
+/*
+ * Configure mx6 mmdc registers based on:
+ *  - board-specific memory configuration
+ *  - board-specific calibration data
+ *  - ddr3 chip details
+ *
+ * The various calculations here are derived from the Freescale
+ * i.Mx6DQSDL DDR3 Script Aid spreadsheet (DOC-94917) designed to generate MMDC
+ * configuration registers based on memory system and memory chip parameters.
+ *
+ * The defaults here are those which were specified in the spreadsheet.
+ * For details on each register, refer to the IMX6DQRM and/or IMX6SDLRM
+ */
+void mx6_dram_cfg(const struct mx6_ddr_sysinfo *i,
+		  const struct mx6_mmdc_cfgregs *a,
+		  const struct mx6_mmdc_calibration *c,
+		  const struct mx6_ddr3_cfg *m)
+{
+	u32 reg;
+	u8 tcke, tcksrx, tcksre, txpdll, taofpd, taonpd, trrd;
+	u8 todtlon, taxpd, tanpd, tcwl, txp, tfaw, tcl;
+	u8 todt_idle_off = 0x4; /* from DDR3 Script Aid spreadsheet */
+	u16 trcd, trc, tras, twr, tmrd, trtp, trp, twtr, trfc, txs, txpr;
+	u16 CS0_END;
+	u16 tdllk = 0x1ff; /* DLL locking time: 512 cycles (JEDEC DDR3) */
+	int clkper; /* clock period in picoseconds */
+	int clock; /* clock freq in mHz */
+	int cs;
+
+	/* MX6D/MX6Q: 1066 MHz memory clock, clkper = 1.894ns = 1894ps */
+	if (is_cpu_type(MXC_CPU_MX6Q)) {
+		clock = 528;
+		tcwl = 4;
+	}
+	/* MX6S/MX6DL: 800 MHz memory clock, clkper = 2.5ns = 2500ps */
+	else {
+		clock = 400;
+		tcwl = 3;
+	}
+	clkper = (1000*1000)/clock; /* ps */
+	todtlon = tcwl;
+	taxpd = tcwl;
+	tanpd = tcwl;
+	tcwl = tcwl;
+
+	switch (m->density) {
+	case 1: /* 1Gb per chip */
+		trfc = DIV_ROUND_UP(110000, clkper) - 1;
+		txs = DIV_ROUND_UP(120000, clkper) - 1;
+		break;
+	case 2: /* 2Gb per chip */
+		trfc = DIV_ROUND_UP(160000, clkper) - 1;
+		txs = DIV_ROUND_UP(170000, clkper) - 1;
+		break;
+	case 4: /* 4Gb per chip */
+		trfc = DIV_ROUND_UP(260000, clkper) - 1;
+		txs = DIV_ROUND_UP(270000, clkper) - 1;
+		break;
+	case 8: /* 8Gb per chip */
+		trfc = DIV_ROUND_UP(350000, clkper) - 1;
+		txs = DIV_ROUND_UP(360000, clkper) - 1;
+		break;
+	default:
+		/* invalid density */
+		printf("invalid chip density\n");
+		hang();
+		break;
+	}
+	txpr = txs;
+
+	switch (m->mem_speed) {
+	case 800:
+		txp = DIV_ROUND_UP(MAX(3*clkper, 7500), clkper) - 1;
+		tcke = DIV_ROUND_UP(MAX(3*clkper, 7500), clkper) - 1;
+		if (m->pagesz == 1) {
+			tfaw = DIV_ROUND_UP(40000, clkper) - 1;
+			trrd = DIV_ROUND_UP(MAX(4*clkper, 10000), clkper) - 1;
+		} else {
+			tfaw = DIV_ROUND_UP(50000, clkper) - 1;
+			trrd = DIV_ROUND_UP(MAX(4*clkper, 10000), clkper) - 1;
+		}
+		break;
+	case 1066:
+		txp = DIV_ROUND_UP(MAX(3*clkper, 7500), clkper) - 1;
+		tcke = DIV_ROUND_UP(MAX(3*clkper, 5625), clkper) - 1;
+		if (m->pagesz == 1) {
+			tfaw = DIV_ROUND_UP(37500, clkper) - 1;
+			trrd = DIV_ROUND_UP(MAX(4*clkper, 7500), clkper) - 1;
+		} else {
+			tfaw = DIV_ROUND_UP(50000, clkper) - 1;
+			trrd = DIV_ROUND_UP(MAX(4*clkper, 10000), clkper) - 1;
+		}
+		break;
+	case 1333:
+		txp = DIV_ROUND_UP(MAX(3*clkper, 6000), clkper) - 1;
+		tcke = DIV_ROUND_UP(MAX(3*clkper, 5625), clkper) - 1;
+		if (m->pagesz == 1) {
+			tfaw = DIV_ROUND_UP(30000, clkper) - 1;
+			trrd = DIV_ROUND_UP(MAX(4*clkper, 6000), clkper) - 1;
+		} else {
+			tfaw = DIV_ROUND_UP(45000, clkper) - 1;
+			trrd = DIV_ROUND_UP(MAX(4*clkper, 7500), clkper) - 1;
+		}
+		break;
+	case 1600:
+		txp = DIV_ROUND_UP(MAX(3*clkper, 6000), clkper) - 1;
+		tcke = DIV_ROUND_UP(MAX(3*clkper, 5000), clkper) - 1;
+		if (m->pagesz == 1) {
+			tfaw = DIV_ROUND_UP(30000, clkper) - 1;
+			trrd = DIV_ROUND_UP(MAX(4*clkper, 6000), clkper) - 1;
+		} else {
+			tfaw = DIV_ROUND_UP(40000, clkper) - 1;
+			trrd = DIV_ROUND_UP(MAX(4*clkper, 7500), clkper) - 1;
+		}
+		break;
+	default:
+		printf("invalid memory speed\n");
+		hang();
+		break;
+	}
+	txpdll = DIV_ROUND_UP(MAX(10*clkper, 24000), clkper) - 1;
+	tcl = DIV_ROUND_UP(m->trcd, clkper/10) - 3;
+	tcksre = DIV_ROUND_UP(MAX(5*clkper, 10000), clkper);
+	tcksrx = tcksre;
+	taonpd = DIV_ROUND_UP(2000, clkper) - 1;
+	taofpd = taonpd;
+	trp = DIV_ROUND_UP(m->trcd, clkper/10) - 1;
+	trcd = trp;
+	trc = DIV_ROUND_UP(m->trcmin, clkper/10) - 1;
+	tras = DIV_ROUND_UP(m->trasmin, clkper/10) - 1;
+	twr = DIV_ROUND_UP(15000, clkper) - 1;
+	tmrd = DIV_ROUND_UP(MAX(12*clkper, 15000), clkper) - 1;
+	twtr = ROUND(MAX(4*clkper, 7500)/clkper, 1) - 1;
+	trtp = twtr;
+	CS0_END = ((4*i->cs_density) <= 120) ? (4*i->cs_density)+7 : 127;
+	debug("density:%d Gb (%d Gb per chip)\n", i->cs_density, m->density);
+	debug("clock: %dMHz (%d ps)\n", clock, clkper);
+	debug("memspd:%d\n", m->mem_speed);
+	debug("tcke=%d\n", tcke);
+	debug("tcksrx=%d\n", tcksrx);
+	debug("tcksre=%d\n", tcksre);
+	debug("taofpd=%d\n", taofpd);
+	debug("taonpd=%d\n", taonpd);
+	debug("todtlon=%d\n", todtlon);
+	debug("tanpd=%d\n", tanpd);
+	debug("taxpd=%d\n", taxpd);
+	debug("trfc=%d\n", trfc);
+	debug("txs=%d\n", txs);
+	debug("txp=%d\n", txp);
+	debug("txpdll=%d\n", txpdll);
+	debug("tfaw=%d\n", tfaw);
+	debug("tcl=%d\n", tcl);
+	debug("trcd=%d\n", trcd);
+	debug("trp=%d\n", trp);
+	debug("trc=%d\n", trc);
+	debug("tras=%d\n", tras);
+	debug("twr=%d\n", twr);
+	debug("tmrd=%d\n", tmrd);
+	debug("tcwl=%d\n", tcwl);
+	debug("tdllk=%d\n", tdllk);
+	debug("trtp=%d\n", trtp);
+	debug("twtr=%d\n", twtr);
+	debug("trrd=%d\n", trrd);
+	debug("txpr=%d\n", txpr);
+	debug("CS0_END=%d\n", CS0_END);
+	debug("ncs=%d\n", i->ncs);
+	debug("Rtt_wr=%d\n", i->rtt_wr);
+	debug("Rtt_nom=%d\n", i->rtt_nom);
+	debug("SRT=%d\n", m->SRT);
+	debug("tcl=%d\n", tcl);
+	debug("twr=%d\n", twr);
+
+	/* ZQ: enable both one-time & periodic (1ms) HW ZQ calibration */
+	writel(0xa1390003, a->mmdc_p0_mpzqhwctrl);
+	if (i->dsize > 1)
+		writel(0xa1390003, a->mmdc_p1_mpzqhwctrl);
+
+	/*
+	 * board-specific configuration:
+	 *  These values are determined empirically and vary per board layout
+	 *  see:
+	 *   appnote, ddr3 spreadsheet
+	 */
+	writel(c->mmdc_p0_mpwldectrl0, a->mmdc_p0_mpwldectrl0);
+	writel(c->mmdc_p0_mpwldectrl1, a->mmdc_p0_mpwldectrl1);
+	writel(c->mmdc_p0_mpdgctrl0, a->mmdc_p0_mpdgctrl0);
+	writel(c->mmdc_p0_mpdgctrl1, a->mmdc_p0_mpdgctrl1);
+	writel(c->mmdc_p0_mprddlctl, a->mmdc_p0_mprddlctl);
+	writel(c->mmdc_p0_mpwrdlctl, a->mmdc_p0_mpwrdlctl);
+	if (i->dsize > 1) {
+		writel(c->mmdc_p1_mpwldectrl0, a->mmdc_p1_mpwldectrl0);
+		writel(c->mmdc_p1_mpwldectrl1, a->mmdc_p1_mpwldectrl1);
+		writel(c->mmdc_p1_mpdgctrl0, a->mmdc_p1_mpdgctrl0);
+		writel(c->mmdc_p1_mpdgctrl1, a->mmdc_p1_mpdgctrl1);
+		writel(c->mmdc_p1_mprddlctl, a->mmdc_p1_mprddlctl);
+		writel(c->mmdc_p1_mpwrdlctl, a->mmdc_p1_mpwrdlctl);
+	}
+
+	/* Read data DQ Byte0-3 delay */
+	writel(0x33333333, a->mmdc_p0_mprddqby0dl);
+	writel(0x33333333, a->mmdc_p0_mprddqby1dl);
+	if (i->dsize > 0) {
+		writel(0x33333333, a->mmdc_p0_mprddqby2dl);
+		writel(0x33333333, a->mmdc_p0_mprddqby3dl);
+	}
+	if (i->dsize > 1) {
+		writel(0x33333333, a->mmdc_p1_mprddqby0dl);
+		writel(0x33333333, a->mmdc_p1_mprddqby1dl);
+		writel(0x33333333, a->mmdc_p1_mprddqby2dl);
+		writel(0x33333333, a->mmdc_p1_mprddqby3dl);
+	}
+
+	/* complete calibration by forced measurement */
+	writel(0x00000800, a->mmdc_p0_mpmur0);
+	if (i->dsize > 1)
+		writel(0x00000800, a->mmdc_p1_mpmur0);
+
+	/* MMDC init */
+	reg = (tcke << 16) | (tcksrx << 3) | tcksre;
+	writel(reg, a->mmdc_p0_mdpdc);
+	reg = (taofpd << 27) | (taonpd << 24) | (tanpd << 20) | (taxpd << 16) |
+	      (todtlon << 12) | (todt_idle_off << 4);
+	writel(reg, a->mmdc_p0_mdotc);
+
+	/* Timing configuration */
+	reg = (trfc << 24) | (txs << 16) | (txp << 13) | (txpdll << 9) |
+	      (tfaw << 4) | tcl;
+	writel(reg, a->mmdc_p0_mdcfg0);
+	reg = (trcd << 29) | (trp << 26) | (trc << 21) | (tras << 16) |
+	      (1 << 15) |		/* trpa */
+	      (twr << 9) | (tmrd << 5) | tcwl;
+	writel(reg, a->mmdc_p0_mdcfg1);
+	reg = (tdllk << 16) | (trtp << 6) | (twtr << 3) | trrd;
+	writel(reg, a->mmdc_p0_mdcfg2);
+	reg = (i->cs1_mirror << 19) | (i->walat << 16) | (i->bi_on << 12) |
+	      (i->mif3_mode << 9) | (i->ralat << 6);
+	writel(reg, a->mmdc_p0_mdmisc);
+
+	/* configuration request */
+	writel(1 << 15, a->mmdc_p0_mdscr); /* config request */
+	writel(0x000026d2, a->mmdc_p0_mdrwd);
+	reg = (txpr << 16) | (i->sde_to_rst << 8) | (i->rst_to_cke << 0);
+	writel(reg, a->mmdc_p0_mdor);
+
+	/* addressing */
+	writel(CS0_END, a->mmdc_p0_mdasp);
+	reg = (1 << 31) |			/* SDE_0 */
+	      ((i->ncs == 2) ? 1 : 0) << 30 |	/* SDE_1 */
+	      (m->rowaddr - 11) << 24 |		/* ROW */
+	      (m->coladdr - 9) << 20 |		/* COL */
+	      (1 << 19) |			/* Burst Length = 8 for DDR3 */
+	      (i->dsize << 16);			/* DDR data bus size */
+	writel(reg, a->mmdc_p0_mdctl);
+
+	/* Write Mode Registers to Init DDR3 devices */
+	for (cs = 0; cs < i->ncs; cs++) {
+		/* MR2 */
+		reg = (i->rtt_wr & 3) << 9 | (m->SRT & 1) << 7 |
+		      ((tcwl - 3) & 3) << 3;
+		write_mr(reg, 2, 3, cs, a->mmdc_p0_mdscr);
+		/* MR3 */
+		write_mr(0, 3, 3, cs, a->mmdc_p0_mdscr);
+		/* MR1 */
+		reg = ((i->rtt_nom & 1) ? 1 : 0) << 2 |
+		      ((i->rtt_nom & 2) ? 1 : 0) << 6;
+		write_mr(reg, 1, 3, cs, a->mmdc_p0_mdscr);
+		reg = ((tcl - 1) << 4) |	/* CAS */
+		      (1 << 8)   |		/* DLL Reset */
+		      ((twr - 3) << 9);		/* Write Recovery */
+		/* MR0 */
+		write_mr(reg, 0, 3, cs, a->mmdc_p0_mdscr);
+		/* ZQ calibration */
+		reg = (1 << 10);
+		write_mr(reg, 0, 4, cs, a->mmdc_p0_mdscr);
+	}
+
+	/* refresh control register */
+	reg = (1 << 14) |	/* REF_SEL: Periodic refresh cycles of 32kHz */
+	      (7 << 11);	/* REFR: Refresh Rate - 8 refreshes */
+	writel(reg, a->mmdc_p0_mdref);
+
+	/* MMDC Termination: rtt_nom:2 RZQ/2(120ohm), rtt_nom:1 RZQ/4(60ohm) */
+	reg = (i->rtt_nom == 2) ? 0x00011117 : 0x00022227;
+	writel(reg, a->mmdc_p0_mpodtctrl);
+	if (i->dsize > 1)
+		writel(reg, a->mmdc_p1_mpodtctrl);
+	/* Power down control */
+	reg = (tcke & 0x7) << 16 |
+	      5            << 12 |  /* PWDT_1: 256 cycles */
+	      5            <<  8 |  /* PWDT_0: 256 cycles */
+	      1            <<  6 |  /* BOTH_CS_PD */
+	      (tcksrx & 0x7) << 3 |
+	      (tcksre & 0x7);
+	writel(reg, a->mmdc_p0_mdpdc); /* SDCTL power down enabled */
+	writel(0x00011006, a->mmdc_p0_mapsr); /* ADOPT power down enabled */
+	writel(0x00000000, a->mmdc_p0_mdscr); /* init complete */
+}
diff --git a/arch/arm/include/asm/arch-mx6/mx6-ddr.h b/arch/arm/include/asm/arch-mx6/mx6-ddr.h
index 6ac857e..1081aa9 100644
--- a/arch/arm/include/asm/arch-mx6/mx6-ddr.h
+++ b/arch/arm/include/asm/arch-mx6/mx6-ddr.h
@@ -9,6 +9,52 @@
 #define MX6_ADDR_DECLARE(prefix, name, addr) \
 	prefix##name = addr
 
+/*
+ * MMDC iomux registers (pinctl/padctl) - (different for IMX6DQ vs IMX6SDL)
+ */
+struct mx6_mmdc_ioregs {
+	u32 mmdc_dram_dqm0;
+	u32 mmdc_dram_dqm1;
+	u32 mmdc_dram_dqm2;
+	u32 mmdc_dram_dqm3;
+	u32 mmdc_dram_dqm4;
+	u32 mmdc_dram_dqm5;
+	u32 mmdc_dram_dqm6;
+	u32 mmdc_dram_dqm7;
+	u32 mmdc_dram_cas;
+	u32 mmdc_dram_ras;
+	u32 mmdc_dram_reset;
+	u32 mmdc_dram_sdclk_0;
+	u32 mmdc_dram_sdclk_1;
+	u32 mmdc_dram_sdba2;
+	u32 mmdc_dram_sdcke0;
+	u32 mmdc_dram_sdcke1;
+	u32 mmdc_dram_sdodt0;
+	u32 mmdc_dram_sdodt1;
+	u32 mmdc_dram_sdqs0;
+	u32 mmdc_dram_sdqs1;
+	u32 mmdc_dram_sdqs2;
+	u32 mmdc_dram_sdqs3;
+	u32 mmdc_dram_sdqs4;
+	u32 mmdc_dram_sdqs5;
+	u32 mmdc_dram_sdqs6;
+	u32 mmdc_dram_sdqs7;
+	u32 mmdc_grp_b0ds;
+	u32 mmdc_grp_b1ds;
+	u32 mmdc_grp_b2ds;
+	u32 mmdc_grp_b3ds;
+	u32 mmdc_grp_b4ds;
+	u32 mmdc_grp_b5ds;
+	u32 mmdc_grp_b6ds;
+	u32 mmdc_grp_b7ds;
+	u32 mmdc_grp_addds;
+	u32 mmdc_grp_ddrmode;
+	u32 mmdc_ddrmode_ctl;
+	u32 mmdc_grp_ddrpke;
+	u32 mmdc_grp_ctlds;
+	u32 mmdc_grp_ddr_type;
+};
+
 #ifdef CONFIG_MX6QDL
 enum {
 #define MX6_ADDR_DECL(name, addr) \
@@ -16,6 +62,7 @@ enum {
 #include "mx6q-ddr.h"
 };
 #undef MX6_ADDR_DECL
+extern struct mx6_mmdc_ioregs const mx6dq_ctrl;
 
 enum {
 #define MX6_ADDR_DECL(name, addr) \
@@ -23,6 +70,8 @@ enum {
 #include "mx6dl-ddr.h"
 };
 
+extern struct mx6_mmdc_ioregs const mx6sdl_ctrl;
+
 #elif defined(CONFIG_MX6Q)
 #define MX6_ADDR_DECL(name, addr) \
 	MX6_ADDR_DECLARE(MX6_, name, addr),
@@ -35,6 +84,7 @@ enum {
 #error "Please select cpu"
 #endif
 
+/* channel 0 */
 #define MX6_MMDC_P0_MDCTL	0x021b0000
 #define MX6_MMDC_P0_MDPDC	0x021b0004
 #define MX6_MMDC_P0_MDOTC	0x021b0008
@@ -62,6 +112,7 @@ enum {
 #define MX6_MMDC_P0_MPWRDLCTL	0x021b0850
 #define MX6_MMDC_P0_MPMUR0	0x021b08b8
 
+/* channel 1 */
 #define MX6_MMDC_P1_MDCTL	0x021b4000
 #define MX6_MMDC_P1_MDPDC	0x021b4004
 #define MX6_MMDC_P1_MDOTC	0x021b4008
@@ -89,4 +140,149 @@ enum {
 #define MX6_MMDC_P1_MPWRDLCTL	0x021b4850
 #define MX6_MMDC_P1_MPMUR0	0x021b48b8
 
+/* Device Information:
+ *  Varies per DDR3 part number and speed grade
+ */
+struct mx6_ddr3_cfg {
+	u16 mem_speed;	/* ie 1600 for DDR3-1600 (800,1066,1333,1600) */
+	u8 density;	/* chip density (Gb) (1,2,4,8) */
+	u8 width;	/* bus width (bits) (4,8,16) */
+	u8 banks;	/* number of banks */
+	u8 rowaddr;	/* row address bits (11-16)*/
+	u8 coladdr;	/* col address bits (9-12) */
+	u8 pagesz;	/* page size (K) (1-2) */
+	u16 trcd;	/* tRCD=tRP=CL (ns*100) */
+	u16 trcmin;	/* tRC min (ns*100) */
+	u16 trasmin;	/* tRAS min (ns*100) */
+	u8 SRT;		/* self-refresh temperature: 0=normal, 1=extended */
+};
+
+/*
+ * System Information:
+ *  Varies per board design, memory layout, and termination choices
+ */
+struct mx6_ddr_sysinfo {
+	u8 dsize;	/* size of bus (in dwords: 0=16bit,1=32bit,2=64bit) */
+	u8 cs_density;	/* density per chip select (Gb) */
+	u8 ncs;		/* number chip selects used (1|2) */
+	char cs1_mirror;/* enable address mirror (0|1) */
+	char bi_on;	/* Bank interleaving enable */
+	u8 rtt_nom;	/* Rtt_Nom (DDR3_RTT_*) */
+	u8 rtt_wr;	/* Rtt_Wr (DDR3_RTT_*) */
+	u8 ralat;	/* Read Additional Latency (0-7) */
+	u8 walat;	/* Write Additional Latency (0-3) */
+	u8 mif3_mode;	/* Command prediction working mode */
+	u8 rst_to_cke;	/* Time from SDE enable to CKE rise */
+	u8 sde_to_rst;	/* Time from SDE enable until DDR reset# is high */
+};
+
+/*
+ * Board specific calibration:
+ *   This includes write leveling calibration values as well as DQS gating
+ *   and read/write delays. These values are board/layout/device specific.
+ *   Freescale recommends using the i.MX6 DDR Stress Test Tool V1.0.2
+ *   (DOC-96412) to determine these values over a range of boards and
+ *   temperatures.
+ */
+struct mx6_mmdc_calibration {
+	/* write leveling calibration */
+	u32 mmdc_p0_mpwldectrl0;
+	u32 mmdc_p0_mpwldectrl1;
+	u32 mmdc_p1_mpwldectrl0;
+	u32 mmdc_p1_mpwldectrl1;
+	/* read DQS gating */
+	u32 mmdc_p0_mpdgctrl0;
+	u32 mmdc_p0_mpdgctrl1;
+	u32 mmdc_p1_mpdgctrl0;
+	u32 mmdc_p1_mpdgctrl1;
+	/* read delay */
+	u32 mmdc_p0_mprddlctl;
+	u32 mmdc_p1_mprddlctl;
+	/* write delay */
+	u32 mmdc_p0_mpwrdlctl;
+	u32 mmdc_p1_mpwrdlctl;
+};
+
+/*
+ * MMDC Configuration registers (same for IMX6DQ vs IMX6SDL)
+ */
+struct mx6_mmdc_cfgregs {
+	/* channel0 */
+	u32 mmdc_p0_mdctl;
+	u32 mmdc_p0_mdpdc;
+	u32 mmdc_p0_mdotc;
+	u32 mmdc_p0_mdcfg0;
+	u32 mmdc_p0_mdcfg1;
+	u32 mmdc_p0_mdcfg2;
+	u32 mmdc_p0_mdmisc;
+	u32 mmdc_p0_mdscr;
+	u32 mmdc_p0_mdref;
+	u32 mmdc_p0_mdrwd;
+	u32 mmdc_p0_mdor;
+	u32 mmdc_p0_mdasp;
+	u32 mmdc_p0_mapsr;
+	u32 mmdc_p0_mpzqhwctrl;
+
+	/* board specific: run write leveling calibration to determine */
+	u32 mmdc_p0_mpwldectrl0;
+	u32 mmdc_p0_mpwldectrl1;
+	/* board specific: read DQS gating */
+	u32 mmdc_p0_mpdgctrl0;
+	u32 mmdc_p0_mpdgctrl1;
+	/* board specific: read calibration */
+	u32 mmdc_p0_mprddlctl;
+	/* board specific: write calibration */
+	u32 mmdc_p0_mpwrdlctl;
+
+	/* read data bit delay */
+	u32 mmdc_p0_mprddqby0dl;
+	u32 mmdc_p0_mprddqby1dl;
+	u32 mmdc_p0_mprddqby2dl;
+	u32 mmdc_p0_mprddqby3dl;
+
+	u32 mmdc_p0_mpodtctrl;
+	u32 mmdc_p0_mpmur0;
+
+	/* channel1 */
+	u32 mmdc_p1_mdctl;
+	u32 mmdc_p1_mdpdc;
+	u32 mmdc_p1_mdotc;
+	u32 mmdc_p1_mdcfg0;
+	u32 mmdc_p1_mdcfg1;
+	u32 mmdc_p1_mdcfg2;
+	u32 mmdc_p1_mdmisc;
+	u32 mmdc_p1_mdscr;
+	u32 mmdc_p1_mdref;
+	u32 mmdc_p1_mdrwd;
+	u32 mmdc_p1_mdor;
+	u32 mmdc_p1_mdasp;
+	u32 mmdc_p1_mapsr;
+	u32 mmdc_p1_mpzqhwctrl;
+	u32 mmdc_p1_mpwldectrl0;
+	u32 mmdc_p1_mpwldectrl1;
+	u32 mmdc_p1_mpodtctrl;
+	u32 mmdc_p1_mprddqby0dl;
+	u32 mmdc_p1_mprddqby1dl;
+	u32 mmdc_p1_mprddqby2dl;
+	u32 mmdc_p1_mprddqby3dl;
+	u32 mmdc_p1_mpdgctrl0;
+	u32 mmdc_p1_mpdgctrl1;
+	u32 mmdc_p1_mprddlctl;
+	u32 mmdc_p1_mpwrdlctl;
+	u32 mmdc_p1_mpmur0;
+};
+
+extern struct mx6_mmdc_cfgregs const mx6_ddrcfg;
+
+/* configure iomux (pinctl/padctl) */
+void mx6_dram_iocfg(unsigned width,
+		    const struct mx6_mmdc_ioregs *,
+		    const struct mx6_mmdc_ioregs *);
+
+/* configure mx6 mmdc registers */
+void mx6_dram_cfg(const struct mx6_ddr_sysinfo *,
+		  const struct mx6_mmdc_cfgregs *,
+		  const struct mx6_mmdc_calibration *,
+		  const struct mx6_ddr3_cfg *);
+
 #endif	/*__ASM_ARCH_MX6_DDR_H__ */
