From patchwork Thu Apr 3 06:01:08 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Harvey X-Patchwork-Id: 336525 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id DE06B140144 for ; Thu, 3 Apr 2014 17:02:56 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 74F0B4BB91; Thu, 3 Apr 2014 08:02:46 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id aQon+Yj1onwc; Thu, 3 Apr 2014 08:02:46 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id D04AC4BB2A; Thu, 3 Apr 2014 08:01:57 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2F7A74BB19 for ; Thu, 3 Apr 2014 08:01:55 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id WlYXtRQBmTNK for ; Thu, 3 Apr 2014 08:01:51 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pb0-f51.google.com (mail-pb0-f51.google.com [209.85.160.51]) by theia.denx.de (Postfix) with ESMTPS id 590A74BB43 for ; Thu, 3 Apr 2014 08:01:28 +0200 (CEST) Received: by mail-pb0-f51.google.com with SMTP id uo5so1315381pbc.24 for ; Wed, 02 Apr 2014 23:01:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Bj/Xww83p5nzTGamqTnzZOQD79iirMhLHk9Opf5oA4c=; b=O52xRps2F1DCqBcLeICGS26aDXD+Mvzt5Tu4zPrfPMZhWMOkYyR6NvSArKE16rY/el X8UdEmlFSmbvWVF7ZaLV/h69QX7x2yjUyE5Wfwf6ZbIZszXn3X2QjStaR9jC3W/AuOA8 gggD9ebWQpjhLAmNEmGQbNwQAvYigf0yviirzDFul+/UqR2pCtNE6snXx5bABykQYd6/ ld5tfp9gF1yPKpEm/5VPPoRLr/QwRq6f50hmG5dXfD4viqnIEy0nv+PAhyDl8tiqQyut JoEa/8P6LkrVpfu9vLq0PfyUQrGjv6tTi/DPFDEgKn1LcNgDfAMblblhgi6fdwVNg+CA lQQA== X-Gm-Message-State: ALoCoQktPLiqTiMbkzJykXOyKHvsoHgOnwfc2MqTAq6SXWBVoReVAGf2fBxSxdNAjotkzRJbn5/u X-Received: by 10.68.178.1 with SMTP id cu1mr5117682pbc.34.1396504886590; Wed, 02 Apr 2014 23:01:26 -0700 (PDT) Received: from tharvey-gw.gw (68-189-91-139.static.snlo.ca.charter.com. [68.189.91.139]) by mx.google.com with ESMTPSA id ug9sm8598838pbc.11.2014.04.02.23.01.25 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 02 Apr 2014 23:01:26 -0700 (PDT) From: Tim Harvey To: Stefano Babic Date: Wed, 2 Apr 2014 23:01:08 -0700 Message-Id: <1396504871-1454-9-git-send-email-tharvey@gateworks.com> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1396504871-1454-1-git-send-email-tharvey@gateworks.com> References: <1396504871-1454-1-git-send-email-tharvey@gateworks.com> Cc: Otavio Salvador , u-boot@lists.denx.de, Tom Rini , Stefan Roese Subject: [U-Boot] [PATCH 08/11] MX6: add mmdc configuration for MX6Q/MX6DL X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de - add mmdc iomux structs and defines - add mmdc ddr3 structs and defines - add function for configuring iomux based on board-specific regs - add function for configuring mmdc based on board-specific and chip-specific data The purpose of these structures and functions is to dynamically configure the IMX MMDC per memory bus frequency (dictated by the CPU type), memory bus size, DDR3 chip characteristics, and board-specific calibration data. An alternative approach would be to simply pass in an entire structure with the values to write to the MMDC registers, however that creates some duplication and propagates 'magic' values which should be able to be calculated. Some of the MMDC registers here are still hard-coded as that is what Freescale recommends by way of their 'DDR3 Script Aid' spreadsheet (DOC-94917) and if need be these can be moved into struct params as well. Additionally if this is agreed to be the right step forward I can put all the shifts and masks for the MMDC registers into #defines in a header. Signed-off-by: Tim Harvey --- arch/arm/cpu/armv7/mx6/Makefile | 1 + arch/arm/cpu/armv7/mx6/ddr.c | 542 ++++++++++++++++++++++++++++++++ arch/arm/include/asm/arch-mx6/mx6-ddr.h | 196 ++++++++++++ 3 files changed, 739 insertions(+) create mode 100644 arch/arm/cpu/armv7/mx6/ddr.c 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 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#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__ */