
From: Cody Eksal masterr3c0rd@epochal.quest
This adds preliminary support for the DRAM controller in the Allwinner A100/A133 SoCs. This is work in progress, and has rough edges, but works on at least three different boards. It contains support for DDR4 and LPDDR4. --- .../include/asm/arch-sunxi/cpu_sun50i_h6.h | 4 + arch/arm/include/asm/arch-sunxi/dram.h | 2 + .../include/asm/arch-sunxi/dram_sun50i_a133.h | 210 +++ arch/arm/mach-sunxi/Kconfig | 184 ++- arch/arm/mach-sunxi/Makefile | 2 + arch/arm/mach-sunxi/dram_sun50i_a133.c | 1217 +++++++++++++++++ arch/arm/mach-sunxi/dram_timings/Makefile | 2 + arch/arm/mach-sunxi/dram_timings/a133_ddr4.c | 80 ++ .../arm/mach-sunxi/dram_timings/a133_lpddr4.c | 102 ++ 9 files changed, 1795 insertions(+), 8 deletions(-) create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h create mode 100644 arch/arm/mach-sunxi/dram_sun50i_a133.c create mode 100644 arch/arm/mach-sunxi/dram_timings/a133_ddr4.c create mode 100644 arch/arm/mach-sunxi/dram_timings/a133_lpddr4.c
diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h index 8a3f465545a..2a9b086991c 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h @@ -29,6 +29,10 @@ #define SUNXI_DRAM_COM_BASE 0x047FA000 #define SUNXI_DRAM_CTL0_BASE 0x047FB000 #define SUNXI_DRAM_PHY0_BASE 0x04800000 +#elif CONFIG_MACH_SUN50I_A133 +#define SUNXI_DRAM_COM_BASE 0x04810000 +#define SUNXI_DRAM_CTL0_BASE 0x04820000 +#define SUNXI_DRAM_PHY0_BASE 0x04830000 #endif
#define SUNXI_TWI0_BASE 0x05002000 diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 9d21b492418..0708ae3ee3b 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -31,6 +31,8 @@ #include <asm/arch/dram_sun50i_h6.h> #elif defined(CONFIG_MACH_SUN50I_H616) #include <asm/arch/dram_sun50i_h616.h> +#elif defined(CONFIG_DRAM_SUN50I_A133) +#include <asm/arch/dram_sun50i_a133.h> #elif defined(CONFIG_MACH_SUNIV) #include <asm/arch/dram_suniv.h> #else diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h new file mode 100644 index 00000000000..8e5b86ba492 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * A133 dram controller register and constant defines + * + * (C) Copyright 2024 MasterR3C0RD masterr3c0rd@epochal.quest + */ + +#ifndef _SUNXI_DRAM_SUN50I_A133_H +#define _SUNXI_DRAM_SUN50I_A133_H + +#include <stdbool.h> +#ifndef __ASSEMBLY__ +#include <linux/bitops.h> +#endif + +enum sunxi_dram_type { + SUNXI_DRAM_TYPE_DDR3 = 3, + SUNXI_DRAM_TYPE_DDR4, + SUNXI_DRAM_TYPE_LPDDR3 = 7, + SUNXI_DRAM_TYPE_LPDDR4 +}; + +static inline int ns_to_t(int nanoseconds) +{ + const unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2; + + return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000); +} + +/* MBUS part is largely the same as in H6, except for one special register */ +struct sunxi_mctl_com_reg { + u32 cr; /* 0x000 control register */ + u8 reserved_0x004[4]; /* 0x004 */ + u32 unk_0x008; /* 0x008 */ + u32 tmr; /* 0x00c timer register */ + u8 reserved_0x010[4]; /* 0x010 */ + u32 unk_0x014; /* 0x014 */ + u8 reserved_0x018[8]; /* 0x018 */ + u32 maer0; /* 0x020 master enable register 0 */ + /* NOTE: This register has the same importance as mctl_ctl->clken in H616 */ + u32 maer1; /* 0x024 master enable register 1 */ + u32 maer2; /* 0x028 master enable register 2 */ + u8 reserved_0x02c[468]; /* 0x02c */ + u32 bwcr; /* 0x200 bandwidth control register */ + u8 reserved_0x204[12]; /* 0x204 */ + /* + * The last master configured by BSP libdram is at 0x49x, so the + * size of this struct array is set to 41 (0x29) now. + */ + struct { + u32 cfg0; /* 0x0 */ + u32 cfg1; /* 0x4 */ + u8 reserved_0x8[8]; /* 0x8 */ + } master[41]; /* 0x210 + index * 0x10 */ + u8 reserved_0x4a0[96]; /* 0x4a0 */ + u32 unk_0x500; /* 0x500 */ +}; +check_member(sunxi_mctl_com_reg, unk_0x500, 0x500); + +/* + * Controller registers seems to be the same or at least very similar + * to those in H6. + */ +struct sunxi_mctl_ctl_reg { + u32 mstr; /* 0x000 */ + u32 statr; /* 0x004 unused */ + u32 mstr1; /* 0x008 unused */ + u32 clken; /* 0x00c */ + u32 mrctrl0; /* 0x010 unused */ + u32 mrctrl1; /* 0x014 unused */ + u32 mrstatr; /* 0x018 unused */ + u32 mrctrl2; /* 0x01c unused */ + u32 derateen; /* 0x020 unused */ + u32 derateint; /* 0x024 unused */ + u8 reserved_0x028[8]; /* 0x028 */ + u32 pwrctl; /* 0x030 unused */ + u32 pwrtmg; /* 0x034 unused */ + u32 hwlpctl; /* 0x038 unused */ + u8 reserved_0x03c[20]; /* 0x03c */ + u32 rfshctl0; /* 0x050 unused */ + u32 rfshctl1; /* 0x054 unused */ + u8 reserved_0x058[8]; /* 0x05c */ + u32 rfshctl3; /* 0x060 */ + u32 rfshtmg; /* 0x064 */ + u8 reserved_0x068[104]; /* 0x068 */ + u32 init[8]; /* 0x0d0 */ + u32 dimmctl; /* 0x0f0 unused */ + u32 rankctl; /* 0x0f4 */ + u8 reserved_0x0f8[8]; /* 0x0f8 */ + u32 dramtmg[17]; /* 0x100 */ + u8 reserved_0x144[60]; /* 0x144 */ + u32 zqctl[3]; /* 0x180 */ + u32 zqstat; /* 0x18c unused */ + u32 dfitmg0; /* 0x190 */ + u32 dfitmg1; /* 0x194 */ + u32 dfilpcfg[2]; /* 0x198 unused */ + u32 dfiupd[3]; /* 0x1a0 */ + u32 reserved_0x1ac; /* 0x1ac */ + u32 dfimisc; /* 0x1b0 */ + u32 dfitmg2; /* 0x1b4 unused */ + u32 dfitmg3; /* 0x1b8 unused */ + u32 dfistat; /* 0x1bc */ + u32 dbictl; /* 0x1c0 */ + u8 reserved_0x1c4[60]; /* 0x1c4 */ + u32 addrmap[12]; /* 0x200 */ + u8 reserved_0x230[16]; /* 0x230 */ + u32 odtcfg; /* 0x240 */ + u32 odtmap; /* 0x244 */ + u8 reserved_0x248[8]; /* 0x248 */ + u32 sched[2]; /* 0x250 */ + u8 reserved_0x258[180]; /* 0x258 */ + u32 dbgcmd; /* 0x30c unused */ + u32 dbgstat; /* 0x310 unused */ + u8 reserved_0x314[12]; /* 0x314 */ + u32 swctl; /* 0x320 */ + u32 swstat; /* 0x324 */ + u8 reserved_0x328[7768]; /* 0x328 */ + u32 unk_0x2180; /* 0x2180 */ + u8 reserved_0x2184[188]; /* 0x2184 */ + u32 unk_0x2240; /* 0x2240 */ + u8 reserved_0x2244[3900]; /* 0x2244 */ + u32 unk_0x3180; /* 0x3180 */ + u8 reserved_0x3184[188]; /* 0x3184 */ + u32 unk_0x3240; /* 0x3240 */ + u8 reserved_0x3244[3900]; /* 0x3244 */ + u32 unk_0x4180; /* 0x4180 */ + u8 reserved_0x4184[188]; /* 0x4184 */ + u32 unk_0x4240; /* 0x4240 */ +}; + +check_member(sunxi_mctl_ctl_reg, swstat, 0x324); +check_member(sunxi_mctl_ctl_reg, unk_0x4240, 0x4240); + +#define MSTR_DEVICETYPE_DDR3 BIT(0) +#define MSTR_DEVICETYPE_LPDDR2 BIT(2) +#define MSTR_DEVICETYPE_LPDDR3 BIT(3) +#define MSTR_DEVICETYPE_DDR4 BIT(4) +#define MSTR_DEVICETYPE_LPDDR4 BIT(5) +#define MSTR_DEVICETYPE_MASK GENMASK(5, 0) +#define MSTR_GEARDOWNMODE BIT(0) /* Same as MSTR_DEVICETYPE_DDR3, only used for DDR4 */ +#define MSTR_2TMODE BIT(10) +#define MSTR_BUSWIDTH_FULL (0 << 12) +#define MSTR_BUSWIDTH_HALF (1 << 12) +#define MSTR_ACTIVE_RANKS(x) (((x == 1) ? 3 : 1) << 24) +#define MSTR_BURST_LENGTH(x) (((x) >> 1) << 16) +#define MSTR_DEVICECONFIG_X32 (3 << 30) + +#define TPR10_CA_BIT_DELAY BIT(16) +#define TPR10_DX_BIT_DELAY0 BIT(17) +#define TPR10_DX_BIT_DELAY1 BIT(18) +#define TPR10_WRITE_LEVELING BIT(20) +#define TPR10_READ_CALIBRATION BIT(21) +#define TPR10_READ_TRAINING BIT(22) +#define TPR10_WRITE_TRAINING BIT(23) + +/* MRCTRL constants */ +#define MRCTRL0_MR_RANKS_ALL (3 << 4) +#define MRCTRL0_MR_ADDR(x) (x << 12) +#define MRCTRL0_MR_WR BIT(31) + +#define MRCTRL1_MR_ADDR(x) (x << 8) +#define MRCTRL1_MR_DATA(x) (x) + +/* TODO: Remove unused fields */ +struct dram_para { + uint32_t clk; + enum sunxi_dram_type type; + uint32_t dx_odt; + uint32_t dx_dri; + uint32_t ca_dri; + uint32_t para0; + uint32_t para1; + uint32_t para2; + uint32_t mr0; + uint32_t mr1; + uint32_t mr2; + uint32_t mr3; + uint32_t mr4; + uint32_t mr5; + uint32_t mr6; + uint32_t mr11; + uint32_t mr12; + uint32_t mr13; + uint32_t mr14; + uint32_t mr16; + uint32_t mr17; + uint32_t mr22; + uint32_t tpr1; + uint32_t tpr2; + uint32_t tpr3; + uint32_t tpr6; + uint32_t tpr10; + uint32_t tpr11; + uint32_t tpr12; + uint32_t tpr13; + uint32_t tpr14; +}; + +void mctl_set_timing_params(const struct dram_para *para); + +struct dram_config { + u8 cols; /* Column bits */ + u8 rows; /* Row bits */ + u8 ranks; /* Rank bits (different from H616!) */ + u8 banks; /* Bank bits */ + u8 bankgrps; /* Bank group bits */ + u8 bus_full_width; /* 1 = x32, 0 = x16 */ +}; + +#endif /* _SUNXI_DRAM_SUN50I_A133_H */ diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 78fd74f3f28..ccf0c9a403e 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -51,7 +51,13 @@ config DRAM_SUN50I_H616 Select this dram controller driver for some sun50i platforms, like H616.
-if DRAM_SUN50I_H616 +config DRAM_SUN50I_A133 + bool + help + Select this dram controller driver for some sun50i platforms, + like A133. + +if DRAM_SUN50I_H616 || DRAM_SUN50I_A133 config DRAM_SUNXI_DX_ODT hex "DRAM DX ODT parameter" help @@ -73,18 +79,144 @@ config DRAM_SUNXI_ODT_EN help ODT EN value from vendor DRAM settings.
+config DRAM_SUNXI_PARA0 + hex "DRAM PARA0 parameter" + depends on DRAM_SUN50I_A133 + help + PARA0 value from vendor DRAM settings. + +config DRAM_SUNXI_PARA1 + hex "DRAM PARA1 parameter" + help + PARA1 value from vendor DRAM settings. + +config DRAM_SUNXI_PARA2 + hex "DRAM PARA2 parameter" + help + PARA2 value from vendor DRAM settings. + +config DRAM_SUNXI_MR0 + hex "DRAM MR0 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR0 value from vendor DRAM settings. + +config DRAM_SUNXI_MR1 + hex "DRAM MR1 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR1 value from vendor DRAM settings. + +config DRAM_SUNXI_MR2 + hex "DRAM MR2 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR2 value from vendor DRAM settings. + +config DRAM_SUNXI_MR3 + hex "DRAM MR3 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR3 value from vendor DRAM settings. + +config DRAM_SUNXI_MR4 + hex "DRAM MR4 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR4 value from vendor DRAM settings. + +config DRAM_SUNXI_MR5 + hex "DRAM MR5 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR5 value from vendor DRAM settings. + +config DRAM_SUNXI_MR6 + hex "DRAM MR6 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR6 value from vendor DRAM settings. + +config DRAM_SUNXI_MR11 + hex "DRAM MR11 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR11 value from vendor DRAM settings. + +config DRAM_SUNXI_MR12 + hex "DRAM MR12 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR12 value from vendor DRAM settings. + +config DRAM_SUNXI_MR13 + hex "DRAM MR13 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR13 value from vendor DRAM settings. + +config DRAM_SUNXI_MR14 + hex "DRAM MR14 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR14 value from vendor DRAM settings. + +config DRAM_SUNXI_MR16 + hex "DRAM MR16 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR16 value from vendor DRAM settings. + +config DRAM_SUNXI_MR17 + hex "DRAM MR17 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + MR17 value from vendor DRAM settings. + +config DRAM_SUNXI_MR22 + hex "DRAM MR22 parameter" + depends on !DRAM_SUN50I_A133 + default 0x0 + help + MR22 value from vendor DRAM settings. + config DRAM_SUNXI_TPR0 hex "DRAM TPR0 parameter" default 0x0 help TPR0 value from vendor DRAM settings.
+config DRAM_SUNXI_TPR1 + hex "DRAM TPR1 parameter" + default 0x0 + help + TPR1 value from vendor DRAM settings. + config DRAM_SUNXI_TPR2 hex "DRAM TPR2 parameter" default 0x0 help TPR2 value from vendor DRAM settings.
+config DRAM_SUNXI_TPR3 + hex "DRAM TPR3 parameter" + default 0x0 + help + TPR3 value from vendor DRAM settings. + config DRAM_SUNXI_TPR6 hex "DRAM TPR6 parameter" default 0x3300c080 @@ -109,6 +241,20 @@ config DRAM_SUNXI_TPR12 help TPR12 value from vendor DRAM settings.
+config DRAM_SUNXI_TPR13 + hex "DRAM TPR13 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + TPR13 value from vendor DRAM settings. + +config DRAM_SUNXI_TPR14 + hex "DRAM TPR14 parameter" + depends on DRAM_SUN50I_A133 + default 0x0 + help + TPR14 value from vendor DRAM settings. + choice prompt "DRAM PHY pin mapping selection" default DRAM_SUNXI_PHY_ADDR_MAP_0 @@ -116,7 +262,8 @@ choice config DRAM_SUNXI_PHY_ADDR_MAP_0 bool "DRAM PHY address map 0" help - This pin mapping selection should be used by the H313, H616, H618. + This pin mapping selection should be used by the H313, H616, H618, + and A133, R818 SoCs.
config DRAM_SUNXI_PHY_ADDR_MAP_1 bool "DRAM PHY address map 1" @@ -497,7 +644,7 @@ config ARM_BOOT_HOOK_RMR This allows both the SPL and the U-Boot proper to be entered in either mode and switch to AArch64 if needed.
-if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 +if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 || DRAM_SUN50I_A133 config SUNXI_DRAM_DDR3 bool
@@ -510,6 +657,9 @@ config SUNXI_DRAM_LPDDR3 config SUNXI_DRAM_LPDDR4 bool
+config SUNXI_DRAM_DDR4 + bool + choice prompt "DRAM Type and Timing" default SUNXI_DRAM_DDR3_1333 if !MACH_SUN8I_V3S @@ -518,6 +668,7 @@ choice config SUNXI_DRAM_DDR3_1333 bool "DDR3 1333" select SUNXI_DRAM_DDR3 + depends on !DRAM_SUN50I_A133 ---help--- This option is the original only supported memory type, which suits many H3/H5/A64 boards available now. @@ -525,6 +676,7 @@ config SUNXI_DRAM_DDR3_1333 config SUNXI_DRAM_LPDDR3_STOCK bool "LPDDR3 with Allwinner stock configuration" select SUNXI_DRAM_LPDDR3 + depends on !DRAM_SUN50I_A133 ---help--- This option is the LPDDR3 timing used by the stock boot0 by Allwinner. @@ -548,7 +700,7 @@ config SUNXI_DRAM_H6_DDR3_1333 config SUNXI_DRAM_H616_LPDDR3 bool "LPDDR3 DRAM chips on the H616 DRAM controller" select SUNXI_DRAM_LPDDR3 - depends on DRAM_SUN50I_H616 + depends on DRAM_SUN50I_H616 || DRAM_SUN50I_A133 help This option is the LPDDR3 timing used by the stock boot0 by Allwinner. @@ -556,7 +708,7 @@ config SUNXI_DRAM_H616_LPDDR3 config SUNXI_DRAM_H616_LPDDR4 bool "LPDDR4 DRAM chips on the H616 DRAM controller" select SUNXI_DRAM_LPDDR4 - depends on DRAM_SUN50I_H616 + depends on DRAM_SUN50I_H616 || DRAM_SUN50I_A133 help This option is the LPDDR4 timing used by the stock boot0 by Allwinner. @@ -564,11 +716,27 @@ config SUNXI_DRAM_H616_LPDDR4 config SUNXI_DRAM_H616_DDR3_1333 bool "DDR3-1333 boot0 timings on the H616 DRAM controller" select SUNXI_DRAM_DDR3 - depends on DRAM_SUN50I_H616 + depends on DRAM_SUN50I_H616 || DRAM_SUN50I_A133 help This option is the DDR3 timing used by the boot0 on H616 TV boxes which use a DDR3-1333 timing.
+config SUNXI_DRAM_A133_DDR4 + bool "DDR4 boot0 timings on the A133 DRAM controller" + select SUNXI_DRAM_DDR4 + depends on DRAM_SUN50I_A133 + help + This option is the DDR4 timing used by the boot0 on A133 devices + which use a DDR4 timing. + +config SUNXI_DRAM_A133_LPDDR4 + bool "LPDDR4 boot0 timings on the A133 DRAM controller" + select SUNXI_DRAM_LPDDR4 + depends on DRAM_SUN50I_A133 + help + This option is the LPDDR4 timing used by the boot0 on A133 devices + which use an LPDDR4 timing. + config SUNXI_DRAM_DDR2_V3S bool "DDR2 found in V3s chip" select SUNXI_DRAM_DDR2 @@ -596,7 +764,7 @@ config DRAM_CLK MACH_SUN8I_V3S default 672 if MACH_SUN50I default 744 if MACH_SUN50I_H6 - default 720 if MACH_SUN50I_H616 + default 720 if MACH_SUN50I_H616 || MACH_SUN50I_A133 ---help--- Set the dram clock speed, valid range 240 - 480 (prior to sun9i), must be a multiple of 24. For the sun9i (A80), the tested values @@ -613,7 +781,7 @@ endif
config DRAM_ZQ int "sunxi dram zq value" - depends on !MACH_SUN50I_H616 + depends on !MACH_SUN50I_H616 && !MACH_SUN50I_A133 default 123 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || \ MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_A83T default 127 if MACH_SUN7I diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile index eb6a49119a1..9b38f1d8663 100644 --- a/arch/arm/mach-sunxi/Makefile +++ b/arch/arm/mach-sunxi/Makefile @@ -45,4 +45,6 @@ obj-$(CONFIG_DRAM_SUN50I_H6) += dram_sun50i_h6.o obj-$(CONFIG_DRAM_SUN50I_H6) += dram_timings/ obj-$(CONFIG_DRAM_SUN50I_H616) += dram_sun50i_h616.o obj-$(CONFIG_DRAM_SUN50I_H616) += dram_timings/ +obj-$(CONFIG_DRAM_SUN50I_A133) += dram_sun50i_a133.o +obj-$(CONFIG_DRAM_SUN50I_A133) += dram_timings/ endif diff --git a/arch/arm/mach-sunxi/dram_sun50i_a133.c b/arch/arm/mach-sunxi/dram_sun50i_a133.c new file mode 100644 index 00000000000..1221244cbb6 --- /dev/null +++ b/arch/arm/mach-sunxi/dram_sun50i_a133.c @@ -0,0 +1,1217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sun50i A133 platform dram controller driver + * + * Controller and PHY appear to be quite similar to that of the H616; + * however certain offsets, timings, and other details are different enough that + * the original code does not work as expected. Some device flags and calibrations + * are not yet implemented, and configuration aside from DDR4 have not been tested. + * + * (C) Copyright 2024 MasterR3C0RD masterr3c0rd@epochal.quest + * + * Uses code from H616 driver, which is + * (C) Copyright 2020 Jernej Skrabec jernej.skrabec@siol.net + * + */ + +#define DEBUG + +#include <asm/arch/clock.h> +#include <asm/arch/cpu.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h> +#include <asm/io.h> +#include <init.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <log.h> + +#ifdef CONFIG_DRAM_SUNXI_PHY_ADDR_MAP_1 +static const u8 phy_init[] = { +#ifdef CONFIG_SUNXI_DRAM_DDR3 + 0x0c, 0x08, 0x19, 0x18, 0x10, 0x06, 0x0a, 0x03, 0x0e, + 0x00, 0x0b, 0x05, 0x09, 0x1a, 0x04, 0x13, 0x16, 0x11, + 0x01, 0x15, 0x0d, 0x07, 0x12, 0x17, 0x14, 0x02, 0x0f +#elif CONFIG_SUNXI_DRAM_DDR4 + 0x19, 0x1a, 0x04, 0x12, 0x09, 0x06, 0x08, 0x0a, 0x16, + 0x17, 0x18, 0x0f, 0x0c, 0x13, 0x02, 0x05, 0x01, 0x11, + 0x0e, 0x00, 0x0b, 0x07, 0x03, 0x14, 0x15, 0x0d, 0x10 +#elif CONFIG_SUNXI_DRAM_LPDDR3 + 0x08, 0x03, 0x02, 0x00, 0x18, 0x19, 0x09, 0x01, 0x06, + 0x17, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x05, 0x07, 0x1a +#elif CONFIG_SUNXI_DRAM_LPDDR4 + 0x01, 0x05, 0x02, 0x00, 0x19, 0x03, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x04, 0x1a +#endif +}; +#else +static const u8 phy_init[] = { +#ifdef CONFIG_SUNXI_DRAM_DDR3 + 0x03, 0x19, 0x18, 0x02, 0x10, 0x15, 0x16, 0x07, 0x06, + 0x0e, 0x05, 0x08, 0x0d, 0x04, 0x17, 0x1a, 0x13, 0x11, + 0x12, 0x14, 0x00, 0x01, 0x0c, 0x0a, 0x09, 0x0b, 0x0f +#elif CONFIG_SUNXI_DRAM_DDR4 + 0x13, 0x17, 0x0e, 0x01, 0x06, 0x12, 0x14, 0x07, 0x09, + 0x02, 0x0f, 0x00, 0x0d, 0x05, 0x16, 0x0c, 0x0a, 0x11, + 0x04, 0x03, 0x18, 0x15, 0x08, 0x10, 0x0b, 0x19, 0x1a +#elif CONFIG_SUNXI_DRAM_LPDDR3 + 0x05, 0x06, 0x17, 0x02, 0x19, 0x18, 0x04, 0x07, 0x03, + 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x09, 0x00, 0x1a +#elif CONFIG_SUNXI_DRAM_LPDDR4 + 0x01, 0x03, 0x02, 0x19, 0x17, 0x00, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x18, 0x05, 0x1a +#endif +}; +#endif + +static void mctl_clk_init(u32 clk) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* Place all DRAM blocks into reset */ + clrbits_le32(&ccm->mbus_cfg, MBUS_ENABLE); + clrbits_le32(&ccm->mbus_cfg, MBUS_RESET); + clrbits_le32(&ccm->dram_gate_reset, BIT(GATE_SHIFT)); + clrbits_le32(&ccm->dram_gate_reset, BIT(RESET_SHIFT)); + clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN); + clrbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET); + udelay(5); + + /* Set up PLL5 clock, used for DRAM */ + clrsetbits_le32(&ccm->pll5_cfg, 0xff03, + CCM_PLL5_CTRL_N((clk * 2) / 24) | CCM_PLL5_CTRL_EN); + setbits_le32(&ccm->pll5_cfg, BIT(24)); + clrsetbits_le32(&ccm->pll5_cfg, 0x3, + CCM_PLL5_LOCK_EN | CCM_PLL5_CTRL_EN | BIT(30)); + clrbits_le32(&ccm->pll5_cfg, 0x3 | BIT(30)); + mctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK); + + /* Enable DRAM clock and gate*/ + clrbits_le32(&ccm->dram_clk_cfg, BIT(24) | BIT(25)); + clrsetbits_le32(&ccm->dram_clk_cfg, 0x1f, BIT(1) | BIT(0)); + setbits_le32(&ccm->dram_clk_cfg, DRAM_CLK_UPDATE); + setbits_le32(&ccm->dram_gate_reset, BIT(RESET_SHIFT)); + setbits_le32(&ccm->dram_gate_reset, BIT(GATE_SHIFT)); + + /* Re-enable MBUS and reset the DRAM module */ + setbits_le32(&ccm->mbus_cfg, MBUS_RESET); + setbits_le32(&ccm->mbus_cfg, MBUS_ENABLE); + setbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET); + udelay(5); +} + +static void mctl_set_odtmap(const struct dram_para *para, + const struct dram_config *config) +{ + struct sunxi_mctl_ctl_reg *mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u32 val, temp1, temp2; + + /* Set ODT/rank mappings*/ + if (config->bus_full_width) + writel(0x0201, &mctl_ctl->odtmap); + else + writel(0x0303, &mctl_ctl->odtmap); + + switch (para->type) { + case SUNXI_DRAM_TYPE_DDR3: + val = 0x06000400; + break; + case SUNXI_DRAM_TYPE_LPDDR3: + /* TODO: What's the purpose of these values? */ + temp1 = para->clk * 7 / 2000; + if (para->clk < 400) + temp2 = 0x3; + else + temp2 = 0x4; + + val = 0x400 | (temp2 - temp1) << 16 | temp1 << 24; + break; + case SUNXI_DRAM_TYPE_DDR4: + val = 0x400 | (para->mr4 << 10 & 0x70000) | + (((para->mr4 >> 12) & 1) + 6) << 24; + break; + case SUNXI_DRAM_TYPE_LPDDR4: + val = 0x4000400; + break; + } + + writel(val, &mctl_ctl->odtcfg); + /* Documented as ODTCFG_SHADOW */ + writel(val, &mctl_ctl->unk_0x2240); + /* Offset's interesting; additional undocumented shadows? */ + writel(val, &mctl_ctl->unk_0x3240); + writel(val, &mctl_ctl->unk_0x4240); +} + +/* + * Note: Unlike the H616, config->ranks is the number of rank *bits*, not the number of ranks *present*. + * For example, if `ranks = 0`, then there is only one rank. If `ranks = 1`, there are two. + * + * If this is deemed an issue during review, fixing this off-by-one is no problem, but this matches how + * boot0 handles these values. + */ +static void mctl_set_addrmap(const struct dram_config *config) +{ + struct sunxi_mctl_ctl_reg *mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u8 bankgrp_bits = config->bankgrps; + u8 bank_bits = config->banks; + u8 rank_bits = config->ranks; + u8 col_bits = config->cols; + u8 row_bits = config->rows; + bool bus_full_width = config->bus_full_width; + + u8 addrmap_bank_bx = bankgrp_bits + col_bits - 2; + u8 addrmap_row_bx = (bankgrp_bits + bank_bits + col_bits) - 6; + + if (!bus_full_width) + col_bits -= 1; + + /* Ordered from LSB to MSB: */ + /* Bank groups */ + switch (bankgrp_bits) { + case 0: + writel(0x3f3f, &mctl_ctl->addrmap[8]); + break; + case 1: + writel(0x01 | 0x3f << 8, &mctl_ctl->addrmap[8]); + break; + case 2: + writel(0x01 | 0x01 << 8, &mctl_ctl->addrmap[8]); + break; + default: + panic("Unsupported dram configuration (bankgrp_bits = %d)", + bankgrp_bits); + } + + /* Columns */ + writel(bankgrp_bits | bankgrp_bits << 8 | bankgrp_bits << 16 | + bankgrp_bits << 24, + &mctl_ctl->addrmap[2]); + + switch (col_bits) { + case 8: + writel(bankgrp_bits | bankgrp_bits << 8 | 0x1f << 16 | + 0x1f << 24, + &mctl_ctl->addrmap[3]); + writel(0x1f | 0x1f << 8, &mctl_ctl->addrmap[4]); + break; + case 9: + writel(bankgrp_bits | bankgrp_bits << 8 | bankgrp_bits << 16 | + 0x1f << 24, + &mctl_ctl->addrmap[3]); + writel(0x1f | 0x1f << 8, &mctl_ctl->addrmap[4]); + break; + case 10: + writel(bankgrp_bits | bankgrp_bits << 8 | bankgrp_bits << 16 | + bankgrp_bits << 24, + &mctl_ctl->addrmap[3]); + writel(0x1f | 0x1f << 8, &mctl_ctl->addrmap[4]); + break; + case 11: + writel(bankgrp_bits | bankgrp_bits << 8 | bankgrp_bits << 16 | + bankgrp_bits << 24, + &mctl_ctl->addrmap[3]); + writel(bankgrp_bits | 0x1f << 8, &mctl_ctl->addrmap[4]); + break; + case 12: + writel(bankgrp_bits | bankgrp_bits << 8 | bankgrp_bits << 16 | + bankgrp_bits << 24, + &mctl_ctl->addrmap[3]); + writel(bankgrp_bits | bankgrp_bits << 8, &mctl_ctl->addrmap[4]); + break; + default: + panic("Unsupported dram configuration (col_bits = %d)", + col_bits); + } + + /* Banks */ + if (bank_bits == 3) { + writel(addrmap_bank_bx | addrmap_bank_bx << 8 | + addrmap_bank_bx << 16, + &mctl_ctl->addrmap[1]); + } else { + writel(addrmap_bank_bx | addrmap_bank_bx << 8 | 0x3f << 16, + &mctl_ctl->addrmap[1]); + } + + /* Rows */ + writel(addrmap_row_bx | addrmap_row_bx << 8 | addrmap_row_bx << 16 | + addrmap_row_bx << 24, + &mctl_ctl->addrmap[5]); + + switch (row_bits) { + case 14: + writel(addrmap_row_bx | addrmap_row_bx << 8 | 0x0f << 16 | + 0x0f << 24, + &mctl_ctl->addrmap[6]); + writel(0x0f | 0x0f << 8, &mctl_ctl->addrmap[7]); + break; + case 15: + if ((rank_bits == 1 && col_bits == 11) || + (rank_bits == 2 && col_bits == 10)) { + writel(addrmap_row_bx | (addrmap_row_bx + 1) << 8 | + (addrmap_row_bx + 1) << 16 | 0x0f << 24, + &mctl_ctl->addrmap[6]); + } else { + writel(addrmap_row_bx | addrmap_row_bx << 8 | + addrmap_row_bx << 16 | 0x0f << 24, + &mctl_ctl->addrmap[6]); + } + writel(0x0f | 0x0f << 8, &mctl_ctl->addrmap[7]); + break; + case 16: + if (rank_bits == 1 && col_bits == 10) { + writel((addrmap_row_bx + 1) | + (addrmap_row_bx + 1) << 8 | + (addrmap_row_bx + 1) << 16 | + (addrmap_row_bx + 1) << 24, + &mctl_ctl->addrmap[6]); + } else { + writel(addrmap_row_bx | addrmap_row_bx << 8 | + addrmap_row_bx << 16 | + addrmap_row_bx << 24, + &mctl_ctl->addrmap[6]); + } + writel(0x0f | 0x0f << 8, &mctl_ctl->addrmap[7]); + break; + case 17: + writel(addrmap_row_bx | addrmap_row_bx << 8 | + addrmap_row_bx << 16 | addrmap_row_bx << 24, + &mctl_ctl->addrmap[6]); + writel(addrmap_row_bx | 0x0f << 8, &mctl_ctl->addrmap[7]); + break; + case 18: + writel(addrmap_row_bx | addrmap_row_bx << 8 | + addrmap_row_bx << 16 | addrmap_row_bx << 24, + &mctl_ctl->addrmap[6]); + writel(addrmap_row_bx | addrmap_row_bx << 8, + &mctl_ctl->addrmap[7]); + break; + default: + panic("Unsupported dram configuration (row_bits = %d)", + row_bits); + } + + /* Ranks */ + if (rank_bits == 0) { + writel(0x1f, &mctl_ctl->addrmap[0]); + } else if ((rank_bits + col_bits + row_bits) == 27) { + writel(addrmap_row_bx + row_bits - 2, &mctl_ctl->addrmap[0]); + } else { + writel(addrmap_row_bx + row_bits, &mctl_ctl->addrmap[0]); + } +} + +static void mctl_com_init(const struct dram_para *para, + const struct dram_config *config) +{ + struct sunxi_mctl_com_reg *mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg *mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + /* Might control power/reset of DDR-related blocks */ + clrsetbits_le32(&mctl_com->unk_0x008, BIT(24), BIT(25) | BIT(9)); + + /* Unlock mctl_ctl registers */ + setbits_le32(&mctl_com->maer0, BIT(15)); + + if (para->type == SUNXI_DRAM_TYPE_LPDDR4) + setbits_le32(0x03102ea8, BIT(0)); + + clrsetbits_le32(&mctl_ctl->sched[0], 0xff << 8, 0x30 << 8); + if (!(para->tpr13 & BIT(28))) + clrsetbits_le32(&mctl_ctl->sched[0], 0xf, BIT(0)); + + writel(0, &mctl_ctl->hwlpctl); + + /* Master settings */ + u32 mstr_value = MSTR_DEVICECONFIG_X32 | + MSTR_ACTIVE_RANKS(config->ranks); + + if (config->bus_full_width) + mstr_value |= MSTR_BUSWIDTH_FULL; + else + mstr_value |= MSTR_BUSWIDTH_HALF; + + /* + * Geardown and 2T mode are always enabled here, but is controlled by a flag in boot0; + * it has not been a problem so far, but may be suspect if a particular board isn't booting. + */ + switch (para->type) { + case SUNXI_DRAM_TYPE_DDR3: + mstr_value |= MSTR_DEVICETYPE_DDR3 | MSTR_BURST_LENGTH(8) | + MSTR_2TMODE; + break; + case SUNXI_DRAM_TYPE_DDR4: + mstr_value |= MSTR_DEVICETYPE_DDR4 | MSTR_BURST_LENGTH(8) | + MSTR_GEARDOWNMODE | MSTR_2TMODE; + break; + case SUNXI_DRAM_TYPE_LPDDR3: + mstr_value |= MSTR_DEVICETYPE_LPDDR3 | MSTR_BURST_LENGTH(8); + break; + case SUNXI_DRAM_TYPE_LPDDR4: + mstr_value |= MSTR_DEVICETYPE_LPDDR4 | MSTR_BURST_LENGTH(16); + break; + } + + writel(mstr_value, &mctl_ctl->mstr); + + mctl_set_odtmap(para, config); + mctl_set_addrmap(config); + mctl_set_timing_params(para); + + writel(0, &mctl_ctl->pwrctl); + + /* Disable automatic controller updates + automatic controller update requests */ + setbits_le32(&mctl_ctl->dfiupd[0], BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->zqctl[0], BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->unk_0x2180, BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->unk_0x3180, BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->unk_0x4180, BIT(31) | BIT(30)); + + /* + * Data bus inversion + * Controlled by a flag in boot0, enabled by default here. + */ +// if (para->type == SUNXI_DRAM_TYPE_DDR4 || +// para->type == SUNXI_DRAM_TYPE_LPDDR4) +// setbits_le32(&mctl_ctl->dbictl, BIT(2)); +} + +static void mctl_drive_odt_config(const struct dram_para *para) +{ + u32 val; + u64 base; + u32 i; + + /* DX drive */ + for (i = 0; i < 4; i++) { + base = SUNXI_DRAM_PHY0_BASE + 0x388 + 0x40 * i; + val = (para->dx_dri >> (i * 8)) & 0x1f; + + writel(val, base); + if (para->type == SUNXI_DRAM_TYPE_LPDDR4) { + if (para->tpr3 & 0x1f1f1f1f) + val = (para->tpr3 >> (i * 8)) & 0x1f; + else + val = 4; + } + writel(val, base + 4); + } + + /* CA drive */ + for (i = 0; i < 2; i++) { + base = SUNXI_DRAM_PHY0_BASE + 0x340 + 0x8 * i; + val = (para->ca_dri >> (i * 8)) & 0x1f; + + writel(val, base); + writel(val, base + 4); + } + + /* DX ODT */ + for (i = 0; i < 4; i++) { + base = SUNXI_DRAM_PHY0_BASE + 0x380 + 0x40 * i; + val = (para->dx_odt >> (i * 8)) & 0x1f; + + if (para->type == SUNXI_DRAM_TYPE_DDR4 || + para->type == SUNXI_DRAM_TYPE_LPDDR3) + writel(0, base); + else + writel(val, base); + + if (para->type == SUNXI_DRAM_TYPE_LPDDR4) + writel(0, base + 4); + else + writel(val, base + 4); + } +} + +static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para) +{ + u32 val, i; + u32 *ptr; + + if (para->tpr10 & BIT(31)) { + val = para->tpr2; + } else { + val = ((para->tpr10 << 1) & 0x1e) | + ((para->tpr10 << 5) & 0x1e00) | + ((para->tpr10 << 9) & 0x1e0000) | + ((para->tpr10 << 13) & 0x1e000000); + + if (para->tpr10 >> 29 != 0) + val <<= 1; + } + + ptr = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x780); + for (i = 0; i < 32; i++) + writel((val >> 8) & 0x3f, &ptr[i]); + + writel(val & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x7dc); + writel(val & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x7e0); + + switch (para->type) { + case SUNXI_DRAM_TYPE_DDR3: + writel((val >> 16) & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x7b8); + writel((val >> 24) & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x784); + break; + case SUNXI_DRAM_TYPE_DDR4: + writel((val >> 16) & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x784); + break; + case SUNXI_DRAM_TYPE_LPDDR3: + writel((val >> 16) & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x788); + writel((val >> 24) & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x790); + break; + case SUNXI_DRAM_TYPE_LPDDR4: + writel((val >> 16) & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x790); + writel((val >> 24) & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x78c); + break; + } +} + +static void mctl_phy_init(const struct dram_para *para, + const struct dram_config *config) +{ + struct sunxi_mctl_ctl_reg *mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + const struct sunxi_prcm_reg *prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + struct sunxi_mctl_com_reg *mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + u32 val, val2, i; + u32 *ptr; + + /* Disable auto refresh. */ + setbits_le32(&mctl_ctl->rfshctl3, BIT(0)); + + /* Set "phy_dbi_mode" to mark the DFI as implementing DBI functionality */ + writel(0, &mctl_ctl->pwrctl); + clrbits_le32(&mctl_ctl->dfimisc, 1); + writel(0x20, &mctl_ctl->pwrctl); + + /* PHY cold reset */ + clrsetbits_le32(&mctl_com->unk_0x008, BIT(24), BIT(9)); + udelay(1); + setbits_le32(&mctl_com->unk_0x008, BIT(24)); + + /* Not sure what this gates the power of. */ + clrbits_le32(&prcm->sys_pwroff_gating, BIT(4)); + + if (para->type == SUNXI_DRAM_TYPE_LPDDR4) + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4, BIT(7)); + + /* Note: Similar enumeration of values is used during read training */ + if (config->bus_full_width) + val = 0xf; + else + val = 0x3; + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val); + + switch (para->type) { + case SUNXI_DRAM_TYPE_DDR3: + val = 13; + val2 = 9; + break; + case SUNXI_DRAM_TYPE_DDR4: + val = 13; + val2 = 10; + break; + case SUNXI_DRAM_TYPE_LPDDR3: + val = 14; + val2 = 8; + break; + case SUNXI_DRAM_TYPE_LPDDR4: + if (para->tpr13 & BIT(28)) + val = 22; + else + val = 20; + val2 = 10; + break; + } + + writel(val, SUNXI_DRAM_PHY0_BASE + 0x14); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x35c); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x368); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x374); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x18); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x360); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x36c); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x378); + writel(val2, SUNXI_DRAM_PHY0_BASE + 0x1c); + writel(val2, SUNXI_DRAM_PHY0_BASE + 0x364); + writel(val2, SUNXI_DRAM_PHY0_BASE + 0x370); + writel(val2, SUNXI_DRAM_PHY0_BASE + 0x37c); + + /* boot0 does this in "phy_set_address_remapping". Seems odd for an address map table, though. */ + ptr = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xc0); + for (i = 0; i < ARRAY_SIZE(phy_init); i++) + writel(phy_init[i], &ptr[i]); + + /* Set VREF */ + val = 0; + switch (para->type) { + case SUNXI_DRAM_TYPE_DDR3: + val = para->tpr6 & 0xff; + if (val == 0) + val = 0x80; + break; + case SUNXI_DRAM_TYPE_DDR4: + val = (para->tpr6 >> 8) & 0xff; + if (val == 0) + val = 0x80; + break; + case SUNXI_DRAM_TYPE_LPDDR3: + val = (para->tpr6 >> 16) & 0xff; + if (val == 0) + val = 0x80; + break; + case SUNXI_DRAM_TYPE_LPDDR4: + val = (para->tpr6 >> 24) & 0xff; + if (val == 0) + val = 0x33; + break; + } + writel(val, SUNXI_DRAM_PHY0_BASE + 0x3dc); + writel(val, SUNXI_DRAM_PHY0_BASE + 0x45c); + + mctl_drive_odt_config(para); + + if (para->tpr10 & TPR10_CA_BIT_DELAY) + mctl_phy_ca_bit_delay_compensation(para); + + switch (para->type) { + case SUNXI_DRAM_TYPE_DDR3: + val = 2; + break; + case SUNXI_DRAM_TYPE_LPDDR3: + val = 3; + break; + case SUNXI_DRAM_TYPE_DDR4: + val = 4; + break; + case SUNXI_DRAM_TYPE_LPDDR4: + val = 5; + break; + } + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4, 0x7, val | 8); + + if (para->clk <= 672) + writel(0xf, SUNXI_DRAM_PHY0_BASE + 0x20); + + if (para->clk > 500) { + val = 0; + val2 = 0; + } else { + val = 0x80; + val2 = 0x20; + } + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x144, 0x80, val); + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, 0xe0, val2); + + clrbits_le32(&mctl_com->unk_0x008, BIT(9)); + udelay(1); + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, BIT(3)); + + mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x180), BIT(2), + BIT(2)); + + /* This is controlled by a tpr13 flag in boot0; doesn't hurt to always do it though. */ + udelay(1000); + writel(0x37, SUNXI_DRAM_PHY0_BASE + 0x58); + + setbits_le32(&prcm->sys_pwroff_gating, BIT(4)); +} + +/* Helpers for updating mode registers */ +static inline void mctl_mr_write(u32 mrctrl0, u32 mrctrl1) +{ + struct sunxi_mctl_ctl_reg *mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + writel(mrctrl1, &mctl_ctl->mrctrl1); + writel(mrctrl0 | MRCTRL0_MR_WR | MRCTRL0_MR_RANKS_ALL, + &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, MRCTRL0_MR_WR, 0); +} + +static inline void mctl_mr_write_lpddr4(u8 addr, u8 value) +{ + mctl_mr_write(0, MRCTRL1_MR_ADDR(addr) | MRCTRL1_MR_DATA(value)); +} + +static inline void mctl_mr_write_lpddr3(u8 addr, u8 value) +{ + /* Bit [7:6] are set by boot0, but undocumented */ + mctl_mr_write(BIT(6) | BIT(7), + MRCTRL1_MR_ADDR(addr) | MRCTRL1_MR_DATA(value)); +} + +static void mctl_dfi_init(const struct dram_para *para) +{ + struct sunxi_mctl_com_reg *mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg *mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + /* Unlock DFI registers? */ + setbits_le32(&mctl_com->maer0, BIT(8)); + + /* Enable dfi_init_complete signal and trigger PHY init start request */ + writel(0, &mctl_ctl->swctl); + setbits_le32(&mctl_ctl->dfimisc, BIT(0)); + setbits_le32(&mctl_ctl->dfimisc, BIT(5)); + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0)); + + /* Stop sending init request and wait for DFI initialization to complete. */ + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->dfimisc, BIT(5)); + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0)); + mctl_await_completion(&mctl_ctl->dfistat, BIT(0), BIT(0)); + + /* Enter Software Exit from Self Refresh */ + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->pwrctl, BIT(5)); + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0)); + mctl_await_completion(&mctl_ctl->statr, 0x3, 1); + + udelay(200); + + /* Disable dfi_init_complete signal */ + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->dfimisc, BIT(0)); + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0)); + + /* Write mode registers */ + switch (para->type) { + case SUNXI_DRAM_TYPE_DDR3: + mctl_mr_write(MRCTRL0_MR_ADDR(0), para->mr0); + mctl_mr_write(MRCTRL0_MR_ADDR(1), para->mr1); + mctl_mr_write(MRCTRL0_MR_ADDR(2), para->mr2); + mctl_mr_write(MRCTRL0_MR_ADDR(3), para->mr3); + break; + case SUNXI_DRAM_TYPE_DDR4: + mctl_mr_write(MRCTRL0_MR_ADDR(0), para->mr0); + mctl_mr_write(MRCTRL0_MR_ADDR(1), para->mr1); + mctl_mr_write(MRCTRL0_MR_ADDR(2), para->mr2); + mctl_mr_write(MRCTRL0_MR_ADDR(3), para->mr3); + mctl_mr_write(MRCTRL0_MR_ADDR(4), para->mr4); + mctl_mr_write(MRCTRL0_MR_ADDR(5), para->mr5); + + mctl_mr_write(MRCTRL0_MR_ADDR(6), para->mr6 | BIT(7)); + mctl_mr_write(MRCTRL0_MR_ADDR(6), para->mr6 | BIT(7)); + mctl_mr_write(MRCTRL0_MR_ADDR(6), para->mr6 & (~BIT(7))); + break; + case SUNXI_DRAM_TYPE_LPDDR3: + mctl_mr_write_lpddr3(1, para->mr1); + mctl_mr_write_lpddr3(2, para->mr2); + mctl_mr_write_lpddr3(3, para->mr3); + mctl_mr_write_lpddr3(11, para->mr11); + break; + case SUNXI_DRAM_TYPE_LPDDR4: + mctl_mr_write_lpddr4(0, para->mr0); + mctl_mr_write_lpddr4(1, para->mr1); + mctl_mr_write_lpddr4(2, para->mr2); + mctl_mr_write_lpddr4(3, para->mr3); + mctl_mr_write_lpddr4(4, para->mr4); + mctl_mr_write_lpddr4(11, para->mr11); + mctl_mr_write_lpddr4(12, para->mr12); + mctl_mr_write_lpddr4(13, para->mr13); + mctl_mr_write_lpddr4(14, para->mr14); + mctl_mr_write_lpddr4(22, para->tpr1); + break; + } + + writel(0, SUNXI_DRAM_PHY0_BASE + 0x54); + + /* Re-enable controller refresh */ + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->rfshctl3, BIT(0)); + writel(1, &mctl_ctl->swctl); +} + +/* Slightly modified from H616 driver */ +static bool mctl_phy_read_calibration(const struct dram_config *config) +{ + bool result = true; + u32 val, tmp; + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x20); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); + + if (config->bus_full_width) + val = 0xf; + else + val = 3; + + while ((readl(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != val) { + if (readl(SUNXI_DRAM_PHY0_BASE + 0x184) & 0x20) { + result = false; + break; + } + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30); + + if (config->ranks == 1) { + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x10); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); + + while ((readl(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != val) { + if (readl(SUNXI_DRAM_PHY0_BASE + 0x184) & 0x20) { + result = false; + break; + } + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30); + + val = readl(SUNXI_DRAM_PHY0_BASE + 0x274) & 7; + tmp = readl(SUNXI_DRAM_PHY0_BASE + 0x26c) & 7; + if (val < tmp) + val = tmp; + tmp = readl(SUNXI_DRAM_PHY0_BASE + 0x32c) & 7; + if (val < tmp) + val = tmp; + tmp = readl(SUNXI_DRAM_PHY0_BASE + 0x334) & 7; + if (val < tmp) + val = tmp; + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x38, 0x7, (val + 2) & 7); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x20); + + return result; +} + +static inline void mctl_phy_dx_delay1_inner(u32 *base, u32 val1, u32 val2) +{ + u32 *ptr = base; + + for (int i = 0; i < 9; i++) { + writel(val1, ptr); + writel(val1, ptr + 0x30); + ptr += 2; + } + + writel(val2, ptr + 1); + writel(val2, ptr + 49); + writel(val2, ptr); + writel(val2, ptr + 48); +} + +static inline void mctl_phy_dx_delay0_inner(u32 *base1, u32 *base2, u32 val1, + u32 val2) +{ + u32 *ptr = base1; + for (int i = 0; i < 9; i++) { + writel(val1, ptr); + writel(val1, ptr + 0x30); + ptr += 2; + } + + writel(val2, base2); + writel(val2, base2 + 48); + writel(val2, ptr); + writel(val2, base2 + 44); +} + +/* This might be somewhat transferable to H616; whether or not people like the design is another question */ +static void mctl_phy_dx_delay_compensation(const struct dram_para *para) +{ + if (para->tpr10 & TPR10_DX_BIT_DELAY1) { + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1); + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, BIT(3)); + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, BIT(4)); + + if (para->type == SUNXI_DRAM_TYPE_DDR4) + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4, BIT(7)); + + mctl_phy_dx_delay1_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x484), + para->tpr11 & 0x3f, + para->para0 & 0x3f); + mctl_phy_dx_delay1_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x4d8), + (para->tpr11 >> 8) & 0x3f, + (para->para0 >> 8) & 0x3f); + mctl_phy_dx_delay1_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x604), + (para->tpr11 >> 16) & 0x3f, + (para->para0 >> 16) & 0x3f); + mctl_phy_dx_delay1_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x658), + (para->tpr11 >> 24) & 0x3f, + (para->para0 >> 24) & 0x3f); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1); + } + + if (para->tpr10 & TPR10_DX_BIT_DELAY0) { + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, BIT(7)); + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, BIT(2)); + + mctl_phy_dx_delay0_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x480), + (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x528), + para->tpr12 & 0x3f, + para->tpr14 & 0x3f); + + mctl_phy_dx_delay0_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x4d4), + (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x52c), + (para->tpr12 >> 8) & 0x3f, + (para->tpr14 >> 8) & 0x3f); + + mctl_phy_dx_delay0_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x600), + (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x6a8), + (para->tpr12 >> 16) & 0x3f, + (para->tpr14 >> 16) & 0x3f); + + mctl_phy_dx_delay0_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x6ac), + (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x528), + (para->tpr12 >> 24) & 0x3f, + (para->tpr14 >> 24) & 0x3f); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, BIT(7)); + } +} + +static bool mctl_calibrate_phy(const struct dram_para *para, + const struct dram_config *config) +{ + struct sunxi_mctl_ctl_reg *mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + int i; + + /* TODO: Implement write levelling */ + if (para->tpr10 & TPR10_READ_CALIBRATION) { + for (i = 0; i < 5; i++) + if (mctl_phy_read_calibration(config)) + break; + if (i == 5) { + debug("read calibration failed"); + return false; + } + } + + /* TODO: Implement read training leveling */ + /* TODO: Implement write training */ + + mctl_phy_dx_delay_compensation(para); + /* TODO: Implement DFS */ + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, BIT(0)); + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, 7); + + /* Q: Does self-refresh get disabled by a calibration? */ + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->rfshctl3, BIT(1)); + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0)); + + return true; +} + +static bool mctl_core_init(const struct dram_para *para, + const struct dram_config *config) +{ + mctl_clk_init(para->clk); + mctl_com_init(para, config); + mctl_phy_init(para, config); + mctl_dfi_init(para); + + return mctl_calibrate_phy(para, config); +} + +/* Heavily inspired from H616 driver. UNUSED */ +/* static */ void auto_detect_ranks(const struct dram_para *para, + struct dram_config *config) +{ + int i; + bool found_config; + + config->cols = 9; + config->rows = 14; + config->ranks = 0; + config->banks = 0; + config->bankgrps = 0; + + /* Test ranks */ + found_config = false; + for (i = 1; i >= 0; i--) { + config->ranks = i; + config->bus_full_width = true; + debug("Testing ranks = %d, 32-bit bus\n", i); + if (mctl_core_init(para, config)) { + found_config = true; + break; + } + + config->bus_full_width = false; + debug("Testing ranks = %d, 16-bit bus\n", i); + if (mctl_core_init(para, config)) { + found_config = true; + break; + } + } + + debug("Found ranks = %d\n", config->ranks); +} + +/* UNUSED? */ +/* static */ void mctl_auto_detect_dram_size(const struct dram_para *para, + struct dram_config *config) +{ + unsigned int shift; + + /* max config for bankgrps, minimum for everything else */ + config->bankgrps = 2; + config->cols = 8; + config->banks = 0; + config->rows = 14; + mctl_core_init(para, config); + + shift = config->bus_full_width + 1; + + /* detect bank group address bits */ + for (config->bankgrps = 0; config->bankgrps < 2; config->bankgrps++) { + writel(config->bankgrps, CFG_SYS_SDRAM_BASE); + for (long i = 0; i < 0x100; i += 4) { + debug("[%lx] = %x\n", i, readl(CFG_SYS_SDRAM_BASE + i)); + } + if (mctl_mem_matches(3ULL << (config->bankgrps + shift + 1))) + break; + } + + debug("detected %u bank groups\n", config->bankgrps); + + /* reconfigure to make sure all active columns are accessible */ + config->cols = 12; + mctl_core_init(para, config); + + /* detect column address bits */ + shift += config->bankgrps; + for (config->cols = 8; config->cols < 12; config->cols++) { + if (mctl_mem_matches(1ULL << (config->cols + shift))) + break; + } + debug("detected %u columns\n", config->cols); + + /* reconfigure to make sure that all active banks are accessible */ + config->banks = 3; + mctl_core_init(para, config); + debug("detected %u banks\n", config->bankgrps); + + /* detect bank bits */ + shift += config->cols; + for (config->banks = 0; config->banks < 3; config->banks++) { + if (mctl_mem_matches(1ULL << (config->banks + shift))) + break; + } + + /* reconfigure to make sure that all active rows are accessible */ + config->rows = 18; + mctl_core_init(para, config); + + /* detect row address bits */ + shift += config->banks; + for (config->rows = 14; config->rows < 18; config->rows++) { + if (mctl_mem_matches(1ULL << (config->rows + shift))) + break; + } + debug("detected %u rows\n", config->rows); +} + +/* Modified from H616 driver, UNUSED? */ +/* static */ void auto_detect_size(const struct dram_para *para, + struct dram_config *config) +{ + /* detect row address bits */ + config->cols = 8; + config->rows = 18; + config->banks = 0; + config->bankgrps = 0; + mctl_core_init(para, config); + + for (config->rows = 14; config->rows < 18; config->rows++) { + /* 8 banks, 8 bit per byte and 16/32 bit width */ + if (mctl_mem_matches((1 << (config->bankgrps + config->banks + + config->cols + config->rows + + config->bus_full_width + 1)))) + break; + } + + /* detect column address bits */ + config->cols = 12; + mctl_core_init(para, config); + + for (config->cols = 8; config->cols < 12; config->cols++) { + /* 8 bits per byte and 16/32 bit width */ + if (mctl_mem_matches(1 << (config->bankgrps + config->banks + + config->cols + + config->bus_full_width + 1))) + break; + } + + /* detect bank address bits */ + config->banks = 3; + mctl_core_init(para, config); + + for (config->banks = 0; config->banks < 3; config->banks++) { + if (mctl_mem_matches(1 << (config->banks + config->bankgrps + + config->cols + + config->bus_full_width + 1))) + break; + } + + /* TODO: This needs further testing on devices with different numbers of banks! */ + /* detect bank group address bits */ + config->bankgrps = 2; + mctl_core_init(para, config); + for (config->bankgrps = 0; config->bankgrps < 2; config->bankgrps++) { + if (mctl_mem_matches_base(3 << (config->bankgrps + 2 + + config->bus_full_width), + CFG_SYS_SDRAM_BASE + 0x10)) + break; + } +} + +/* Modified from H616 driver to add banks and bank groups */ +static unsigned long calculate_dram_size(const struct dram_config *config) +{ + /* Bootrom only uses x32 or x16 bus widths */ + u8 width = config->bus_full_width ? 4 : 2; + + return (1ULL << (config->cols + config->rows + config->banks + + config->bankgrps)) * + width * (1ULL << config->ranks); +} + +static const struct dram_para para = { + .clk = CONFIG_DRAM_CLK, +#ifdef CONFIG_SUNXI_DRAM_DDR3 + .type = SUNXI_DRAM_TYPE_DDR3, +#elif defined(CONFIG_SUNXI_DRAM_DDR4) + .type = SUNXI_DRAM_TYPE_DDR4, +#elif defined(CONFIG_SUNXI_DRAM_LPDDR3) + .type = SUNXI_DRAM_TYPE_LPDDR3, +#elif defined(CONFIG_SUNXI_DRAM_LPDDR4) + .type = SUNXI_DRAM_TYPE_LPDDR4, +#endif + /* TODO: Populate from config */ + .dx_odt = CONFIG_DRAM_SUNXI_DX_ODT, + .dx_dri = CONFIG_DRAM_SUNXI_DX_DRI, + .ca_dri = CONFIG_DRAM_SUNXI_CA_DRI, + .para0 = CONFIG_DRAM_SUNXI_PARA0, + .para1 = CONFIG_DRAM_SUNXI_PARA1, + .para2 = CONFIG_DRAM_SUNXI_PARA2, + .mr0 = CONFIG_DRAM_SUNXI_MR0, + .mr1 = CONFIG_DRAM_SUNXI_MR1, + .mr2 = CONFIG_DRAM_SUNXI_MR2, + .mr3 = CONFIG_DRAM_SUNXI_MR3, + .mr4 = CONFIG_DRAM_SUNXI_MR4, + .mr5 = CONFIG_DRAM_SUNXI_MR5, + .mr6 = CONFIG_DRAM_SUNXI_MR6, + .mr11 = CONFIG_DRAM_SUNXI_MR11, + .mr12 = CONFIG_DRAM_SUNXI_MR12, + .mr13 = CONFIG_DRAM_SUNXI_MR13, + .mr14 = CONFIG_DRAM_SUNXI_MR14, + .mr16 = CONFIG_DRAM_SUNXI_MR16, + .mr17 = CONFIG_DRAM_SUNXI_MR17, + .tpr1 = CONFIG_DRAM_SUNXI_TPR1, + .tpr2 = CONFIG_DRAM_SUNXI_TPR2, + .tpr3 = CONFIG_DRAM_SUNXI_TPR3, + .tpr6 = CONFIG_DRAM_SUNXI_TPR6, + .tpr10 = CONFIG_DRAM_SUNXI_TPR10, + .tpr11 = CONFIG_DRAM_SUNXI_TPR11, + .tpr12 = CONFIG_DRAM_SUNXI_TPR12, + .tpr13 = CONFIG_DRAM_SUNXI_TPR13, + .tpr14 = CONFIG_DRAM_SUNXI_TPR14, +}; + +/* TODO: Remove, copied and modified slightly from aodzip repo as temporary sanity check */ +static int libdram_dramc_simple_wr_test(uint32_t dram_size, uint32_t test_range) +{ + uint32_t *dram_memory = (uint32_t *)CFG_SYS_SDRAM_BASE; + uint32_t step = dram_size / 8; + + for (unsigned i = 0; i < test_range; i++) { + dram_memory[i] = i + 0x1234567; + dram_memory[i + step] = i - 0x1234568; + } + + for (unsigned i = 0; i < test_range; i++) { + uint32_t *ptr; + if (dram_memory[i] != i + 0x1234567) { + ptr = &dram_memory[i]; + goto fail; + } + if (dram_memory[i + step] != i - 0x1234568) { + ptr = &dram_memory[i + step]; + goto fail; + } + continue; +fail: + debug("DRAM simple test FAIL----- address %p = %d\n", ptr, + readl(ptr)); + return 1; + } + + debug("DRAM simple test OK.\n"); + return 0; +} + +unsigned long sunxi_dram_init(void) +{ + unsigned long size; + + /* Keeping for now as documentation of where different parameters come from */ + struct dram_config config = { + .cols = (para.para1 & 0xF), + .rows = (para.para1 >> 4) & 0xFF, + .banks = (para.para1 >> 12) & 0x3, + .bankgrps = (para.para1 >> 14) & 0x3, + .ranks = ((para.tpr13 >> 16) & 3), + .bus_full_width = !((para.para2 >> 3) & 1), + }; + + /* Writing to undocumented SYS_CFG area, according to user manual. */ + setbits_le32(0x03000160, BIT(8)); + clrbits_le32(0x03000168, 0x3f); + + /* TODO: Figure out how to catch bank group errors. */ + // auto_detect_ranks(¶, &config); + // mctl_auto_detect_dram_size(¶, &config); + + if (!mctl_core_init(¶, &config)) + return 0; + + debug("cols = %d, rows = %d, banks = %d, bankgrps = %d, ranks = %d, full_width = %d\n", + config.cols, config.rows, config.banks, config.bankgrps, + config.ranks, config.bus_full_width); + + size = calculate_dram_size(&config); + + /* TODO: This is just a sanity check for now. */ + if (libdram_dramc_simple_wr_test(size, 4096)) + return 0; + + return size; +} diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile index 5f203419240..4dc1f29fc08 100644 --- a/arch/arm/mach-sunxi/dram_timings/Makefile +++ b/arch/arm/mach-sunxi/dram_timings/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333) += h616_ddr3_1333.o obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4) += h616_lpddr4_2133.o +obj-$(CONFIG_SUNXI_DRAM_A133_DDR4) += a133_ddr4.o +obj-$(CONFIG_SUNXI_DRAM_A133_LPDDR4) += a133_lpddr4.o diff --git a/arch/arm/mach-sunxi/dram_timings/a133_ddr4.c b/arch/arm/mach-sunxi/dram_timings/a133_ddr4.c new file mode 100644 index 00000000000..1bfc1f7a3af --- /dev/null +++ b/arch/arm/mach-sunxi/dram_timings/a133_ddr4.c @@ -0,0 +1,80 @@ +#include <asm/arch/cpu.h> +#include <asm/arch/dram.h> + +void mctl_set_timing_params(const struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg *const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u8 txsr = 4; + u8 tccd = 3; + u8 rd2wr = 5; + u8 tmrd = 4; + u8 tmrw = 0; + u8 wrlat = 5; + u8 rdlat = 7; + u8 wr2pre = 14; + u8 dfi_tphy_wrlat = 6; + u8 dfi_trddata_en = 10; + + u8 tfaw = ns_to_t(35); + u8 trrd = max(ns_to_t(8), 2); + u8 txp = max(ns_to_t(6), 2); + u8 tmrd_pda = max(ns_to_t(10), 8); + u8 trp = ns_to_t(15); + u8 trc = ns_to_t(49); + u8 wr2rd_s = max(ns_to_t(3), 1) + 7; + u8 tras_min = ns_to_t(34); + u16 trefi_x32 = ns_to_t(7800) / 32; + u16 trfc_min = ns_to_t(350); + u16 txs_x32 = ns_to_t(360) / 32; + u16 tmod = max(ns_to_t(15), 12); + u8 tcke = max(ns_to_t(5), 2); + u8 tcksrx = max(ns_to_t(10), 3); + u8 txs_abort_x32 = ns_to_t(170) / 32; + u8 tras_max = ns_to_t(70200) / 1024; + + u8 rd2pre = (trp < 5 ? 9 - trp : 4); + u8 wr2rd = trrd + 7; + u8 tckesr = tcke + 1; + u8 trcd = trp; + u8 trrd_s = txp; + u8 tcksre = tcksrx; + + writel(tras_min | tras_max << 8 | tfaw << 16 | wr2pre << 24, + &mctl_ctl->dramtmg[0]); + writel(trc | rd2pre << 8 | txp << 16, &mctl_ctl->dramtmg[1]); + writel(wr2rd | rd2wr << 8 | rdlat << 16 | wrlat << 24, + &mctl_ctl->dramtmg[2]); + writel(tmod | tmrd << 12 | tmrw << 20, &mctl_ctl->dramtmg[3]); + writel(trp | trrd << 8 | tccd << 16 | trcd << 24, + &mctl_ctl->dramtmg[4]); + writel(tcke | tckesr << 8 | tcksre << 16 | tcksrx << 24, + &mctl_ctl->dramtmg[5]); + writel((txp + 2) | 0x20 << 16 | 0x20 << 24, + &mctl_ctl->dramtmg[6]); + writel(txs_x32 | 0x10 << 8 | txs_abort_x32 << 16 | txs_abort_x32 << 24, + &mctl_ctl->dramtmg[8]); + writel(wr2rd_s | trrd_s << 8 | 0x2 << 16, &mctl_ctl->dramtmg[9]); + writel(0xe0c05, &mctl_ctl->dramtmg[10]); + writel(0x440c021c, &mctl_ctl->dramtmg[11]); + writel(tmrd_pda, &mctl_ctl->dramtmg[12]); + writel(0xa100002, &mctl_ctl->dramtmg[13]); + writel(txsr, &mctl_ctl->dramtmg[14]); + + clrsetbits_le32(&mctl_ctl->init[0], 0xc0000fff, 1008); + writel(0x1f20000, &mctl_ctl->init[1]); + clrsetbits_le32(&mctl_ctl->init[2], 0xff0f, 0xd05); + writel(0, &mctl_ctl->dfimisc); + + writel(para->mr0 << 16 | para->mr1, &mctl_ctl->init[3]); + writel(para->mr2 << 16 | para->mr3, &mctl_ctl->init[4]); + writel(para->mr4 << 16 | para->mr5, &mctl_ctl->init[6]); + writel(para->mr6, &mctl_ctl->init[7]); + + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); + writel((dfi_tphy_wrlat - 1) | 0x2000000 | (dfi_trddata_en - 1) << 16 | + 0x808000, &mctl_ctl->dfitmg0); + writel(0x100202, &mctl_ctl->dfitmg1); + writel(trfc_min | trefi_x32 << 16, &mctl_ctl->rfshtmg); +} diff --git a/arch/arm/mach-sunxi/dram_timings/a133_lpddr4.c b/arch/arm/mach-sunxi/dram_timings/a133_lpddr4.c new file mode 100644 index 00000000000..7b06d428b6a --- /dev/null +++ b/arch/arm/mach-sunxi/dram_timings/a133_lpddr4.c @@ -0,0 +1,102 @@ +#include <asm/arch/cpu.h> +#include <asm/arch/dram.h> + +void mctl_set_timing_params(const struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg *const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + bool tpr13_flag1 = para->tpr13 & BIT(28); + bool tpr13_flag2 = para->tpr13 & BIT(3); + bool tpr13_flag3 = para->tpr13 & BIT(5); + + u8 tccd = 4; + u8 tfaw = ns_to_t(40); + u8 trrd = max(ns_to_t(10), 2); + u8 trcd = max(ns_to_t(18), 2); + u8 trc = ns_to_t(65); + u8 txp = max(ns_to_t(8), 2); + + u8 trp = ns_to_t(21); + u8 tras_min = ns_to_t(42); + u16 trefi_x32 = ns_to_t(3904) / 32; + u16 trfc_min = ns_to_t(180); + u16 txsr = ns_to_t(190); + + u8 tmrw = max(ns_to_t(14), 5); + u8 tmrd = max(ns_to_t(14), 5); + u8 tmod = 12; + u8 tcke = max(ns_to_t(15), 2); + u8 tcksrx = max(ns_to_t(2), 2); + u8 tcksre = max(ns_to_t(5), 2); + u8 tckesr = max(ns_to_t(15), 2); + u8 tras_max = (trefi_x32 * 9) / 32; + u8 txs_x32 = 4; + u8 txsabort_x32 = 4; + + u8 wrlat = 5; + u8 wr2rd_s = 8; + u8 trrd_s = 2; + u8 tmrd_pda = 8; + + u8 wr2pre = 24; + u8 rd2pre = 4; + u8 wr2rd = 14 + max(ns_to_t(tpr13_flag1 ? 10 : 12), 4); + u8 rd2wr = 17 + ns_to_t(4) - ns_to_t(1); + u8 tphy_wrlat = 5; + + u8 rdlat = 10; + u8 trddata_en = 17; + + if (tpr13_flag1) { + rdlat = 11; + trddata_en = 19; + } + + writel(tras_min | tras_max << 8 | tfaw << 16 | wr2pre << 24, + &mctl_ctl->dramtmg[0]); + writel(trc | rd2pre << 8 | txp << 16, &mctl_ctl->dramtmg[1]); + writel(wr2rd | rd2wr << 8 | rdlat << 16 | wrlat << 24, + &mctl_ctl->dramtmg[2]); + writel(tmod | tmrd << 12 | tmrw << 20, &mctl_ctl->dramtmg[3]); + writel(trp | trrd << 8 | tccd << 16 | trcd << 24, + &mctl_ctl->dramtmg[4]); + writel(tcke | tckesr << 8 | tcksre << 16 | tcksrx << 24, + &mctl_ctl->dramtmg[5]); + writel((txp + 2) | 0x20 << 16 | 0x20 << 24, &mctl_ctl->dramtmg[6]); + writel(txs_x32 | 0x10 << 8 | txsabort_x32 << 16 | txsabort_x32 << 24, + &mctl_ctl->dramtmg[8]); + writel(wr2rd_s | trrd_s << 8 | 0x2 << 16, &mctl_ctl->dramtmg[9]); + writel(0xe0c05, &mctl_ctl->dramtmg[10]); + writel(0x440c021c, &mctl_ctl->dramtmg[11]); + writel(tmrd_pda, &mctl_ctl->dramtmg[12]); + writel(0xa100002, &mctl_ctl->dramtmg[13]); + writel(txsr, &mctl_ctl->dramtmg[14]); + + clrsetbits_le32(&mctl_ctl->init[0], 0xc0000fff, 1008); + + if (tpr13_flag2) + writel(0x420000, &mctl_ctl->init[1]); + else + writel(0x1f20000, &mctl_ctl->init[1]); + + clrsetbits_le32(&mctl_ctl->init[2], 0xff0f, 0xd05); + writel(0, &mctl_ctl->dfimisc); + + writel(para->mr1 << 16 | para->mr2, &mctl_ctl->init[3]); + writel(para->mr3 << 16, &mctl_ctl->init[4]); + writel(para->mr11 << 16 | para->mr12, &mctl_ctl->init[6]); + writel(para->tpr1 << 16 | para->mr14, &mctl_ctl->init[7]); + + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); + if (!tpr13_flag3) { + tphy_wrlat -= 1; + trddata_en -= 1; + } + + writel(tphy_wrlat | trddata_en << 16 | 0x808000 | 0x2000000, + &mctl_ctl->dfitmg0); + writel(0x100202, &mctl_ctl->dfitmg1); + + writel(trfc_min | trefi_x32 << 16, &mctl_ctl->rfshtmg); +}