Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/1421828/?format=api
{ "id": 1421828, "url": "http://patchwork.ozlabs.org/api/patches/1421828/?format=api", "web_url": "http://patchwork.ozlabs.org/project/uboot/patch/20210103092633.36226-12-jernej.skrabec@siol.net/", "project": { "id": 18, "url": "http://patchwork.ozlabs.org/api/projects/18/?format=api", "name": "U-Boot", "link_name": "uboot", "list_id": "u-boot.lists.denx.de", "list_email": "u-boot@lists.denx.de", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20210103092633.36226-12-jernej.skrabec@siol.net>", "list_archive_url": null, "date": "2021-01-03T09:26:27", "name": "[11/17] sunxi: Add H616 DRAM support", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "356fa07b25cea395da5636d9957d6dded2159d48", "submitter": { "id": 70601, "url": "http://patchwork.ozlabs.org/api/people/70601/?format=api", "name": "Jernej Škrabec", "email": "jernej.skrabec@siol.net" }, "delegate": { "id": 114289, "url": "http://patchwork.ozlabs.org/api/users/114289/?format=api", "username": "apritzel", "first_name": "Andre", "last_name": "Przywara", "email": "andre.przywara@arm.com" }, "mbox": "http://patchwork.ozlabs.org/project/uboot/patch/20210103092633.36226-12-jernej.skrabec@siol.net/mbox/", "series": [ { "id": 222511, "url": "http://patchwork.ozlabs.org/api/series/222511/?format=api", "web_url": "http://patchwork.ozlabs.org/project/uboot/list/?series=222511", "date": "2021-01-03T09:26:16", "name": "sunxi: Introduce H616 support", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/222511/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/1421828/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/1421828/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<u-boot-bounces@lists.denx.de>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org", "Authentication-Results": [ "ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de\n (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de;\n envelope-from=u-boot-bounces@lists.denx.de; receiver=<UNKNOWN>)", "ozlabs.org;\n dmarc=fail (p=none dis=none) header.from=siol.net", "phobos.denx.de;\n dmarc=fail (p=none dis=none) header.from=siol.net", "phobos.denx.de;\n spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de", "phobos.denx.de;\n dmarc=pass (p=none dis=none) header.from=siol.net", "phobos.denx.de;\n spf=pass smtp.mailfrom=jernej.skrabec@siol.net" ], "Received": [ "from phobos.denx.de (phobos.denx.de\n [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (4096 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 4D7tkQ4vVqz9sVn\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 3 Jan 2021 20:29:18 +1100 (AEDT)", "from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id 00B3A82697;\n\tSun, 3 Jan 2021 10:27:23 +0100 (CET)", "by phobos.denx.de (Postfix, from userid 109)\n id 0BBB582697; Sun, 3 Jan 2021 10:27:22 +0100 (CET)", "from mail.siol.net (mailoutvs34.siol.net [185.57.226.225])\n (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id 1ABF882689\n for <u-boot@lists.denx.de>; Sun, 3 Jan 2021 10:27:16 +0100 (CET)", "from localhost (localhost [127.0.0.1])\n by mail.siol.net (Zimbra) with ESMTP id C0E63522D7D;\n Sun, 3 Jan 2021 10:27:15 +0100 (CET)", "from mail.siol.net ([127.0.0.1])\n by localhost (psrvmta12.zcs-production.pri [127.0.0.1]) (amavisd-new,\n port 10032)\n with ESMTP id krAgytoyH6A4; Sun, 3 Jan 2021 10:27:14 +0100 (CET)", "from mail.siol.net (localhost [127.0.0.1])\n by mail.siol.net (Zimbra) with ESMTPS id 2689E522D6C;\n Sun, 3 Jan 2021 10:27:14 +0100 (CET)", "from localhost.localdomain (89-212-178-211.dynamic.t-2.net\n [89.212.178.211]) (Authenticated sender: 031275009)\n by mail.siol.net (Zimbra) with ESMTPSA id D42DE522D5F;\n Sun, 3 Jan 2021 10:27:11 +0100 (CET)" ], "X-Spam-Checker-Version": "SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de", "X-Spam-Level": "", "X-Spam-Status": "No, score=-2.6 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW,\n SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2", "From": "Jernej Skrabec <jernej.skrabec@siol.net>", "To": "jagan@amarulasolutions.com,\n\tandre.przywara@arm.com", "Cc": "hdegoede@redhat.com, jernej.skrabec@siol.net, lukma@denx.de, hs@denx.de,\n peng.fan@nxp.com, joe.hershberger@ni.com, jh80.chung@samsung.com,\n u-boot@lists.denx.de, linux-sunxi@googlegroups.com", "Subject": "[PATCH 11/17] sunxi: Add H616 DRAM support", "Date": "Sun, 3 Jan 2021 10:26:27 +0100", "Message-Id": "<20210103092633.36226-12-jernej.skrabec@siol.net>", "X-Mailer": "git-send-email 2.30.0", "In-Reply-To": "<20210103092633.36226-1-jernej.skrabec@siol.net>", "References": "<20210103092633.36226-1-jernej.skrabec@siol.net>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "quoted-printable", "X-BeenThere": "u-boot@lists.denx.de", "X-Mailman-Version": "2.1.34", "Precedence": "list", "List-Id": "U-Boot discussion <u-boot.lists.denx.de>", "List-Unsubscribe": "<https://lists.denx.de/options/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=unsubscribe>", "List-Archive": "<https://lists.denx.de/pipermail/u-boot/>", "List-Post": "<mailto:u-boot@lists.denx.de>", "List-Help": "<mailto:u-boot-request@lists.denx.de?subject=help>", "List-Subscribe": "<https://lists.denx.de/listinfo/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=subscribe>", "Errors-To": "u-boot-bounces@lists.denx.de", "Sender": "\"U-Boot\" <u-boot-bounces@lists.denx.de>", "X-Virus-Scanned": "clamav-milter 0.102.3 at phobos.denx.de", "X-Virus-Status": "Clean" }, "content": "Allwinner H616 supports many types of DRAM. Most notably it supports\nLPDDR4. However, all commercially available boards at this time use\nonly DDR3, so this commit adds only DDR3 support.\n\nController and MBUS are very similar to H6 but PHY is completely\nunknown.\n\nSigned-off-by: Jernej Skrabec <jernej.skrabec@siol.net>\n---\n arch/arm/include/asm/arch-sunxi/dram.h | 2 +\n .../include/asm/arch-sunxi/dram_sun50i_h616.h | 159 +++\n arch/arm/mach-sunxi/Kconfig | 43 +\n arch/arm/mach-sunxi/Makefile | 2 +\n arch/arm/mach-sunxi/dram_sun50i_h616.c | 1023 +++++++++++++++++\n arch/arm/mach-sunxi/dram_timings/Makefile | 2 +\n .../mach-sunxi/dram_timings/h616_ddr3_1333.c | 94 ++\n 7 files changed, 1325 insertions(+)\n create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h\n create mode 100644 arch/arm/mach-sunxi/dram_sun50i_h616.c\n create mode 100644 arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c", "diff": "diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h\nindex 8002b7efdc19..c3b3e1f512b2 100644\n--- a/arch/arm/include/asm/arch-sunxi/dram.h\n+++ b/arch/arm/include/asm/arch-sunxi/dram.h\n@@ -29,6 +29,8 @@\n #include <asm/arch/dram_sun9i.h>\n #elif defined(CONFIG_MACH_SUN50I_H6)\n #include <asm/arch/dram_sun50i_h6.h>\n+#elif defined(CONFIG_MACH_SUN50I_H616)\n+#include <asm/arch/dram_sun50i_h616.h>\n #else\n #include <asm/arch/dram_sun4i.h>\n #endif\ndiff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h\nnew file mode 100644\nindex 000000000000..5d105afd6110\n--- /dev/null\n+++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_h616.h\n@@ -0,0 +1,159 @@\n+/*\n+ * H616 dram controller register and constant defines\n+ *\n+ * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>\n+ *\n+ * Based on H6 one, which is:\n+ * (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>\n+ *\n+ * SPDX-License-Identifier:\tGPL-2.0+\n+ */\n+\n+#ifndef _SUNXI_DRAM_SUN50I_H616_H\n+#define _SUNXI_DRAM_SUN50I_H616_H\n+\n+#include <stdbool.h>\n+#ifndef __ASSEMBLY__\n+#include <linux/bitops.h>\n+#endif\n+\n+enum sunxi_dram_type {\n+\tSUNXI_DRAM_TYPE_DDR3 = 3,\n+\tSUNXI_DRAM_TYPE_DDR4,\n+\tSUNXI_DRAM_TYPE_LPDDR3 = 7,\n+\tSUNXI_DRAM_TYPE_LPDDR4\n+};\n+\n+/* MBUS part is largely the same as in H6, except for one special register */\n+struct sunxi_mctl_com_reg {\n+\tu32 cr;\t\t\t/* 0x000 control register */\n+\tu8 reserved_0x004[4];\t/* 0x004 */\n+\tu32 unk_0x008;\t\t/* 0x008 */\n+\tu32 tmr;\t\t/* 0x00c timer register */\n+\tu8 reserved_0x010[4];\t/* 0x010 */\n+\tu32 unk_0x014;\t\t/* 0x014 */\n+\tu8 reserved_0x018[8];\t/* 0x018 */\n+\tu32 maer0;\t\t/* 0x020 master enable register 0 */\n+\tu32 maer1;\t\t/* 0x024 master enable register 1 */\n+\tu32 maer2;\t\t/* 0x028 master enable register 2 */\n+\tu8 reserved_0x02c[468];\t/* 0x02c */\n+\tu32 bwcr;\t\t/* 0x200 bandwidth control register */\n+\tu8 reserved_0x204[12];\t/* 0x204 */\n+\t/*\n+\t * The last master configured by BSP libdram is at 0x49x, so the\n+\t * size of this struct array is set to 41 (0x29) now.\n+\t */\n+\tstruct {\n+\t\tu32 cfg0;\t\t/* 0x0 */\n+\t\tu32 cfg1;\t\t/* 0x4 */\n+\t\tu8 reserved_0x8[8];\t/* 0x8 */\n+\t} master[41];\t\t/* 0x210 + index * 0x10 */\n+\tu8 reserved_0x4a0[96];\t/* 0x4a0 */\n+\tu32 unk_0x500;\t\t/* 0x500 */\n+};\n+check_member(sunxi_mctl_com_reg, unk_0x500, 0x500);\n+\n+/*\n+ * Controller registers seems to be the same or at least very similar\n+ * to those in H6.\n+ */\n+struct sunxi_mctl_ctl_reg {\n+\tu32 mstr;\t\t/* 0x000 */\n+\tu32 statr;\t\t/* 0x004 unused */\n+\tu32 mstr1;\t\t/* 0x008 unused */\n+\tu32 unk_0x00c;\t\t/* 0x00c */\n+\tu32 mrctrl0;\t\t/* 0x010 unused */\n+\tu32 mrctrl1;\t\t/* 0x014 unused */\n+\tu32 mrstatr;\t\t/* 0x018 unused */\n+\tu32 mrctrl2;\t\t/* 0x01c unused */\n+\tu32 derateen;\t\t/* 0x020 unused */\n+\tu32 derateint;\t\t/* 0x024 unused */\n+\tu8 reserved_0x028[8];\t/* 0x028 */\n+\tu32 pwrctl;\t\t/* 0x030 unused */\n+\tu32 pwrtmg;\t\t/* 0x034 unused */\n+\tu32 hwlpctl;\t\t/* 0x038 unused */\n+\tu8 reserved_0x03c[20];\t/* 0x03c */\n+\tu32 rfshctl0;\t\t/* 0x050 unused */\n+\tu32 rfshctl1;\t\t/* 0x054 unused */\n+\tu8 reserved_0x058[8];\t/* 0x05c */\n+\tu32 rfshctl3;\t\t/* 0x060 */\n+\tu32 rfshtmg;\t\t/* 0x064 */\n+\tu8 reserved_0x068[104];\t/* 0x068 */\n+\tu32 init[8];\t\t/* 0x0d0 */\n+\tu32 dimmctl;\t\t/* 0x0f0 unused */\n+\tu32 rankctl;\t\t/* 0x0f4 */\n+\tu8 reserved_0x0f8[8];\t/* 0x0f8 */\n+\tu32 dramtmg[17];\t/* 0x100 */\n+\tu8 reserved_0x144[60];\t/* 0x144 */\n+\tu32 zqctl[3];\t\t/* 0x180 */\n+\tu32 zqstat;\t\t/* 0x18c unused */\n+\tu32 dfitmg0;\t\t/* 0x190 */\n+\tu32 dfitmg1;\t\t/* 0x194 */\n+\tu32 dfilpcfg[2];\t/* 0x198 unused */\n+\tu32 dfiupd[3];\t\t/* 0x1a0 */\n+\tu32 reserved_0x1ac;\t/* 0x1ac */\n+\tu32 dfimisc;\t\t/* 0x1b0 */\n+\tu32 dfitmg2;\t\t/* 0x1b4 unused */\n+\tu32 dfitmg3;\t\t/* 0x1b8 unused */\n+\tu32 dfistat;\t\t/* 0x1bc */\n+\tu32 dbictl;\t\t/* 0x1c0 */\n+\tu8 reserved_0x1c4[60];\t/* 0x1c4 */\n+\tu32 addrmap[12];\t/* 0x200 */\n+\tu8 reserved_0x230[16];\t/* 0x230 */\n+\tu32 odtcfg;\t\t/* 0x240 */\n+\tu32 odtmap;\t\t/* 0x244 */\n+\tu8 reserved_0x248[8];\t/* 0x248 */\n+\tu32 sched[2];\t\t/* 0x250 */\n+\tu8 reserved_0x258[180];\t/* 0x258 */\n+\tu32 dbgcmd;\t\t/* 0x30c unused */\n+\tu32 dbgstat;\t\t/* 0x310 unused */\n+\tu8 reserved_0x314[12];\t/* 0x314 */\n+\tu32 swctl;\t\t/* 0x320 */\n+\tu32 swstat;\t\t/* 0x324 */\n+\tu8 reserved_0x328[7768];/* 0x328 */\n+\tu32 unk_0x2180;\t\t/* 0x2180 */\n+\tu8 reserved_0x2184[188];/* 0x2184 */\n+\tu32 unk_0x2240;\t\t/* 0x2240 */\n+\tu8 reserved_0x2244[3900];/* 0x2244 */\n+\tu32 unk_0x3180;\t\t/* 0x3180 */\n+\tu8 reserved_0x3184[188];/* 0x3184 */\n+\tu32 unk_0x3240;\t\t/* 0x3240 */\n+\tu8 reserved_0x3244[3900];/* 0x3244 */\n+\tu32 unk_0x4180;\t\t/* 0x4180 */\n+\tu8 reserved_0x4184[188];/* 0x4184 */\n+\tu32 unk_0x4240;\t\t/* 0x4240 */\n+};\n+check_member(sunxi_mctl_ctl_reg, swstat, 0x324);\n+check_member(sunxi_mctl_ctl_reg, unk_0x4240, 0x4240);\n+\n+#define MSTR_DEVICETYPE_DDR3\tBIT(0)\n+#define MSTR_DEVICETYPE_LPDDR2\tBIT(2)\n+#define MSTR_DEVICETYPE_LPDDR3\tBIT(3)\n+#define MSTR_DEVICETYPE_DDR4\tBIT(4)\n+#define MSTR_DEVICETYPE_MASK\tGENMASK(5, 0)\n+#define MSTR_2TMODE\t\tBIT(10)\n+#define MSTR_BUSWIDTH_FULL\t(0 << 12)\n+#define MSTR_BUSWIDTH_HALF\t(1 << 12)\n+#define MSTR_ACTIVE_RANKS(x)\t(((x == 2) ? 3 : 1) << 24)\n+#define MSTR_BURST_LENGTH(x)\t(((x) >> 1) << 16)\n+\n+struct dram_para {\n+\tu32 clk;\n+\tenum sunxi_dram_type type;\n+\tu8 cols;\n+\tu8 rows;\n+\tu8 ranks;\n+\tu8 bus_full_width;\n+};\n+\n+\n+static inline int ns_to_t(int nanoseconds)\n+{\n+\tconst unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2;\n+\n+\treturn DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000);\n+}\n+\n+void mctl_set_timing_params(struct dram_para *para);\n+\n+#endif /* _SUNXI_DRAM_SUN50I_H616_H */\ndiff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig\nindex 69ab670abf39..1a5497989f04 100644\n--- a/arch/arm/mach-sunxi/Kconfig\n+++ b/arch/arm/mach-sunxi/Kconfig\n@@ -48,6 +48,46 @@ config DRAM_SUN50I_H6\n \t Select this dram controller driver for some sun50i platforms,\n \t like H6.\n \n+config DRAM_SUN50I_H616\n+\tbool\n+\thelp\n+\t Select this dram controller driver for some sun50i platforms,\n+\t like H616.\n+\n+if DRAM_SUN50I_H616\n+config DRAM_SUN50I_H616_WRITE_LEVELING\n+\tbool \"H616 DRAM write leveling\"\n+\t---help---\n+\t Select this when DRAM on your H616 board needs write leveling.\n+\n+config DRAM_SUN50I_H616_READ_CALIBRATION\n+\tbool\"H616 DRAM read calibration\"\n+\t---help---\n+\t Select this when DRAM on your H616 board needs read calibration.\n+\n+config DRAM_SUN50I_H616_READ_TRAINING\n+\tbool \"H616 DRAM read training\"\n+\t---help---\n+\t Select this when DRAM on your H616 board needs read training.\n+\n+config DRAM_SUN50I_H616_WRITE_TRAINING\n+\tbool \"H616 DRAM write training\"\n+\t---help---\n+\t Select this when DRAM on your H616 board needs write training.\n+\n+config DRAM_SUN50I_H616_BIT_DELAY_COMPENSATION\n+\tbool \"H616 DRAM bit delay compensation\"\n+\t---help---\n+\t Select this when DRAM on your H616 board needs bit delay\n+\t compensation.\n+\n+config DRAM_SUN50I_H616_UNKNOWN_FEATURE\n+\tbool \"H616 DRAM unknown feature\"\n+\t---help---\n+\t Select this when DRAM on your H616 board needs this unknown\n+\t feature.\n+endif\n+\n config SUN6I_P2WI\n \tbool \"Allwinner sun6i internal P2WI controller\"\n \thelp\n@@ -423,6 +463,7 @@ config DRAM_CLK\n \t\t MACH_SUN8I_V3S\n \tdefault 672 if MACH_SUN50I\n \tdefault 744 if MACH_SUN50I_H6\n+\tdefault 720 if MACH_SUN50I_H616\n \t---help---\n \tSet the dram clock speed, valid range 240 - 480 (prior to sun9i),\n \tmust be a multiple of 24. For the sun9i (A80), the tested values\n@@ -439,6 +480,7 @@ endif\n \n config DRAM_ZQ\n \tint \"sunxi dram zq value\"\n+\tdepends on !MACH_SUN50I_H616\n \tdefault 123 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || \\\n \t\t MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_A83T\n \tdefault 127 if MACH_SUN7I\n@@ -456,6 +498,7 @@ config DRAM_ODT_EN\n \tdefault y if MACH_SUN8I_R40\n \tdefault y if MACH_SUN50I\n \tdefault y if MACH_SUN50I_H6\n+\tdefault y if MACH_SUN50I_H616\n \t---help---\n \tSelect this to enable dram odt (on die termination).\n \ndiff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile\nindex b8aca43d6630..3f081d92f379 100644\n--- a/arch/arm/mach-sunxi/Makefile\n+++ b/arch/arm/mach-sunxi/Makefile\n@@ -40,4 +40,6 @@ obj-$(CONFIG_SUNXI_DRAM_DW)\t+= dram_sunxi_dw.o\n obj-$(CONFIG_SUNXI_DRAM_DW)\t+= dram_timings/\n obj-$(CONFIG_DRAM_SUN50I_H6)\t+= dram_sun50i_h6.o\n obj-$(CONFIG_DRAM_SUN50I_H6)\t+= dram_timings/\n+obj-$(CONFIG_DRAM_SUN50I_H616)\t+= dram_sun50i_h616.o\n+obj-$(CONFIG_DRAM_SUN50I_H616)\t+= dram_timings/\n endif\ndiff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c\nnew file mode 100644\nindex 000000000000..0c86f54b8a73\n--- /dev/null\n+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c\n@@ -0,0 +1,1023 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * sun50i H616 platform dram controller driver\n+ *\n+ * While controller is very similar to that in H6, PHY is completely\n+ * unknown. That's why this driver has plenty of magic numbers. Some\n+ * meaning was nevertheless deduced from strings found in boot0 and\n+ * known meaning of some dram parameters.\n+ * This driver only supports DDR3 memory and omits logic for all\n+ * other supported types supported by hardware.\n+ *\n+ * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>\n+ *\n+ */\n+#include <common.h>\n+#include <init.h>\n+#include <log.h>\n+#include <asm/io.h>\n+#include <asm/arch/clock.h>\n+#include <asm/arch/dram.h>\n+#include <asm/arch/cpu.h>\n+#include <linux/bitops.h>\n+#include <linux/delay.h>\n+#include <linux/kconfig.h>\n+\n+enum {\n+\tMBUS_QOS_LOWEST = 0,\n+\tMBUS_QOS_LOW,\n+\tMBUS_QOS_HIGH,\n+\tMBUS_QOS_HIGHEST\n+};\n+\n+inline void mbus_configure_port(u8 port,\n+\t\t\t\tbool bwlimit,\n+\t\t\t\tbool priority,\n+\t\t\t\tu8 qos,\n+\t\t\t\tu8 waittime,\n+\t\t\t\tu8 acs,\n+\t\t\t\tu16 bwl0,\n+\t\t\t\tu16 bwl1,\n+\t\t\t\tu16 bwl2)\n+{\n+\tstruct sunxi_mctl_com_reg * const mctl_com =\n+\t\t\t(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;\n+\n+\tconst u32 cfg0 = ( (bwlimit ? (1 << 0) : 0)\n+\t\t\t | (priority ? (1 << 1) : 0)\n+\t\t\t | ((qos & 0x3) << 2)\n+\t\t\t | ((waittime & 0xf) << 4)\n+\t\t\t | ((acs & 0xff) << 8)\n+\t\t\t | (bwl0 << 16) );\n+\tconst u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff);\n+\n+\tdebug(\"MBUS port %d cfg0 %08x cfg1 %08x\\n\", port, cfg0, cfg1);\n+\twritel_relaxed(cfg0, &mctl_com->master[port].cfg0);\n+\twritel_relaxed(cfg1, &mctl_com->master[port].cfg1);\n+}\n+\n+#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2)\t\\\n+\tmbus_configure_port(port, bwlimit, false, \\\n+\t\t\t MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2)\n+\n+static void mctl_set_master_priority(void)\n+{\n+\tstruct sunxi_mctl_com_reg * const mctl_com =\n+\t\t\t(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;\n+\n+\t/* enable bandwidth limit windows and set windows size 1us */\n+\twritel(399, &mctl_com->tmr);\n+\twritel(BIT(16), &mctl_com->bwcr);\n+\n+\tMBUS_CONF( 0, true, HIGHEST, 0, 256, 128, 100);\n+\tMBUS_CONF( 1, true, HIGH, 0, 1536, 1400, 256);\n+\tMBUS_CONF( 2, true, HIGHEST, 0, 512, 256, 96);\n+\tMBUS_CONF( 3, true, HIGH, 0, 256, 100, 80);\n+\tMBUS_CONF( 4, true, HIGH, 2, 8192, 5500, 5000);\n+\tMBUS_CONF( 5, true, HIGH, 2, 100, 64, 32);\n+\tMBUS_CONF( 6, true, HIGH, 2, 100, 64, 32);\n+\tMBUS_CONF( 8, true, HIGH, 0, 256, 128, 64);\n+\tMBUS_CONF(11, true, HIGH, 0, 256, 128, 100);\n+\tMBUS_CONF(14, true, HIGH, 0, 1024, 256, 64);\n+\tMBUS_CONF(16, true, HIGHEST, 6, 8192, 2800, 2400);\n+\tMBUS_CONF(21, true, HIGHEST, 6, 2048, 768, 512);\n+\tMBUS_CONF(25, true, HIGHEST, 0, 100, 64, 32);\n+\tMBUS_CONF(26, true, HIGH, 2, 8192, 5500, 5000);\n+\tMBUS_CONF(37, true, HIGH, 0, 256, 128, 64);\n+\tMBUS_CONF(38, true, HIGH, 2, 100, 64, 32);\n+\tMBUS_CONF(39, true, HIGH, 2, 8192, 5500, 5000);\n+\tMBUS_CONF(40, true, HIGH, 2, 100, 64, 32);\n+\n+\tdmb();\n+}\n+\n+static void mctl_sys_init(struct dram_para *para)\n+{\n+\tstruct sunxi_ccm_reg * const ccm =\n+\t\t\t(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;\n+\tstruct sunxi_mctl_com_reg * const mctl_com =\n+\t\t\t(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;\n+\tstruct sunxi_mctl_ctl_reg * const mctl_ctl =\n+\t\t\t(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;\n+\n+\t/* Put all DRAM-related blocks to reset state */\n+\tclrbits_le32(&ccm->mbus_cfg, MBUS_ENABLE);\n+\tclrbits_le32(&ccm->mbus_cfg, MBUS_RESET);\n+\tclrbits_le32(&ccm->dram_gate_reset, BIT(GATE_SHIFT));\n+\tudelay(5);\n+\tclrbits_le32(&ccm->dram_gate_reset, BIT(RESET_SHIFT));\n+\tclrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);\n+\tclrbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);\n+\n+\tudelay(5);\n+\n+\t/* Set PLL5 rate to doubled DRAM clock rate */\n+\twritel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN | CCM_PLL5_OUT_EN |\n+\t CCM_PLL5_CTRL_N(para->clk * 2 / 24 - 1), &ccm->pll5_cfg);\n+\tmctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK);\n+\n+\t/* Configure DRAM mod clock */\n+\twritel(DRAM_CLK_SRC_PLL5, &ccm->dram_clk_cfg);\n+\twritel(BIT(RESET_SHIFT), &ccm->dram_gate_reset);\n+\tudelay(5);\n+\tsetbits_le32(&ccm->dram_gate_reset, BIT(GATE_SHIFT));\n+\n+\t/* Disable all channels */\n+\twritel(0, &mctl_com->maer0);\n+\twritel(0, &mctl_com->maer1);\n+\twritel(0, &mctl_com->maer2);\n+\n+\t/* Configure MBUS and enable DRAM mod reset */\n+\tsetbits_le32(&ccm->mbus_cfg, MBUS_RESET);\n+\tsetbits_le32(&ccm->mbus_cfg, MBUS_ENABLE);\n+\n+\tclrbits_le32(&mctl_com->unk_0x500, BIT(25));\n+\n+\tsetbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);\n+\tudelay(5);\n+\n+\t/* Unknown hack, which enables access of mctl_ctl regs */\n+\twritel(0x8000, &mctl_ctl->unk_0x00c);\n+}\n+\n+static void mctl_set_addrmap(struct dram_para *para)\n+{\n+\tstruct sunxi_mctl_ctl_reg * const mctl_ctl =\n+\t\t\t(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;\n+\tu8 cols = para->cols;\n+\tu8 rows = para->rows;\n+\tu8 ranks = para->ranks;\n+\n+\tif (!para->bus_full_width)\n+\t\tcols -= 1;\n+\n+\t/* Ranks */\n+\tif (ranks == 2)\n+\t\tmctl_ctl->addrmap[0] = rows + cols - 3;\n+\telse\n+\t\tmctl_ctl->addrmap[0] = 0x1F;\n+\n+\t/* Banks, hardcoded to 8 banks now */\n+\tmctl_ctl->addrmap[1] = (cols - 2) | (cols - 2) << 8 | (cols - 2) << 16;\n+\n+\t/* Columns */\n+\tmctl_ctl->addrmap[2] = 0;\n+\tswitch (cols) {\n+\tcase 7:\n+\t\tmctl_ctl->addrmap[3] = 0x1F1F1F00;\n+\t\tmctl_ctl->addrmap[4] = 0x1F1F;\n+\t\tbreak;\n+\tcase 8:\n+\t\tmctl_ctl->addrmap[3] = 0x1F1F0000;\n+\t\tmctl_ctl->addrmap[4] = 0x1F1F;\n+\t\tbreak;\n+\tcase 9:\n+\t\tmctl_ctl->addrmap[3] = 0x1F000000;\n+\t\tmctl_ctl->addrmap[4] = 0x1F1F;\n+\t\tbreak;\n+\tcase 10:\n+\t\tmctl_ctl->addrmap[3] = 0;\n+\t\tmctl_ctl->addrmap[4] = 0x1F1F;\n+\t\tbreak;\n+\tcase 11:\n+\t\tmctl_ctl->addrmap[3] = 0;\n+\t\tmctl_ctl->addrmap[4] = 0x1F00;\n+\t\tbreak;\n+\tcase 12:\n+\t\tmctl_ctl->addrmap[3] = 0;\n+\t\tmctl_ctl->addrmap[4] = 0;\n+\t\tbreak;\n+\tdefault:\n+\t\tpanic(\"Unsupported DRAM configuration: column number invalid\\n\");\n+\t}\n+\n+\t/* Rows */\n+\tmctl_ctl->addrmap[5] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);\n+\tswitch (rows) {\n+\tcase 13:\n+\t\tmctl_ctl->addrmap[6] = (cols - 3) | 0x0F0F0F00;\n+\t\tmctl_ctl->addrmap[7] = 0x0F0F;\n+\t\tbreak;\n+\tcase 14:\n+\t\tmctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | 0x0F0F0000;\n+\t\tmctl_ctl->addrmap[7] = 0x0F0F;\n+\t\tbreak;\n+\tcase 15:\n+\t\tmctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | 0x0F000000;\n+\t\tmctl_ctl->addrmap[7] = 0x0F0F;\n+\t\tbreak;\n+\tcase 16:\n+\t\tmctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);\n+\t\tmctl_ctl->addrmap[7] = 0x0F0F;\n+\t\tbreak;\n+\tcase 17:\n+\t\tmctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);\n+\t\tmctl_ctl->addrmap[7] = (cols - 3) | 0x0F00;\n+\t\tbreak;\n+\tcase 18:\n+\t\tmctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);\n+\t\tmctl_ctl->addrmap[7] = (cols - 3) | ((cols - 3) << 8);\n+\t\tbreak;\n+\tdefault:\n+\t\tpanic(\"Unsupported DRAM configuration: row number invalid\\n\");\n+\t}\n+\n+\t/* Bank groups, DDR4 only */\n+\tmctl_ctl->addrmap[8] = 0x3F3F;\n+}\n+\n+static const u8 phy_init[] = {\n+\t0x07, 0x0b, 0x02, 0x16, 0x0d, 0x0e, 0x14, 0x19,\n+\t0x0a, 0x15, 0x03, 0x13, 0x04, 0x0c, 0x10, 0x06,\n+\t0x0f, 0x11, 0x1a, 0x01, 0x12, 0x17, 0x00, 0x08,\n+\t0x09, 0x05, 0x18\n+};\n+\n+static void mctl_phy_configure_odt(void)\n+{\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x388);\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x38c);\n+\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x3c8);\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x3cc);\n+\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x408);\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x40c);\n+\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x448);\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x44c);\n+\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x340);\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x344);\n+\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x348);\n+\twritel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x34c);\n+\n+\twritel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x380);\n+\twritel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x384);\n+\n+\twritel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x3c0);\n+\twritel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x3c4);\n+\n+\twritel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x400);\n+\twritel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x404);\n+\n+\twritel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x440);\n+\twritel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x444);\n+\n+\tdmb();\n+}\n+\n+static bool mctl_phy_write_leveling(struct dram_para *para)\n+{\n+\tbool result = true;\n+\tu32 val;\n+\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0, 0x80);\n+\twritel(4, SUNXI_DRAM_PHY0_BASE + 0xc);\n+\twritel(0x40, SUNXI_DRAM_PHY0_BASE + 0x10);\n+\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4);\n+\n+\tif (para->bus_full_width)\n+\t\tval = 0xf;\n+\telse\n+\t\tval = 3;\n+\n+\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x188), val, val);\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4);\n+\n+\tval = readl(SUNXI_DRAM_PHY0_BASE + 0x258);\n+\tif (val == 0 || val == 0x3f)\n+\t\tresult = false;\n+\tval = readl(SUNXI_DRAM_PHY0_BASE + 0x25c);\n+\tif (val == 0 || val == 0x3f)\n+\t\tresult = false;\n+\tval = readl(SUNXI_DRAM_PHY0_BASE + 0x318);\n+\tif (val == 0 || val == 0x3f)\n+\t\tresult = false;\n+\tval = readl(SUNXI_DRAM_PHY0_BASE + 0x31c);\n+\tif (val == 0 || val == 0x3f)\n+\t\tresult = false;\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0);\n+\n+\tif (para->ranks == 2) {\n+\t\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0, 0x40);\n+\n+\t\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4);\n+\n+\t\tif (para->bus_full_width)\n+\t\t\tval = 0xf;\n+\t\telse\n+\t\t\tval = 3;\n+\n+\t\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x188), val, val);\n+\n+\t\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4);\n+\t}\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0);\n+\n+\treturn result;\n+}\n+\n+static bool mctl_phy_read_calibration(struct dram_para *para)\n+{\n+\tbool result = true;\n+\tu32 val, tmp;\n+\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x20);\n+\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);\n+\n+\tif (para->bus_full_width)\n+\t\tval = 0xf;\n+\telse\n+\t\tval = 3;\n+\n+\twhile ((readl(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != val) {\n+\t\tif (readl(SUNXI_DRAM_PHY0_BASE + 0x184) & 0x20) {\n+\t\t\tresult = false;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30);\n+\n+\tif (para->ranks == 2) {\n+\t\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x10);\n+\n+\t\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);\n+\n+\t\twhile ((readl(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != val) {\n+\t\t\tif (readl(SUNXI_DRAM_PHY0_BASE + 0x184) & 0x20) {\n+\t\t\t\tresult = false;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);\n+\t}\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30);\n+\n+\tval = readl(SUNXI_DRAM_PHY0_BASE + 0x274) & 7;\n+\ttmp = readl(SUNXI_DRAM_PHY0_BASE + 0x26c) & 7;\n+\tif (val < tmp)\n+\t\tval = tmp;\n+\ttmp = readl(SUNXI_DRAM_PHY0_BASE + 0x32c) & 7;\n+\tif (val < tmp)\n+\t\tval = tmp;\n+\ttmp = readl(SUNXI_DRAM_PHY0_BASE + 0x334) & 7;\n+\tif (val < tmp)\n+\t\tval = tmp;\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x38, 0x7, (val + 2) & 7);\n+\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x20);\n+\n+\treturn result;\n+}\n+\n+static bool mctl_phy_read_training(struct dram_para *para)\n+{\n+\tu32 val1, val2, *ptr1, *ptr2;\n+\tbool result = true;\n+\tint i;\n+\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3, 2);\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x804, 0x3f, 0xf);\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x808, 0x3f, 0xf);\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xa04, 0x3f, 0xf);\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xa08, 0x3f, 0xf);\n+\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 6);\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 1);\n+\n+\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x840), 0xc, 0xc);\n+\tif (readl(SUNXI_DRAM_PHY0_BASE + 0x840) & 3)\n+\t\tresult = false;\n+\n+\tif (para->bus_full_width) {\n+\t\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0xa40), 0xc, 0xc);\n+\t\tif (readl(SUNXI_DRAM_PHY0_BASE + 0xa40) & 3)\n+\t\t\tresult = false;\n+\t}\n+\n+\tptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x898);\n+\tptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x850);\n+\tfor (i = 0; i < 9; i++) {\n+\t\tval1 = readl(&ptr1[i]);\n+\t\tval2 = readl(&ptr2[i]);\n+\t\tif (val1 - val2 <= 6)\n+\t\t\tresult = false;\n+\t}\n+\tptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x8bc);\n+\tptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x874);\n+\tfor (i = 0; i < 9; i++) {\n+\t\tval1 = readl(&ptr1[i]);\n+\t\tval2 = readl(&ptr2[i]);\n+\t\tif (val1 - val2 <= 6)\n+\t\t\tresult = false;\n+\t}\n+\n+\tif (para->bus_full_width) {\n+\t\tptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xa98);\n+\t\tptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xa50);\n+\t\tfor (i = 0; i < 9; i++) {\n+\t\t\tval1 = readl(&ptr1[i]);\n+\t\t\tval2 = readl(&ptr2[i]);\n+\t\t\tif (val1 - val2 <= 6)\n+\t\t\t\tresult = false;\n+\t\t}\n+\n+\t\tptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xabc);\n+\t\tptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xa74);\n+\t\tfor (i = 0; i < 9; i++) {\n+\t\t\tval1 = readl(&ptr1[i]);\n+\t\t\tval2 = readl(&ptr2[i]);\n+\t\t\tif (val1 - val2 <= 6)\n+\t\t\t\tresult = false;\n+\t\t}\n+\t}\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 3);\n+\n+\tif (para->ranks == 2) {\n+\t\t/* maybe last parameter should be 1? */\n+\t\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3, 2);\n+\n+\t\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 6);\n+\t\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 1);\n+\n+\t\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x840), 0xc, 0xc);\n+\t\tif (readl(SUNXI_DRAM_PHY0_BASE + 0x840) & 3)\n+\t\t\tresult = false;\n+\n+\t\tif (para->bus_full_width) {\n+\t\t\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0xa40), 0xc, 0xc);\n+\t\t\tif (readl(SUNXI_DRAM_PHY0_BASE + 0xa40) & 3)\n+\t\t\t\tresult = false;\n+\t\t}\n+\n+\t\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 3);\n+\t}\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3);\n+\n+\treturn result;\n+}\n+\n+static bool mctl_phy_write_training(struct dram_para *para)\n+{\n+\tu32 val1, val2, *ptr1, *ptr2;\n+\tbool result = true;\n+\tint i;\n+\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x134);\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x138);\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x19c);\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x1a0);\n+\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc, 8);\n+\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10);\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x20);\n+\n+\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x8e0), 3, 3);\n+\tif (readl(SUNXI_DRAM_PHY0_BASE + 0x8e0) & 0xc)\n+\t\tresult = false;\n+\n+\tif (para->bus_full_width) {\n+\t\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0xae0), 3, 3);\n+\t\tif (readl(SUNXI_DRAM_PHY0_BASE + 0xae0) & 0xc)\n+\t\t\tresult = false;\n+\t}\n+\n+\tptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x938);\n+\tptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x8f0);\n+\tfor (i = 0; i < 9; i++) {\n+\t\tval1 = readl(&ptr1[i]);\n+\t\tval2 = readl(&ptr2[i]);\n+\t\tif (val1 - val2 <= 6)\n+\t\t\tresult = false;\n+\t}\n+\tptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x95c);\n+\tptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x914);\n+\tfor (i = 0; i < 9; i++) {\n+\t\tval1 = readl(&ptr1[i]);\n+\t\tval2 = readl(&ptr2[i]);\n+\t\tif (val1 - val2 <= 6)\n+\t\t\tresult = false;\n+\t}\n+\n+\tif (para->bus_full_width) {\n+\t\tptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xb38);\n+\t\tptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xaf0);\n+\t\tfor (i = 0; i < 9; i++) {\n+\t\t\tval1 = readl(&ptr1[i]);\n+\t\t\tval2 = readl(&ptr2[i]);\n+\t\t\tif (val1 - val2 <= 6)\n+\t\t\t\tresult = false;\n+\t\t}\n+\t\tptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xb5c);\n+\t\tptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xb14);\n+\t\tfor (i = 0; i < 9; i++) {\n+\t\t\tval1 = readl(&ptr1[i]);\n+\t\t\tval2 = readl(&ptr2[i]);\n+\t\t\tif (val1 - val2 <= 6)\n+\t\t\t\tresult = false;\n+\t\t}\n+\t}\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x60);\n+\n+\tif (para->ranks == 2) {\n+\t\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc, 4);\n+\n+\t\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10);\n+\t\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x20);\n+\n+\t\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x8e0), 3, 3);\n+\t\tif (readl(SUNXI_DRAM_PHY0_BASE + 0x8e0) & 0xc)\n+\t\t\tresult = false;\n+\n+\t\tif (para->bus_full_width) {\n+\t\t\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0xae0), 3, 3);\n+\t\t\tif (readl(SUNXI_DRAM_PHY0_BASE + 0xae0) & 0xc)\n+\t\t\t\tresult = false;\n+\t\t}\n+\n+\t\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x60);\n+\t}\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc);\n+\n+\treturn result;\n+}\n+\n+static bool mctl_phy_bit_delay_compensation(struct dram_para *para)\n+{\n+\tu32 *ptr;\n+\tint i;\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1);\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 8);\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x484);\n+\tfor (i = 0; i < 9; i++) {\n+\t\twritel_relaxed(0x16, ptr);\n+\t\twritel_relaxed(0x16, ptr + 0x30);\n+\t\tptr += 2;\n+\t}\n+\twritel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x4d0);\n+\twritel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x590);\n+\twritel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x4cc);\n+\twritel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x58c);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x4d8);\n+\tfor (i = 0; i < 9; i++) {\n+\t\twritel_relaxed(0x1a, ptr);\n+\t\twritel_relaxed(0x1a, ptr + 0x30);\n+\t\tptr += 2;\n+\t}\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x524);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x5e4);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x520);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x5e0);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x604);\n+\tfor (i = 0; i < 9; i++) {\n+\t\twritel_relaxed(0x1a, ptr);\n+\t\twritel_relaxed(0x1a, ptr + 0x30);\n+\t\tptr += 2;\n+\t}\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x650);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x710);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x64c);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x70c);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x658);\n+\tfor (i = 0; i < 9; i++) {\n+\t\twritel_relaxed(0x1a, ptr);\n+\t\twritel_relaxed(0x1a, ptr + 0x30);\n+\t\tptr += 2;\n+\t}\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x6a4);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x764);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x6a0);\n+\twritel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x760);\n+\n+\tdmb();\n+\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1);\n+\n+\t/* second part */\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, 0x80);\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 4);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x480);\n+\tfor (i = 0; i < 9; i++) {\n+\t\twritel_relaxed(0x10, ptr);\n+\t\twritel_relaxed(0x10, ptr + 0x30);\n+\t\tptr += 2;\n+\t}\n+\twritel_relaxed(0x18, SUNXI_DRAM_PHY0_BASE + 0x528);\n+\twritel_relaxed(0x18, SUNXI_DRAM_PHY0_BASE + 0x5e8);\n+\twritel_relaxed(0x18, SUNXI_DRAM_PHY0_BASE + 0x4c8);\n+\twritel_relaxed(0x18, SUNXI_DRAM_PHY0_BASE + 0x588);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x4d4);\n+\tfor (i = 0; i < 9; i++) {\n+\t\twritel_relaxed(0x12, ptr);\n+\t\twritel_relaxed(0x12, ptr + 0x30);\n+\t\tptr += 2;\n+\t}\n+\twritel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x52c);\n+\twritel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x5ec);\n+\twritel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x51c);\n+\twritel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x5dc);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x600);\n+\tfor (i = 0; i < 9; i++) {\n+\t\twritel_relaxed(0x12, ptr);\n+\t\twritel_relaxed(0x12, ptr + 0x30);\n+\t\tptr += 2;\n+\t}\n+\twritel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x6a8);\n+\twritel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x768);\n+\twritel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x648);\n+\twritel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x708);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x654);\n+\tfor (i = 0; i < 9; i++) {\n+\t\twritel_relaxed(0x14, ptr);\n+\t\twritel_relaxed(0x14, ptr + 0x30);\n+\t\tptr += 2;\n+\t}\n+\twritel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x6ac);\n+\twritel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x76c);\n+\twritel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x69c);\n+\twritel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x75c);\n+\n+\tdmb();\n+\n+\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, 0x80);\n+\n+\treturn true;\n+}\n+\n+static bool mctl_phy_init(struct dram_para *para)\n+{\n+\tstruct sunxi_mctl_com_reg * const mctl_com =\n+\t\t\t(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;\n+\tstruct sunxi_mctl_ctl_reg * const mctl_ctl =\n+\t\t\t(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;\n+\tu32 val, *ptr;\n+\tint i;\n+\n+\tif (para->bus_full_width)\n+\t\tval = 0xf;\n+\telse\n+\t\tval = 3;\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val);\n+\n+\twritel(0xd, SUNXI_DRAM_PHY0_BASE + 0x14);\n+\twritel(0xd, SUNXI_DRAM_PHY0_BASE + 0x35c);\n+\twritel(0xd, SUNXI_DRAM_PHY0_BASE + 0x368);\n+\twritel(0xd, SUNXI_DRAM_PHY0_BASE + 0x374);\n+\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x18);\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x360);\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x36c);\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x378);\n+\n+\twritel(9, SUNXI_DRAM_PHY0_BASE + 0x1c);\n+\twritel(9, SUNXI_DRAM_PHY0_BASE + 0x364);\n+\twritel(9, SUNXI_DRAM_PHY0_BASE + 0x370);\n+\twritel(9, SUNXI_DRAM_PHY0_BASE + 0x37c);\n+\n+\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xc0);\n+\tfor (i = 0; i < ARRAY_SIZE(phy_init); i++)\n+\t\twritel(phy_init[i], &ptr[i]);\n+\n+\tif (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_UNKNOWN_FEATURE)) {\n+\t\tptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x780);\n+\t\tfor (i = 0; i < 32; i++)\n+\t\t\twritel(0x16, &ptr[i]);\n+\t\twritel(0xe, SUNXI_DRAM_PHY0_BASE + 0x78c);\n+\t\twritel(0xe, SUNXI_DRAM_PHY0_BASE + 0x7a4);\n+\t\twritel(0xe, SUNXI_DRAM_PHY0_BASE + 0x7b8);\n+\t\twritel(0x8, SUNXI_DRAM_PHY0_BASE + 0x7d4);\n+\t\twritel(0xe, SUNXI_DRAM_PHY0_BASE + 0x7dc);\n+\t\twritel(0xe, SUNXI_DRAM_PHY0_BASE + 0x7e0);\n+\t}\n+\n+\twritel(0x80, SUNXI_DRAM_PHY0_BASE + 0x3dc);\n+\twritel(0x80, SUNXI_DRAM_PHY0_BASE + 0x45c);\n+\n+\tif (IS_ENABLED(DRAM_ODT_EN))\n+\t\tmctl_phy_configure_odt();\n+\n+\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xa);\n+\n+\tif (para->clk <= 672)\n+\t\twritel(0xf, SUNXI_DRAM_PHY0_BASE + 0x20);\n+\tif (para->clk > 500) {\n+\t\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x144, BIT(7));\n+\t\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, 0xe0);\n+\t} else {\n+\t\tsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x144, BIT(7));\n+\t\tclrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, 0xe0, 0x20);\n+\t}\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, 8);\n+\n+\tmctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x180), 4, 4);\n+\n+\twritel(0x37, SUNXI_DRAM_PHY0_BASE + 0x58);\n+\tclrbits_le32(&mctl_com->unk_0x500, 0x200);\n+\n+\twritel(0, &mctl_ctl->swctl);\n+\tsetbits_le32(&mctl_ctl->dfimisc, 1);\n+\n+\t/* start DFI init */\n+\tsetbits_le32(&mctl_ctl->dfimisc, 0x20);\n+\twritel(1, &mctl_ctl->swctl);\n+\tmctl_await_completion(&mctl_ctl->swstat, 1, 1);\n+\t/* poll DFI init complete */\n+\tmctl_await_completion(&mctl_ctl->dfistat, 1, 1);\n+\twritel(0, &mctl_ctl->swctl);\n+\tclrbits_le32(&mctl_ctl->dfimisc, 0x20);\n+\n+\tclrbits_le32(&mctl_ctl->pwrctl, 0x20);\n+\twritel(1, &mctl_ctl->swctl);\n+\tmctl_await_completion(&mctl_ctl->swstat, 1, 1);\n+\tmctl_await_completion(&mctl_ctl->statr, 3, 1);\n+\n+\twritel(0, &mctl_ctl->swctl);\n+\tclrbits_le32(&mctl_ctl->dfimisc, 1);\n+\n+\twritel(1, &mctl_ctl->swctl);\n+\tmctl_await_completion(&mctl_ctl->swstat, 1, 1);\n+\n+\twritel(0x1f14, &mctl_ctl->mrctrl1);\n+\twritel(0x80000030, &mctl_ctl->mrctrl0);\n+\tmctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);\n+\n+\twritel(4, &mctl_ctl->mrctrl1);\n+\twritel(0x80001030, &mctl_ctl->mrctrl0);\n+\tmctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);\n+\n+\twritel(0x20, &mctl_ctl->mrctrl1);\n+\twritel(0x80002030, &mctl_ctl->mrctrl0);\n+\tmctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);\n+\n+\twritel(0, &mctl_ctl->mrctrl1);\n+\twritel(0x80003030, &mctl_ctl->mrctrl0);\n+\tmctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);\n+\n+\twritel(0, SUNXI_DRAM_PHY0_BASE + 0x54);\n+\n+\twritel(0, &mctl_ctl->swctl);\n+\tclrbits_le32(&mctl_ctl->rfshctl3, 1);\n+\twritel(1, &mctl_ctl->swctl);\n+\n+\tif (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_WRITE_LEVELING)) {\n+\t\tfor (i = 0; i < 5; i++)\n+\t\t\tif (mctl_phy_write_leveling(para))\n+\t\t\t\tbreak;\n+\t\tif (i == 5) {\n+\t\t\tdebug(\"write leveling failed!\\n\");\n+\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\tif (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_READ_CALIBRATION)) {\n+\t\tfor (i = 0; i < 5; i++)\n+\t\t\tif (mctl_phy_read_calibration(para))\n+\t\t\t\tbreak;\n+\t\tif (i == 5) {\n+\t\t\tdebug(\"read calibration failed!\\n\");\n+\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\tif (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_READ_TRAINING)) {\n+\t\tfor (i = 0; i < 5; i++)\n+\t\t\tif (mctl_phy_read_training(para))\n+\t\t\t\tbreak;\n+\t\tif (i == 5) {\n+\t\t\tdebug(\"read training failed!\\n\");\n+\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\tif (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_WRITE_TRAINING)) {\n+\t\tfor (i = 0; i < 5; i++)\n+\t\t\tif (mctl_phy_write_training(para))\n+\t\t\t\tbreak;\n+\t\tif (i == 5) {\n+\t\t\tdebug(\"write training failed!\\n\");\n+\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\tif (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_BIT_DELAY_COMPENSATION))\n+\t\tmctl_phy_bit_delay_compensation(para);\n+\n+\tclrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 4);\n+\n+\treturn true;\n+}\n+\n+static bool mctl_ctrl_init(struct dram_para *para)\n+{\n+\tstruct sunxi_mctl_com_reg * const mctl_com =\n+\t\t\t(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;\n+\tstruct sunxi_mctl_ctl_reg * const mctl_ctl =\n+\t\t\t(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;\n+\tu32 reg_val;\n+\n+\tclrsetbits_le32(&mctl_com->unk_0x500, BIT(24), 0x200);\n+\twritel(0x8000, &mctl_ctl->unk_0x00c);\n+\n+\tsetbits_le32(&mctl_com->unk_0x008, 0xff00);\n+\n+\tclrsetbits_le32(&mctl_ctl->sched[0], 0xff00, 0x3000);\n+\n+\twritel(0, &mctl_ctl->hwlpctl);\n+\n+\tsetbits_le32(&mctl_com->unk_0x008, 0xff00);\n+\n+\treg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(para->ranks);\n+\treg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;\n+\tif (para->bus_full_width)\n+\t\treg_val |= MSTR_BUSWIDTH_FULL;\n+\telse\n+\t\treg_val |= MSTR_BUSWIDTH_HALF;\n+\twritel(BIT(31) | BIT(30) | reg_val, &mctl_ctl->mstr);\n+\n+\tif (para->ranks == 2)\n+\t\twritel(0x0303, &mctl_ctl->odtmap);\n+\telse\n+\t\twritel(0x0201, &mctl_ctl->odtmap);\n+\n+\twritel(0x06000400, &mctl_ctl->odtcfg);\n+\twritel(0x06000400, &mctl_ctl->unk_0x2240);\n+\twritel(0x06000400, &mctl_ctl->unk_0x3240);\n+\twritel(0x06000400, &mctl_ctl->unk_0x4240);\n+\n+\tsetbits_le32(&mctl_com->cr, BIT(31));\n+\n+\tmctl_set_addrmap(para);\n+\n+\tmctl_set_timing_params(para);\n+\n+\twritel(0, &mctl_ctl->pwrctl);\n+\n+\tsetbits_le32(&mctl_ctl->dfiupd[0], BIT(31) | BIT(30));\n+\tsetbits_le32(&mctl_ctl->zqctl[0], BIT(31) | BIT(30));\n+\tsetbits_le32(&mctl_ctl->unk_0x2180, BIT(31) | BIT(30));\n+\tsetbits_le32(&mctl_ctl->unk_0x3180, BIT(31) | BIT(30));\n+\tsetbits_le32(&mctl_ctl->unk_0x4180, BIT(31) | BIT(30));\n+\n+\tsetbits_le32(&mctl_ctl->rfshctl3, BIT(0));\n+\tclrbits_le32(&mctl_ctl->dfimisc, BIT(0));\n+\n+\twritel(0, &mctl_com->maer0);\n+\twritel(0, &mctl_com->maer1);\n+\twritel(0, &mctl_com->maer2);\n+\n+\twritel(0x20, &mctl_ctl->pwrctl);\n+\tsetbits_le32(&mctl_ctl->unk_0x00c, BIT(8));\n+\n+\tclrsetbits_le32(&mctl_com->unk_0x500, BIT(24), 0x300);\n+\t/* this write seems to enable PHY MMIO region */\n+\tsetbits_le32(&mctl_com->unk_0x500, BIT(24));\n+\n+\tif (!mctl_phy_init(para))\n+\t\treturn false;\n+\n+\twritel(0, &mctl_ctl->swctl);\n+\tclrbits_le32(&mctl_ctl->rfshctl3, BIT(0));\n+\n+\tsetbits_le32(&mctl_com->unk_0x014, BIT(31));\n+\twritel(0xffffffff, &mctl_com->maer0);\n+\twritel(0x7ff, &mctl_com->maer1);\n+\twritel(0xffff, &mctl_com->maer2);\n+\n+\twritel(1, &mctl_ctl->swctl);\n+\tmctl_await_completion(&mctl_ctl->swstat, 1, 1);\n+\n+\treturn true;\n+}\n+\n+static bool mctl_core_init(struct dram_para *para)\n+{\n+\tmctl_sys_init(para);\n+\n+\treturn mctl_ctrl_init(para);\n+}\n+\n+static void mctl_auto_detect_rank_width(struct dram_para *para)\n+{\n+\t/* this is minimum size that it's supported */\n+\tpara->cols = 8;\n+\tpara->rows = 13;\n+\n+\t/*\n+\t * Strategy here is to test most demanding combination first and least\n+\t * demanding last, otherwise HW might not be fully utilized. For\n+\t * example, half bus width and rank = 1 combination would also work\n+\t * on HW with full bus width and rank = 2, but only 1/4 RAM would be\n+\t * visible.\n+\t */\n+\n+\tdebug(\"testing 32-bit width, rank = 2\\n\");\n+\tpara->bus_full_width = 1;\n+\tpara->ranks = 2;\n+\tif (mctl_core_init(para))\n+\t\treturn;\n+\n+\tdebug(\"testing 32-bit width, rank = 1\\n\");\n+\tpara->bus_full_width = 1;\n+\tpara->ranks = 1;\n+\tif (mctl_core_init(para))\n+\t\treturn;\n+\n+\tdebug(\"testing 16-bit width, rank = 2\\n\");\n+\tpara->bus_full_width = 0;\n+\tpara->ranks = 2;\n+\tif (mctl_core_init(para))\n+\t\treturn;\n+\n+\tdebug(\"testing 16-bit width, rank = 1\\n\");\n+\tpara->bus_full_width = 0;\n+\tpara->ranks = 1;\n+\tif (mctl_core_init(para))\n+\t\treturn;\n+\n+\tpanic(\"This DRAM setup is currently not supported.\\n\");\n+}\n+\n+static void mctl_auto_detect_dram_size(struct dram_para *para)\n+{\n+\t/* detect row address bits */\n+\tpara->cols = 8;\n+\tpara->rows = 18;\n+\tmctl_core_init(para);\n+\n+\tfor (para->rows = 13; para->rows < 18; para->rows++) {\n+\t\t/* 8 banks, 8 bit per byte and 16/32 bit width */\n+\t\tif (mctl_mem_matches((1 << (para->rows + para->cols +\n+\t\t\t\t\t 4 + para->bus_full_width))))\n+\t\t\tbreak;\n+\t}\n+\n+\t/* detect column address bits */\n+\tpara->cols = 11;\n+\tmctl_core_init(para);\n+\n+\tfor (para->cols = 8; para->cols < 11; para->cols++) {\n+\t\t/* 8 bits per byte and 16/32 bit width */\n+\t\tif (mctl_mem_matches(1 << (para->cols + 1 +\n+\t\t\t\t\t para->bus_full_width)))\n+\t\t\tbreak;\n+\t}\n+}\n+\n+static unsigned long mctl_calc_size(struct dram_para *para)\n+{\n+\tu8 width = para->bus_full_width ? 4 : 2;\n+\n+\t/* 8 banks */\n+\treturn (1ULL << (para->cols + para->rows + 3)) * width * para->ranks;\n+}\n+\n+unsigned long sunxi_dram_init(void)\n+{\n+\tstruct dram_para para = {\n+\t\t.clk = CONFIG_DRAM_CLK,\n+\t\t.type = SUNXI_DRAM_TYPE_DDR3,\n+\t};\n+\tunsigned long size;\n+\n+\tsetbits_le32(0x7010310, BIT(8));\n+\tclrbits_le32(0x7010318, 0x3f);\n+\n+\tmctl_auto_detect_rank_width(¶);\n+\tmctl_auto_detect_dram_size(¶);\n+\n+\tmctl_core_init(¶);\n+\n+\tsize = mctl_calc_size(¶);\n+\n+\tmctl_set_master_priority();\n+\n+\treturn size;\n+};\ndiff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile\nindex 0deb9911fd79..39a8756c297e 100644\n--- a/arch/arm/mach-sunxi/dram_timings/Makefile\n+++ b/arch/arm/mach-sunxi/dram_timings/Makefile\n@@ -3,3 +3,5 @@ obj-$(CONFIG_SUNXI_DRAM_LPDDR3_STOCK)\t+= lpddr3_stock.o\n obj-$(CONFIG_SUNXI_DRAM_DDR2_V3S)\t+= ddr2_v3s.o\n obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3)\t+= h6_lpddr3.o\n obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333)\t+= h6_ddr3_1333.o\n+# currently only DDR3 is supported on H616\n+obj-$(CONFIG_MACH_SUN50I_H616)\t\t+= h616_ddr3_1333.o\ndiff --git a/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c b/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c\nnew file mode 100644\nindex 000000000000..8f508344bc17\n--- /dev/null\n+++ b/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c\n@@ -0,0 +1,94 @@\n+/*\n+ * sun50i H616 DDR3-1333 timings, as programmed by Allwinner's boot0\n+ *\n+ * The chips are probably able to be driven by a faster clock, but boot0\n+ * uses a more conservative timing (as usual).\n+ *\n+ * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>\n+ * Based on H6 DDR3 timings:\n+ * (C) Copyright 2018,2019 Arm Ltd.\n+ *\n+ * SPDX-License-Identifier:\tGPL-2.0+\n+ */\n+\n+#include <common.h>\n+#include <asm/arch/dram.h>\n+#include <asm/arch/cpu.h>\n+\n+void mctl_set_timing_params(struct dram_para *para)\n+{\n+\tstruct sunxi_mctl_ctl_reg * const mctl_ctl =\n+\t\t\t(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;\n+\n+\tu8 tccd\t\t= 2;\t\t\t/* JEDEC: 4nCK */\n+\tu8 tfaw\t\t= ns_to_t(50);\t\t/* JEDEC: 30 ns w/ 1K pages */\n+\tu8 trrd\t\t= max(ns_to_t(6), 4);\t/* JEDEC: max(6 ns, 4nCK) */\n+\tu8 trcd\t\t= ns_to_t(15);\t\t/* JEDEC: 13.5 ns */\n+\tu8 trc\t\t= ns_to_t(53);\t\t/* JEDEC: 49.5 ns */\n+\tu8 txp\t\t= max(ns_to_t(6), 3);\t/* JEDEC: max(6 ns, 3nCK) */\n+\tu8 trtp\t\t= max(ns_to_t(8), 2);\t/* JEDEC: max(7.5 ns, 4nCK) */\n+\tu8 trp\t\t= ns_to_t(15);\t\t/* JEDEC: >= 13.75 ns */\n+\tu8 tras\t\t= ns_to_t(38);\t\t/* JEDEC >= 36 ns, <= 9*trefi */\n+\tu16 trefi\t= ns_to_t(7800) / 32;\t/* JEDEC: 7.8us@Tcase <= 85C */\n+\tu16 trfc\t= ns_to_t(350);\t\t/* JEDEC: 160 ns for 2Gb */\n+\tu16 txsr\t= 4;\t\t\t/* ? */\n+\n+\tu8 tmrw\t\t= 0;\t\t\t/* ? */\n+\tu8 tmrd\t\t= 4;\t\t\t/* JEDEC: 4nCK */\n+\tu8 tmod\t\t= max(ns_to_t(15), 12);\t/* JEDEC: max(15 ns, 12nCK) */\n+\tu8 tcke\t\t= max(ns_to_t(6), 3);\t/* JEDEC: max(5.625 ns, 3nCK) */\n+\tu8 tcksrx\t= max(ns_to_t(10), 4);\t/* JEDEC: max(10 ns, 5nCK) */\n+\tu8 tcksre\t= max(ns_to_t(10), 4);\t/* JEDEC: max(10 ns, 5nCK) */\n+\tu8 tckesr\t= tcke + 1;\t\t/* JEDEC: tCKE(min) + 1nCK */\n+\tu8 trasmax\t= (para->clk / 2) / 15;\t/* JEDEC: tREFI * 9 */\n+\tu8 txs\t\t= ns_to_t(360) / 32;\t/* JEDEC: max(5nCK,tRFC+10ns) */\n+\tu8 txsdll\t= 16;\t\t\t/* JEDEC: 512 nCK */\n+\tu8 txsabort\t= 4;\t\t\t/* ? */\n+\tu8 txsfast\t= 4;\t\t\t/* ? */\n+\tu8 tcl\t\t= 7;\t\t\t/* JEDEC: CL / 2 => 6 */\n+\tu8 tcwl\t\t= 5;\t\t\t/* JEDEC: 8 */\n+\tu8 t_rdata_en\t= 9;\t\t\t/* ? */\n+\n+\tu8 twtp\t\t= 14;\t\t\t/* (WL + BL / 2 + tWR) / 2 */\n+\tu8 twr2rd\t= trtp + 7;\t\t/* (WL + BL / 2 + tWTR) / 2 */\n+\tu8 trd2wr\t= 5;\t\t\t/* (RL + BL / 2 + 2 - WL) / 2 */\n+\n+\t/* set DRAM timing */\n+\twritel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,\n+\t &mctl_ctl->dramtmg[0]);\n+\twritel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);\n+\twritel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,\n+\t &mctl_ctl->dramtmg[2]);\n+\twritel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);\n+\twritel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,\n+\t &mctl_ctl->dramtmg[4]);\n+\twritel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,\n+\t &mctl_ctl->dramtmg[5]);\n+\t/* Value suggested by ZynqMP manual and used by libdram */\n+\twritel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]);\n+\twritel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs,\n+\t &mctl_ctl->dramtmg[8]);\n+\twritel(0x00020208, &mctl_ctl->dramtmg[9]);\n+\twritel(0xE0C05, &mctl_ctl->dramtmg[10]);\n+\twritel(0x440C021C, &mctl_ctl->dramtmg[11]);\n+\twritel(8, &mctl_ctl->dramtmg[12]);\n+\twritel(0xA100002, &mctl_ctl->dramtmg[13]);\n+\twritel(txsr, &mctl_ctl->dramtmg[14]);\n+\n+\tclrbits_le32(&mctl_ctl->init[0], 3 << 30);\n+\twritel(0x420000, &mctl_ctl->init[1]);\n+\twritel(5, &mctl_ctl->init[2]);\n+\twritel(0x1f140004, &mctl_ctl->init[3]);\n+\twritel(0x00200000, &mctl_ctl->init[4]);\n+\n+\twritel(0, &mctl_ctl->dfimisc);\n+\tclrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);\n+\n+\t/* Configure DFI timing */\n+\twritel((tcl - 2) | 0x2000000 | (t_rdata_en << 16) | 0x808000,\n+\t &mctl_ctl->dfitmg0);\n+\twritel(0x100202, &mctl_ctl->dfitmg1);\n+\n+\t/* set refresh timing */\n+\twritel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);\n+}\n", "prefixes": [ "11/17" ] }