From patchwork Fri Aug 23 17:24:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Jernej_=C5=A0krabec?= X-Patchwork-Id: 1152314 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=siol.net Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 46FSv31JBKz9sBp for ; Sat, 24 Aug 2019 03:24:29 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 6749AC21C38; Fri, 23 Aug 2019 17:24:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 5DF0DC21C38; Fri, 23 Aug 2019 17:24:19 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 22581C21C38; Fri, 23 Aug 2019 17:24:18 +0000 (UTC) Received: from mail.siol.net (mailoutvs54.siol.net [185.57.226.245]) by lists.denx.de (Postfix) with ESMTPS id BC9BBC21C29 for ; Fri, 23 Aug 2019 17:24:17 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by mail.siol.net (Postfix) with ESMTP id 861ED52420B; Fri, 23 Aug 2019 19:24:16 +0200 (CEST) X-Virus-Scanned: amavisd-new at psrvmta09.zcs-production.pri Received: from mail.siol.net ([127.0.0.1]) by localhost (psrvmta09.zcs-production.pri [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id MTVcUXSAgvjQ; Fri, 23 Aug 2019 19:24:16 +0200 (CEST) Received: from mail.siol.net (localhost [127.0.0.1]) by mail.siol.net (Postfix) with ESMTPS id 082E152420E; Fri, 23 Aug 2019 19:24:16 +0200 (CEST) Received: from localhost.localdomain (cpe-86-58-59-25.static.triera.net [86.58.59.25]) (Authenticated sender: 031275009) by mail.siol.net (Postfix) with ESMTPSA id 3D3FC52420B; Fri, 23 Aug 2019 19:24:15 +0200 (CEST) From: Jernej Skrabec To: jagan@amarulasolutions.com, maxime.ripard@bootlin.com Date: Fri, 23 Aug 2019 19:24:04 +0200 Message-Id: <20190823172404.7207-1-jernej.skrabec@siol.net> X-Mailer: git-send-email 2.23.0 MIME-Version: 1.0 Cc: Andre Przywara , thomas graichen , u-boot@lists.denx.de Subject: [U-Boot] [PATCH v2] sunxi: H6: DRAM: Add support for half DQ X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Half DQ configuration seems to be very rare for H6 based boards/STBs, but exists nevertheless. Currently the only known product which needs this support is Tanix TX6 mini. This commit adds support for half DQ configuration. Code was tested for regressions on other configurations (OrangePi 3 1 GiB/LPDDR3, Tanix TX6 4 GiB/DDR3) and none were found. Thanks to Icenowy Zheng for help with this code. Reviewed-by: Andre Przywara Tested-by: thomas graichen Signed-off-by: Jernej Skrabec Reviewed-by: Maxime Ripard --- Changes from v1: - expanded comment with note that dual rank, half bus width combination was not tested. - added Rb and Tb tags .../include/asm/arch-sunxi/dram_sun50i_h6.h | 1 + arch/arm/mach-sunxi/dram_sun50i_h6.c | 78 +++++++++++++------ 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h index 0a1da02376..49a8a66f7b 100644 --- a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h6.h @@ -315,6 +315,7 @@ struct dram_para { u8 cols; u8 rows; u8 ranks; + u8 bus_full_width; const u8 dx_read_delays[NR_OF_BYTE_LANES][RD_LINES_PER_BYTE_LANE]; const u8 dx_write_delays[NR_OF_BYTE_LANES][WR_LINES_PER_BYTE_LANE]; }; diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 2a8275da3a..9375db76a1 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -201,6 +201,9 @@ static void mctl_set_addrmap(struct dram_para *para) u8 rows = para->rows; u8 ranks = para->ranks; + if (!para->bus_full_width) + cols -= 1; + /* Ranks */ if (ranks == 2) mctl_ctl->addrmap[0] = rows + cols - 3; @@ -213,6 +216,10 @@ static void mctl_set_addrmap(struct dram_para *para) /* Columns */ mctl_ctl->addrmap[2] = 0; switch (cols) { + case 7: + mctl_ctl->addrmap[3] = 0x1F1F1F00; + mctl_ctl->addrmap[4] = 0x1F1F; + break; case 8: mctl_ctl->addrmap[3] = 0x1F1F0000; mctl_ctl->addrmap[4] = 0x1F1F; @@ -300,13 +307,16 @@ static void mctl_com_init(struct dram_para *para) reg_val = 0x3f00; clrsetbits_le32(&mctl_com->unk_0x008, 0x3f00, reg_val); - /* TODO: half DQ, DDR4 */ - reg_val = MSTR_BUSWIDTH_FULL | MSTR_BURST_LENGTH(8) | - MSTR_ACTIVE_RANKS(para->ranks); + /* TODO: DDR4 */ + reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(para->ranks); if (para->type == SUNXI_DRAM_TYPE_LPDDR3) reg_val |= MSTR_DEVICETYPE_LPDDR3; if (para->type == SUNXI_DRAM_TYPE_DDR3) reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE; + if (para->bus_full_width) + reg_val |= MSTR_BUSWIDTH_FULL; + else + reg_val |= MSTR_BUSWIDTH_HALF; writel(reg_val | BIT(31), &mctl_ctl->mstr); if (para->type == SUNXI_DRAM_TYPE_LPDDR3) @@ -333,7 +343,10 @@ static void mctl_com_init(struct dram_para *para) } writel(reg_val, &mctl_ctl->odtcfg); - /* TODO: half DQ */ + if (!para->bus_full_width) { + writel(0x0, &mctl_phy->dx[2].gcr[0]); + writel(0x0, &mctl_phy->dx[3].gcr[0]); + } } static void mctl_bit_delay_set(struct dram_para *para) @@ -514,22 +527,35 @@ static void mctl_channel_init(struct dram_para *para) if (readl(&mctl_phy->pgsr[0]) & 0x400000) { - /* - * Detect single rank. - * TODO: also detect half DQ. - */ + /* Check for single rank and optionally half DQ. */ if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 2 && - (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 2 && - (readl(&mctl_phy->dx[2].rsr[0]) & 0x3) == 2 && - (readl(&mctl_phy->dx[3].rsr[0]) & 0x3) == 2) { + (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 2) { para->ranks = 1; + + if ((readl(&mctl_phy->dx[2].rsr[0]) & 0x3) != 2 || + (readl(&mctl_phy->dx[3].rsr[0]) & 0x3) != 2) + para->bus_full_width = 0; + /* Restart DRAM initialization from scratch. */ mctl_core_init(para); return; } - else { - panic("This DRAM setup is currently not supported.\n"); + + /* + * Check for dual rank and half DQ. NOTE: This combination + * is highly unlikely and was not tested. Condition is the + * same as in libdram, though. + */ + if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 0 && + (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 0) { + para->bus_full_width = 0; + + /* Restart DRAM initialization from scratch. */ + mctl_core_init(para); + return; } + + panic("This DRAM setup is currently not supported.\n"); } if (readl(&mctl_phy->pgsr[0]) & 0xff00000) { @@ -557,11 +583,8 @@ static void mctl_channel_init(struct dram_para *para) static void mctl_auto_detect_dram_size(struct dram_para *para) { - /* TODO: non-LPDDR3, half DQ */ - /* - * Detect rank number by the code in mctl_channel_init. Furtherly - * when DQ detection is available it will also be executed there. - */ + /* TODO: non-(LP)DDR3 */ + /* Detect rank number and half DQ by the code in mctl_channel_init. */ mctl_core_init(para); /* detect row address bits */ @@ -570,8 +593,9 @@ static void mctl_auto_detect_dram_size(struct dram_para *para) mctl_core_init(para); for (para->rows = 13; para->rows < 18; para->rows++) { - /* 8 banks, 8 bit per byte and 32 bit width */ - if (mctl_mem_matches((1 << (para->rows + para->cols + 5)))) + /* 8 banks, 8 bit per byte and 16/32 bit width */ + if (mctl_mem_matches((1 << (para->rows + para->cols + + 4 + para->bus_full_width)))) break; } @@ -580,18 +604,21 @@ static void mctl_auto_detect_dram_size(struct dram_para *para) mctl_core_init(para); for (para->cols = 8; para->cols < 11; para->cols++) { - /* 8 bits per byte and 32 bit width */ - if (mctl_mem_matches(1 << (para->cols + 2))) + /* 8 bits per byte and 16/32 bit width */ + if (mctl_mem_matches(1 << (para->cols + 1 + + para->bus_full_width))) break; } } unsigned long mctl_calc_size(struct dram_para *para) { - /* TODO: non-LPDDR3, half DQ */ + u8 width = para->bus_full_width ? 4 : 2; + + /* TODO: non-(LP)DDR3 */ - /* 8 banks, 32-bit (4 byte) data width */ - return (1ULL << (para->cols + para->rows + 3)) * 4 * para->ranks; + /* 8 banks */ + return (1ULL << (para->cols + para->rows + 3)) * width * para->ranks; } #define SUN50I_H6_LPDDR3_DX_WRITE_DELAYS \ @@ -625,6 +652,7 @@ unsigned long sunxi_dram_init(void) .ranks = 2, .cols = 11, .rows = 14, + .bus_full_width = 1, #ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3 .type = SUNXI_DRAM_TYPE_LPDDR3, .dx_read_delays = SUN50I_H6_LPDDR3_DX_READ_DELAYS,