summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/imx6-mmdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/imx6-mmdc.c')
-rw-r--r--arch/arm/mach-imx/imx6-mmdc.c63
1 files changed, 44 insertions, 19 deletions
diff --git a/arch/arm/mach-imx/imx6-mmdc.c b/arch/arm/mach-imx/imx6-mmdc.c
index 8f661e3dfe..134a41bad5 100644
--- a/arch/arm/mach-imx/imx6-mmdc.c
+++ b/arch/arm/mach-imx/imx6-mmdc.c
@@ -1,33 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2013 Sascha Hauer <s.hauer@pengutronix.de
+
/*
* i.MX6 DDR controller calibration functions
* Based on Freescale code
- *
- * Copyright (C) 2013 Sascha Hauer <s.hauer@pengutronix.de>
- *
- * 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; either version 2 of
- * the License, or (at your option) any later version.
- *
- * 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.
- *
*/
#include <common.h>
#include <io.h>
-#include <mach/imx6-mmdc.h>
-#include <mach/imx6-regs.h>
-#include <mach/imx6.h>
+#include <mach/imx/imx6-mmdc.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/imx6.h>
+
+static bool wlcalib_failed(void __iomem *ips)
+{
+ /*
+ * The i.MX 6 reference manual specifies that an MMDC flags reports
+ * write calibration errors in the MPWLGCR register's HW_WL_ERR field.
+ *
+ * ERR050070 specifies that this doesn't work and we should check
+ * the MPWLHWERR register instead which reports which write leveling
+ * steps succeeded or failed on a per-byte basis.
+ *
+ * Check each byte to see which steps succeeded. If no steps succeeded
+ * then declare the calibration a failure.
+ */
+
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if (readb(ips + MPWLHWERR + i) == 0)
+ return true;
+ }
+
+ return false;
+}
int mmdc_do_write_level_calibration(void)
{
+ u32 ldectrl[4];
u32 esdmisc_val, zq_val;
int errorcount = 0;
u32 val;
u32 ddr_mr1 = 0x4;
+ /* Store current calibration data in case of failure */
+ ldectrl[0] = readl(P0_IPS + MPWLDECTRL0);
+ ldectrl[1] = readl(P0_IPS + MPWLDECTRL1);
+ ldectrl[2] = readl(P1_IPS + MPWLDECTRL0);
+ ldectrl[3] = readl(P1_IPS + MPWLDECTRL1);
+
/* disable DDR logic power down timer */
val = readl((P0_IPS + MDPDC));
val &= 0xffff00ff;
@@ -66,9 +87,13 @@ int mmdc_do_write_level_calibration(void)
/* Upon completion of this process the MMDC de-asserts the MPWLGCR[HW_WL_EN] */
while (readl(P0_IPS + MPWLGCR) & 0x00000001);
- /* check for any errors: check both PHYs for x64 configuration, if x32, check only PHY0 */
- if ((readl(P0_IPS + MPWLGCR) & 0x00000F00) ||
- (readl(P1_IPS + MPWLGCR) & 0x00000F00)) {
+ /* check for any errors on both PHYs */
+ if (wlcalib_failed(P0_IPS) || wlcalib_failed(P1_IPS)) {
+ pr_debug("Calibration failed, rolling back calibration data\n");
+ writel(ldectrl[0], P0_IPS + MPWLDECTRL0);
+ writel(ldectrl[1], P0_IPS + MPWLDECTRL1);
+ writel(ldectrl[2], P1_IPS + MPWLDECTRL0);
+ writel(ldectrl[3], P1_IPS + MPWLDECTRL1);
errorcount++;
}