Skip to content
Snippets Groups Projects
ddr.c 46.7 KiB
Newer Older
/*
 * Copyright (C) 2014 Gateworks Corporation
 * Author: Tim Harvey <tharvey@gateworks.com>
 *
 * SPDX-License-Identifier:     GPL-2.0+
 */

#include <common.h>
#include <linux/types.h>
#include <asm/arch/clock.h>
#include <asm/arch/mx6-ddr.h>
#include <asm/arch/sys_proto.h>
#include <asm/io.h>
#include <asm/types.h>
#include <wait_bit.h>
static void reset_read_data_fifos(void)
{
	struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR;

	/* Reset data FIFOs twice. */
	setbits_le32(&mmdc0->mpdgctrl0, 1 << 31);
	wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0);

	setbits_le32(&mmdc0->mpdgctrl0, 1 << 31);
	wait_for_bit("MMDC", &mmdc0->mpdgctrl0, 1 << 31, 0, 100, 0);
}

static void precharge_all(const bool cs0_enable, const bool cs1_enable)
{
	struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR;

	/*
	 * Issue the Precharge-All command to the DDR device for both
	 * chip selects. Note, CON_REQ bit should also remain set. If
	 * only using one chip select, then precharge only the desired
	 * chip select.
	 */
	if (cs0_enable) { /* CS0 */
		writel(0x04008050, &mmdc0->mdscr);
		wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0);
	}

	if (cs1_enable) { /* CS1 */
		writel(0x04008058, &mmdc0->mdscr);
		wait_for_bit("MMDC", &mmdc0->mdscr, 1 << 14, 1, 100, 0);
	}
}

static void force_delay_measurement(int bus_size)
{
	struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR;
	struct mmdc_p_regs *mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR;

	writel(0x800, &mmdc0->mpmur0);
	if (bus_size == 0x2)
		writel(0x800, &mmdc1->mpmur0);
}

static void modify_dg_result(u32 *reg_st0, u32 *reg_st1, u32 *reg_ctrl)
{
	u32 dg_tmp_val, dg_dl_abs_offset, dg_hc_del, val_ctrl;

	/*
	 * DQS gating absolute offset should be modified from reflecting
	 * (HW_DG_LOWx + HW_DG_UPx)/2 to reflecting (HW_DG_UPx - 0x80)
	 */

	val_ctrl = readl(reg_ctrl);
	val_ctrl &= 0xf0000000;

	dg_tmp_val = ((readl(reg_st0) & 0x07ff0000) >> 16) - 0xc0;
	dg_dl_abs_offset = dg_tmp_val & 0x7f;
	dg_hc_del = (dg_tmp_val & 0x780) << 1;

	val_ctrl |= dg_dl_abs_offset + dg_hc_del;

	dg_tmp_val = ((readl(reg_st1) & 0x07ff0000) >> 16) - 0xc0;
	dg_dl_abs_offset = dg_tmp_val & 0x7f;
	dg_hc_del = (dg_tmp_val & 0x780) << 1;

	val_ctrl |= (dg_dl_abs_offset + dg_hc_del) << 16;

	writel(val_ctrl, reg_ctrl);
}

int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo)
{
	struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR;
	struct mmdc_p_regs *mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR;
	u32 esdmisc_val, zq_val;
	u32 errors = 0;

	/*
	 * Stash old values in case calibration fails,
	 * we need to restore them
	 */
	ldectrl[0] = readl(&mmdc0->mpwldectrl0);
	ldectrl[1] = readl(&mmdc0->mpwldectrl1);
	if (sysinfo->dsize == 2) {
		ldectrl[2] = readl(&mmdc1->mpwldectrl0);
		ldectrl[3] = readl(&mmdc1->mpwldectrl1);
	}

	/* disable DDR logic power down timer */
	clrbits_le32(&mmdc0->mdpdc, 0xff00);

	/* disable Adopt power down timer */
	setbits_le32(&mmdc0->mapsr, 0x1);

	debug("Starting write leveling calibration.\n");

	/*
	 * 2. disable auto refresh and ZQ calibration
	 * before proceeding with Write Leveling calibration
	 */
	esdmisc_val = readl(&mmdc0->mdref);
	writel(0x0000C000, &mmdc0->mdref);
	zq_val = readl(&mmdc0->mpzqhwctrl);
	writel(zq_val & ~0x3, &mmdc0->mpzqhwctrl);

	/* 3. increase walat and ralat to maximum */
	rwalat_max = (1 << 6) | (1 << 7) | (1 << 8) | (1 << 16) | (1 << 17);
	setbits_le32(&mmdc0->mdmisc, rwalat_max);
	if (sysinfo->dsize == 2)
		setbits_le32(&mmdc1->mdmisc, rwalat_max);
	/*
	 * 4 & 5. Configure the external DDR device to enter write-leveling
	 * mode through Load Mode Register command.
	 * Register setting:
	 * Bits[31:16] MR1 value (0x0080 write leveling enable)
	 * Bit[9] set WL_EN to enable MMDC DQS output
	 * Bits[6:4] set CMD bits for Load Mode Register programming
	 * Bits[2:0] set CMD_BA to 0x1 for DDR MR1 programming
	 */
	writel(0x00808231, &mmdc0->mdscr);

	/* 6. Activate automatic calibration by setting MPWLGCR[HW_WL_EN] */
	writel(0x00000001, &mmdc0->mpwlgcr);

	/*
	 * 7. Upon completion of this process the MMDC de-asserts
	 * the MPWLGCR[HW_WL_EN]
	 */
	wait_for_bit("MMDC", &mmdc0->mpwlgcr, 1 << 0, 0, 100, 0);

	/*
	 * 8. check for any errors: check both PHYs for x64 configuration,
	 * if x32, check only PHY0
	 */
	if (readl(&mmdc0->mpwlgcr) & 0x00000F00)
		errors |= 1;
	if (sysinfo->dsize == 2)
		if (readl(&mmdc1->mpwlgcr) & 0x00000F00)
			errors |= 2;

	debug("Ending write leveling calibration. Error mask: 0x%x\n", errors);

	/* check to see if cal failed */
	if ((readl(&mmdc0->mpwldectrl0) == 0x001F001F) &&
	    (readl(&mmdc0->mpwldectrl1) == 0x001F001F) &&
	    ((sysinfo->dsize < 2) ||
	     ((readl(&mmdc1->mpwldectrl0) == 0x001F001F) &&
	      (readl(&mmdc1->mpwldectrl1) == 0x001F001F)))) {
		debug("Cal seems to have soft-failed due to memory not supporting write leveling on all channels. Restoring original write leveling values.\n");
		writel(ldectrl[0], &mmdc0->mpwldectrl0);
		writel(ldectrl[1], &mmdc0->mpwldectrl1);
		if (sysinfo->dsize == 2) {
			writel(ldectrl[2], &mmdc1->mpwldectrl0);
			writel(ldectrl[3], &mmdc1->mpwldectrl1);
		}
		errors |= 4;
	}

	/*
	 * User should issue MRS command to exit write leveling mode
	 * through Load Mode Register command
	 * Register setting:
	 * Bits[31:16] MR1 value "ddr_mr1" value from initialization
	 * Bit[9] clear WL_EN to disable MMDC DQS output
	 * Bits[6:4] set CMD bits for Load Mode Register programming
	 * Bits[2:0] set CMD_BA to 0x1 for DDR MR1 programming
	 */
	writel((ddr_mr1 << 16) + 0x8031, &mmdc0->mdscr);

	/* re-enable auto refresh and zq cal */
	writel(esdmisc_val, &mmdc0->mdref);
	writel(zq_val, &mmdc0->mpzqhwctrl);

	debug("\tMMDC_MPWLDECTRL0 after write level cal: 0x%08X\n",
	      readl(&mmdc0->mpwldectrl0));
	debug("\tMMDC_MPWLDECTRL1 after write level cal: 0x%08X\n",
	      readl(&mmdc0->mpwldectrl1));
	if (sysinfo->dsize == 2) {
		debug("\tMMDC_MPWLDECTRL0 after write level cal: 0x%08X\n",
		      readl(&mmdc1->mpwldectrl0));
Loading
Loading full blame…