On Sun, 11 May 2025 02:09:58 +0100 Andre Przywara <andre.przyw...@arm.com> wrote:
> 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. > > [Andre: formatting fixes, adapt to mainline, drop unused parameters, > remove struct struct sunxi_mctl_com_reg, hardcode MR registers, > switch to mctl_check_pattern(), remove simple DRAM check] I pushed a (just updated) branch to my github which has those changes split up in separate patches, in case you want to track what has changed exactly: https://github.com/apritzel/u-boot/commits/a133-dram-WIP/ Cheers, Andre > --- > .../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 | 230 ++++ > arch/arm/mach-sunxi/Kconfig | 104 +- > arch/arm/mach-sunxi/Makefile | 2 + > arch/arm/mach-sunxi/dram_sun50i_a133.c | 1204 +++++++++++++++++ > 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, 1722 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..a5fc6ad3656 > --- /dev/null > +++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h > @@ -0,0 +1,230 @@ > +// 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 <linux/bitops.h> > + > +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 */ > +#define MCTL_COM_UNK_008 0x008 > +/* NOTE: This register has the same importance as mctl_ctl->clken in H616 */ > +#define MCTL_COM_MAER0 0x020 > + > +/* > + * 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) > + > +/* ADDRMAP constants */ > +#define ADDRMAP_DISABLED_3F_B(b) (0x3f + b) > +#define ADDRMAP_DISABLED_1F_B(b) (0x1f + b) > +#define ADDRMAP_DISABLED_0F_B(b) (0x0f + b) > + > +#define _ADDRMAP_VALUE(a,x,b) (((a) - b) << (x * 8)) > + > +/* > + * Bx = internal base > + * The selected HIF address bit for each address bit is determined > + * by adding the internal base to the value of each field > + * */ > + > +#define ADDRMAP0_CS0_B6(v) _ADDRMAP_VALUE(v, 0, 6) > + > +#define ADDRMAP1_BANK0_B2(v) _ADDRMAP_VALUE(v, 0, 2) > +#define ADDRMAP1_BANK1_B3(v) _ADDRMAP_VALUE(v, 1, 3) > +#define ADDRMAP1_BANK2_B4(v) _ADDRMAP_VALUE(v, 2, 4) > + > +#define ADDRMAP2_COL2_B2(v) _ADDRMAP_VALUE(v, 0, 2) > +#define ADDRMAP2_COL3_B3(v) _ADDRMAP_VALUE(v, 1, 3) > +#define ADDRMAP2_COL4_B4(v) _ADDRMAP_VALUE(v, 2, 4) > +#define ADDRMAP2_COL5_B5(v) _ADDRMAP_VALUE(v, 3, 5) > + > +#define ADDRMAP3_COL6_B6(v) _ADDRMAP_VALUE(v, 0, 6) > +#define ADDRMAP3_COL7_B7(v) _ADDRMAP_VALUE(v, 1, 7) > +#define ADDRMAP3_COL8_B8(v) _ADDRMAP_VALUE(v, 2, 8) > +#define ADDRMAP3_COL9_B9(v) _ADDRMAP_VALUE(v, 3, 9) > + > +#define ADDRMAP4_COL10_B10(v) _ADDRMAP_VALUE(v, 0, 10) > +#define ADDRMAP4_COL11_B11(v) _ADDRMAP_VALUE(v, 1, 11) > + > +#define ADDRMAP5_ROW0_B6(v) _ADDRMAP_VALUE(v, 0, 6) > +#define ADDRMAP5_ROW1_B7(v) _ADDRMAP_VALUE(v, 1, 7) > +#define ADDRMAP5_ROW2_10_B8(v) _ADDRMAP_VALUE(v, 2, 8) > +#define ADDRMAP5_ROW11_B17(v) _ADDRMAP_VALUE(v, 3, 17) > + > +#define ADDRMAP6_ROW12_B18(v) _ADDRMAP_VALUE(v, 0, 18) > +#define ADDRMAP6_ROW13_B19(v) _ADDRMAP_VALUE(v, 1, 19) > +#define ADDRMAP6_ROW14_B20(v) _ADDRMAP_VALUE(v, 2, 20) > +#define ADDRMAP6_ROW15_B21(v) _ADDRMAP_VALUE(v, 3, 21) > + > +#define ADDRMAP7_ROW16_B22(v) _ADDRMAP_VALUE(v, 0, 22) > +#define ADDRMAP7_ROW17_B23(v) _ADDRMAP_VALUE(v, 1, 23) > + > +#define ADDRMAP8_BG0_B2(v) _ADDRMAP_VALUE(v, 0, 2) > +#define ADDRMAP8_BG1_B3(v) _ADDRMAP_VALUE(v, 1, 3) > + > +/* These are only used if ADDRMAP5_ROW_BITS_2_10 = ADDRMAP_DISABLED_0F */ > +#define ADDRMAP9_ROW2_B8(v) _ADDRMAP_VALUE(v, 0, 8) > +#define ADDRMAP9_ROW3_B9(v) _ADDRMAP_VALUE(v, 1, 9) > +#define ADDRMAP9_ROW4_B10(v) _ADDRMAP_VALUE(v, 2, 10) > +#define ADDRMAP9_ROW5_B11(v) _ADDRMAP_VALUE(v, 3, 11) > + > +#define ADDRMAP10_ROW6_B12(v) _ADDRMAP_VALUE(v, 0, 12) > +#define ADDRMAP10_ROW7_B13(v) _ADDRMAP_VALUE(v, 1, 13) > +#define ADDRMAP10_ROW8_B14(v) _ADDRMAP_VALUE(v, 2, 14) > +#define ADDRMAP10_ROW9_B15(v) _ADDRMAP_VALUE(v, 3, 15) > + > +#define ADDRMAP11_ROW10_B16(v) _ADDRMAP_VALUE(v, 0, 16) > + > +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 mr11; > + uint32_t mr12; > + uint32_t mr13; > + uint32_t mr14; > + 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 c3718126cec..98b947f6e33 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,64 @@ 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_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_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 +161,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 +182,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" > @@ -500,7 +567,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 > > @@ -513,6 +580,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 > @@ -521,6 +591,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. > @@ -528,6 +599,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. > @@ -551,7 +623,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. > @@ -559,7 +631,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. > @@ -567,11 +639,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 > @@ -599,7 +687,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 > @@ -616,7 +704,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 a33cd5b0f07..8eff20b77bf 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 > dram_dw_helpers.o > obj-$(CONFIG_DRAM_SUN50I_H6) += dram_timings/ > obj-$(CONFIG_DRAM_SUN50I_H616) += dram_sun50i_h616.o dram_dw_helpers.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..a0fca3738f4 > --- /dev/null > +++ b/arch/arm/mach-sunxi/dram_sun50i_a133.c > @@ -0,0 +1,1204 @@ > +// 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.skra...@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) > +{ > + void * const ccm = (void *)SUNXI_CCM_BASE; > + > + /* Place all DRAM blocks into reset */ > + clrbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_ENABLE); > + clrbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_RESET); > + clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT)); > + clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(RESET_SHIFT)); > + clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL5_CTRL_EN); > + clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_MOD_RESET); > + udelay(5); > + > + /* Set up PLL5 clock, used for DRAM */ > + clrsetbits_le32(ccm + CCU_H6_PLL5_CFG, 0xff03, > + CCM_PLL5_CTRL_N((clk * 2) / 24) | CCM_PLL5_CTRL_EN); > + setbits_le32(ccm + CCU_H6_PLL5_CFG, BIT(24)); > + clrsetbits_le32(ccm + CCU_H6_PLL5_CFG, 0x3, > + CCM_PLL5_LOCK_EN | CCM_PLL5_CTRL_EN | BIT(30)); > + clrbits_le32(ccm + CCU_H6_PLL5_CFG, 0x3 | BIT(30)); > + mctl_await_completion(ccm + CCU_H6_PLL5_CFG, > + CCM_PLL5_LOCK, CCM_PLL5_LOCK); > + > + /* Enable DRAM clock and gate*/ > + clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, BIT(24) | BIT(25)); > + clrsetbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, 0x1f, BIT(1) | BIT(0)); > + setbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_CLK_UPDATE); > + setbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(RESET_SHIFT)); > + setbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT)); > + > + /* Re-enable MBUS and reset the DRAM module */ > + setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_RESET); > + setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_ENABLE); > + setbits_le32(ccm + CCU_H6_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_relaxed(0x0201, &mctl_ctl->odtmap); > + else > + writel_relaxed(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: > + /* MR4: CS to CMD / ADDR Latency and write preamble */ > + val = 0x400 | (0x000 << 10 & 0x70000) | > + (((0x0000 >> 12) & 1) + 6) << 24; > + break; > + case SUNXI_DRAM_TYPE_LPDDR4: > + val = 0x4000400; > + break; > + } > + > + writel_relaxed(val, &mctl_ctl->odtcfg); > + /* Documented as ODTCFG_SHADOW */ > + writel_relaxed(val, &mctl_ctl->unk_0x2240); > + /* Offset's interesting; additional undocumented shadows? */ > + writel_relaxed(val, &mctl_ctl->unk_0x3240); > + writel_relaxed(val, &mctl_ctl->unk_0x4240); > +} > + > +/* > + * This function produces address mapping parameters, used internally by the > + * controller to map address lines to HIF addresses. HIF addresses are word > + * addresses, not byte addresses; > + * In other words, DDR address 0x400 maps to HIF address 0x100. > + * > + * This implementation sets up a reasonable mapping where HIF address > + * ordering (LSB->MSB) is as such: > + * - Bank Groups > + * - Columns > + * - Banks > + * - Rows > + * - Ranks > + * > + * TODO: Handle 1.5GB + 3GB configurations. Info about these is stored in > + * upper bits of TPR13 after autoscan in boot0, and then some extra logic > + * happens in the address mapping > + */ > +#define INITIAL_HIF_OFFSET 3 > + > +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 col_bits = config->cols; > + u8 bank_bits = config->banks; > + u8 row_bits = config->rows; > + u8 rank_bits = config->ranks; > + > + unsigned int i, hif_offset, hif_bits[6]; > + > + /* > + * When the bus is half width, we need to adjust address mapping, > + * as COL[0] will be reallocated as part of the byte address, > + * offsetting the column address mapping values by 1 > + */ > + if (!config->bus_full_width) > + col_bits--; > + > + /* Match boot0's DRAM requirements */ > + if (bankgrp_bits > 2) > + panic("invalid dram configuration (bankgrps_bits = %d)", > + bankgrp_bits); > + if (col_bits < 8 || col_bits > 12) > + panic("invalid dram configuration (col_bits = %d)", col_bits); > + > + if (bank_bits < 2 || bank_bits > 3) > + panic("invalid dram configuration (bank_bits = %d)", bank_bits); > + > + if (row_bits < 14 || row_bits > 18) > + panic("invalid dram configuration (row_bits = %d)", row_bits); > + > + if (rank_bits > 1) > + panic("invalid dram configuration (rank_bits = %d)", rank_bits); > + > + /* > + * Col[0:1] + HIF[0:1] (hardwired), Col[2] = HIF[2] (required) > + * Thus, we start allocating from HIF[3] onwards > + */ > + hif_offset = INITIAL_HIF_OFFSET; > + > + /* BG[bankgrp_bits:0] = HIF[3 + bankgrp_bits:3]*/ > + switch (bankgrp_bits) { > + case 0: > + writel_relaxed(ADDRMAP8_BG0_B2(ADDRMAP_DISABLED_1F_B(2)) | > + ADDRMAP8_BG1_B3(ADDRMAP_DISABLED_1F_B(3)), > + &mctl_ctl->addrmap[8]); > + break; > + case 1: > + writel_relaxed(ADDRMAP8_BG0_B2(hif_offset) | > + ADDRMAP8_BG1_B3(ADDRMAP_DISABLED_1F_B(3)), > + &mctl_ctl->addrmap[8]); > + break; > + case 2: > + writel_relaxed(ADDRMAP8_BG0_B2(hif_offset) | > + ADDRMAP8_BG1_B3(hif_offset + 1), > + &mctl_ctl->addrmap[8]); > + break; > + default: > + panic("invalid dram configuration (bankgrp_bits = %d)", > + bankgrp_bits); > + } > + > + hif_offset += bankgrp_bits; > + > + /* Col[2] = HIF[2], Col[5:3] = HIF[offset + 2:offset] */ > + writel_relaxed(ADDRMAP2_COL2_B2(2) | ADDRMAP2_COL3_B3(hif_offset) | > + ADDRMAP2_COL4_B4(hif_offset + 1) | > + ADDRMAP2_COL5_B5(hif_offset + 2), > + &mctl_ctl->addrmap[2]); > + > + /* Col[col_bits:6] = HIF[col_bits + offset - 3:offset - 3] */ > + for (i = 6; i < 12; i++) { > + if (i < col_bits) > + hif_bits[i - 6] = hif_offset + (i - INITIAL_HIF_OFFSET); > + else > + hif_bits[i - 6] = ADDRMAP_DISABLED_1F_B(i); > + } > + > + writel_relaxed(ADDRMAP3_COL6_B6(hif_bits[0]) | > + ADDRMAP3_COL7_B7(hif_bits[1]) | > + ADDRMAP3_COL8_B8(hif_bits[2]) | > + ADDRMAP3_COL9_B9(hif_bits[3]), > + &mctl_ctl->addrmap[3]); > + > + writel_relaxed(ADDRMAP4_COL10_B10(hif_bits[4]) | > + ADDRMAP4_COL11_B11(hif_bits[5]), > + &mctl_ctl->addrmap[4]); > + > + hif_offset = bankgrp_bits + col_bits; > + > + /* Bank[bank_bits:0] = HIF[bank_bits + offset:offset] */ > + if (bank_bits == 3) > + writel_relaxed(ADDRMAP1_BANK0_B2(hif_offset) | > + ADDRMAP1_BANK1_B3(hif_offset + 1) | > + ADDRMAP1_BANK2_B4(hif_offset + 2), > + &mctl_ctl->addrmap[1]); > + else > + writel_relaxed(ADDRMAP1_BANK0_B2(hif_offset) | > + ADDRMAP1_BANK1_B3(hif_offset + 1) | > + ADDRMAP1_BANK2_B4(ADDRMAP_DISABLED_1F_B(4)), > + &mctl_ctl->addrmap[1]); > + > + hif_offset += bank_bits; > + > + /* Row[11:0] = HIF[11 + offset:offset] */ > + writel_relaxed(ADDRMAP5_ROW0_B6(hif_offset) | > + ADDRMAP5_ROW1_B7(hif_offset + 1) | > + ADDRMAP5_ROW2_10_B8(hif_offset + 2) | > + ADDRMAP5_ROW11_B17(hif_offset + 11), > + &mctl_ctl->addrmap[5]); > + > + /* > + * There's some complexity here because of a special case > + * in boot0 code that appears to work around a hardware bug. > + * For (col_bits, row_bits, rank_bits) = (10, 16, 1), we have to > + * place CS[0] in the position we would normally place ROW[14], > + * and shift ROW[14] and ROW[15] over by one. Using the bit following > + * ROW[15], as would be standard here, seems to cause nonsensical > + * aliasing patterns. > + * > + * Aside from this case, mapping is simple: > + * Row[row_bits:12] = HIF[offset + row_bits:offset + 12] > + */ > + for (i = 12; i < 18; i++) { > + if (i >= row_bits) > + hif_bits[i - 12] = ADDRMAP_DISABLED_0F_B(6 + i); > + else if (rank_bits != 1 || col_bits != 10 || row_bits != 16 || > + i < 14) > + hif_bits[i - 12] = hif_offset + i; > + else > + hif_bits[i - 12] = hif_offset + i + 1; > + } > + > + writel_relaxed(ADDRMAP6_ROW12_B18(hif_bits[0]) | > + ADDRMAP6_ROW13_B19(hif_bits[1]) | > + ADDRMAP6_ROW14_B20(hif_bits[2]) | > + ADDRMAP6_ROW15_B21(hif_bits[3]), > + &mctl_ctl->addrmap[6]); > + > + writel_relaxed(ADDRMAP7_ROW16_B22(hif_bits[4]) | > + ADDRMAP7_ROW17_B23(hif_bits[5]), > + &mctl_ctl->addrmap[7]); > + > + hif_offset += row_bits; > + > + /* > + * Ranks > + * Most cases: CS[0] = HIF[offset] > + * Special case (see above): CS[0] = HIF[offset - 2] > + */ > + if (rank_bits == 0) > + writel_relaxed(ADDRMAP0_CS0_B6(ADDRMAP_DISABLED_1F_B(6)), > + &mctl_ctl->addrmap[0]); > + else if (col_bits == 10 && row_bits == 16) > + writel_relaxed(ADDRMAP0_CS0_B6(hif_offset - 2), > + &mctl_ctl->addrmap[0]); > + else > + writel_relaxed(ADDRMAP0_CS0_B6(hif_offset), > + &mctl_ctl->addrmap[0]); > +} > + > +static void mctl_com_init(const struct dram_para *para, > + const struct dram_config *config) > +{ > + void *const mctl_com = (void *)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 + MCTL_COM_UNK_008, BIT(24), BIT(25) | BIT(9)); > + > + /* Unlock mctl_ctl registers */ > + setbits_le32(mctl_com + 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_relaxed(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_relaxed(mstr_value, &mctl_ctl->mstr); > + > + mctl_set_odtmap(para, config); > + mctl_set_addrmap(config); > + mctl_set_timing_params(para); > + > + dsb(); > + 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_relaxed(val, base); > + if (para->type == SUNXI_DRAM_TYPE_LPDDR4) { > + if (para->tpr3 & 0x1f1f1f1f) > + val = (para->tpr3 >> (i * 8)) & 0x1f; > + else > + val = 4; > + } > + writel_relaxed(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_relaxed(val, base); > + writel_relaxed(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_relaxed(0, base); > + else > + writel_relaxed(val, base); > + > + if (para->type == SUNXI_DRAM_TYPE_LPDDR4) > + writel_relaxed(0, base + 4); > + else > + writel_relaxed(val, base + 4); > + } > + dsb(); > +} > + > +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_relaxed((val >> 8) & 0x3f, &ptr[i]); > + > + writel_relaxed(val & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x7dc); > + writel_relaxed(val & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x7e0); > + > + switch (para->type) { > + case SUNXI_DRAM_TYPE_DDR3: > + writel_relaxed((val >> 16) & 0x3f, > + SUNXI_DRAM_PHY0_BASE + 0x7b8); > + writel_relaxed((val >> 24) & 0x3f, > + SUNXI_DRAM_PHY0_BASE + 0x784); > + break; > + case SUNXI_DRAM_TYPE_DDR4: > + writel_relaxed((val >> 16) & 0x3f, > + SUNXI_DRAM_PHY0_BASE + 0x784); > + break; > + case SUNXI_DRAM_TYPE_LPDDR3: > + writel_relaxed((val >> 16) & 0x3f, > + SUNXI_DRAM_PHY0_BASE + 0x788); > + writel_relaxed((val >> 24) & 0x3f, > + SUNXI_DRAM_PHY0_BASE + 0x790); > + break; > + case SUNXI_DRAM_TYPE_LPDDR4: > + writel_relaxed((val >> 16) & 0x3f, > + SUNXI_DRAM_PHY0_BASE + 0x790); > + writel_relaxed((val >> 24) & 0x3f, > + SUNXI_DRAM_PHY0_BASE + 0x78c); > + break; > + } > + > + dsb(); > +} > + > +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; > + void *const prcm = (void *)SUNXI_PRCM_BASE; > + void *const mctl_com = (void *)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_relaxed(0, &mctl_ctl->pwrctl); > + clrbits_le32(&mctl_ctl->dfimisc, 1); > + writel_relaxed(0x20, &mctl_ctl->pwrctl); > + > + /* PHY cold reset */ > + clrsetbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24), BIT(9)); > + udelay(1); > + setbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24)); > + > + /* Not sure what this gates the power of. */ > + clrbits_le32(prcm + CCU_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_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x14); > + writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x35c); > + writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x368); > + writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x374); > + writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x18); > + writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x360); > + writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x36c); > + writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x378); > + writel_relaxed(val2, SUNXI_DRAM_PHY0_BASE + 0x1c); > + writel_relaxed(val2, SUNXI_DRAM_PHY0_BASE + 0x364); > + writel_relaxed(val2, SUNXI_DRAM_PHY0_BASE + 0x370); > + writel_relaxed(val2, SUNXI_DRAM_PHY0_BASE + 0x37c); > + > + /* Set up SDQ swizzle */ > + ptr = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xc0); > + for (i = 0; i < ARRAY_SIZE(phy_init); i++) > + writel_relaxed(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_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3dc); > + writel_relaxed(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_relaxed(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); > + > + dsb(); > + clrbits_le32(mctl_com + MCTL_COM_UNK_008, 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 delay 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 + CCU_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) > +{ > + void *const mctl_com = (void *)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 + MCTL_COM_MAER0, BIT(8)); > + > + /* Enable dfi_init_complete signal and trigger PHY init start request */ > + writel_relaxed(0, &mctl_ctl->swctl); > + setbits_le32(&mctl_ctl->dfimisc, BIT(0)); > + setbits_le32(&mctl_ctl->dfimisc, BIT(5)); > + writel_relaxed(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_relaxed(0, &mctl_ctl->swctl); > + clrbits_le32(&mctl_ctl->dfimisc, BIT(5)); > + writel_relaxed(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_relaxed(0, &mctl_ctl->swctl); > + clrbits_le32(&mctl_ctl->pwrctl, BIT(5)); > + writel_relaxed(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_relaxed(0, &mctl_ctl->swctl); > + clrbits_le32(&mctl_ctl->dfimisc, BIT(0)); > + writel_relaxed(1, &mctl_ctl->swctl); > + mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0)); > + > + /* Write mode registers, fixed in the JEDEC spec */ > + switch (para->type) { > + case SUNXI_DRAM_TYPE_DDR3: > + mctl_mr_write(MRCTRL0_MR_ADDR(0), 0x1c70); /* MR0 */ > + /* > + * outbuf en, TDQs dis, write leveling dis, out drv 40 Ohms, > + * DLL en, Rtt_nom 120 Ohms > + */ > + mctl_mr_write(MRCTRL0_MR_ADDR(1), 0x40); /* MR1 */ > + /* > + * full array self-ref, CAS: 8 cyc, SRT w/ norm temp range, > + * dynamic ODT off > + */ > + mctl_mr_write(MRCTRL0_MR_ADDR(2), 0x18); /* MR2 */ > + /* predef MPR pattern */ > + mctl_mr_write(MRCTRL0_MR_ADDR(3), 0); /* MR3 */ > + break; > + case SUNXI_DRAM_TYPE_DDR4: > + mctl_mr_write(MRCTRL0_MR_ADDR(0), 0x840); > + mctl_mr_write(MRCTRL0_MR_ADDR(1), 0x601); > + mctl_mr_write(MRCTRL0_MR_ADDR(2), 0x8); > + mctl_mr_write(MRCTRL0_MR_ADDR(3), 0); > + mctl_mr_write(MRCTRL0_MR_ADDR(4), 0); > + mctl_mr_write(MRCTRL0_MR_ADDR(5), 0x400); > + > + mctl_mr_write(MRCTRL0_MR_ADDR(6), 0x862 | BIT(7)); > + mctl_mr_write(MRCTRL0_MR_ADDR(6), 0x862 | BIT(7)); > + mctl_mr_write(MRCTRL0_MR_ADDR(6), 0x862 & (~BIT(7))); > + break; > + case SUNXI_DRAM_TYPE_LPDDR3: > + mctl_mr_write_lpddr3(1, 0xc3); /* MR1: nWR=8, BL8 */ > + mctl_mr_write_lpddr3(2, 0xa); /* MR2: RL=12, WL=6 */ > + mctl_mr_write_lpddr3(3, 0x2); /* MR3: 40 0hms PD/PU */ > + mctl_mr_write_lpddr3(11, para->mr11); > + break; > + case SUNXI_DRAM_TYPE_LPDDR4: > + mctl_mr_write_lpddr4(0, 0); /* MR0 */ > + mctl_mr_write_lpddr4(1, 0x34); /* MR1 */ > + mctl_mr_write_lpddr4(2, 0x1b); /* MR2 */ > + mctl_mr_write_lpddr4(3, 0x33); /* MR3 */ > + mctl_mr_write_lpddr4(4, 0x3); /* 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_relaxed(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != val) { > + if (readl_relaxed(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_relaxed(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != > + val) { > + if (readl_relaxed(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_relaxed(SUNXI_DRAM_PHY0_BASE + 0x274) & 7; > + tmp = readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x26c) & 7; > + if (val < tmp) > + val = tmp; > + tmp = readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x32c) & 7; > + if (val < tmp) > + val = tmp; > + tmp = readl_relaxed(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_relaxed(val1, ptr); > + writel_relaxed(val1, ptr + 0x30); > + ptr += 2; > + } > + > + writel_relaxed(val2, ptr + 1); > + writel_relaxed(val2, ptr + 49); > + writel_relaxed(val2, ptr); > + writel_relaxed(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_relaxed(val1, ptr); > + writel_relaxed(val1, ptr + 0x30); > + ptr += 2; > + } > + > + writel_relaxed(val2, base2); > + writel_relaxed(val2, base2 + 48); > + writel_relaxed(val2, ptr); > + writel_relaxed(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\n"); > + return false; > + } > + } > + > + /* TODO: Implement read training */ > + /* 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_relaxed(0, &mctl_ctl->swctl); > + clrbits_le32(&mctl_ctl->rfshctl3, BIT(1)); > + writel_relaxed(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. */ > +static void auto_detect_ranks(const struct dram_para *para, > + struct dram_config *config) > +{ > + int i; > + > + config->cols = 9; > + config->rows = 14; > + config->banks = 2; > + config->bankgrps = 0; > + > + /* Test ranks */ > + for (i = 1; i >= 0; i--) { > + config->ranks = i; > + config->bus_full_width = true; > + debug("Testing ranks = %d, 32-bit bus: ", i); > + if (mctl_core_init(para, config)) { > + debug("OK\n"); > + break; > + } > + > + config->bus_full_width = false; > + debug("Testing ranks = %d, 16-bit bus: ", i); > + if (mctl_core_init(para, config)) { > + debug("OK\n"); > + break; > + } > + } > + > + if (i < 0) > + debug("rank testing failed\n"); > +} > + > +static void mctl_write_pattern(void) > +{ > + unsigned int i; > + u32 *ptr, val; > + > + ptr = (u32 *)CFG_SYS_SDRAM_BASE; > + for (i = 0; i < 16; ptr++, i++) { > + if (i & 1) > + val = ~(ulong)ptr; > + else > + val = (ulong)ptr; > + writel(val, ptr); > + } > +} > + > +static bool mctl_check_pattern(ulong offset) > +{ > + unsigned int i; > + u32 *ptr, val; > + > + ptr = (u32 *)CFG_SYS_SDRAM_BASE; > + for (i = 0; i < 16; ptr++, i++) { > + if (i & 1) > + val = ~(ulong)ptr; > + else > + val = (ulong)ptr; > + if (val != *(ptr + offset / 4)) > + return false; > + } > + > + return true; > +} > + > +static void mctl_auto_detect_dram_size(const struct dram_para *para, > + struct dram_config *config) > +{ > + unsigned int shift; > + u32 buffer[16]; > + > + /* max config for bankgrps on DDR4, minimum for everything else */ > + config->cols = 8; > + config->banks = 2; > + config->rows = 14; > + > + shift = 1 + config->bus_full_width; > + if (para->type == SUNXI_DRAM_TYPE_DDR4) { > + config->bankgrps = 2; > + mctl_core_init(para, config); > + > + /* store content so it can be restored later. */ > + memcpy(buffer, (u32 *)CFG_SYS_SDRAM_BASE, sizeof(buffer)); > + mctl_write_pattern(); > + > + if (mctl_check_pattern(1ULL << (shift + 4))) > + config->bankgrps = 1; > + > + /* restore data */ > + memcpy((u32 *)CFG_SYS_SDRAM_BASE, buffer, sizeof(buffer)); > + } else { > + /* No bank groups in (LP)DDR3/LPDDR4 */ > + config->bankgrps = 0; > + } > + > + /* reconfigure to make sure all active columns are accessible */ > + config->cols = 12; > + mctl_core_init(para, config); > + > + /* store data again as it might be moved */ > + memcpy(buffer, (u32 *)CFG_SYS_SDRAM_BASE, sizeof(buffer)); > + mctl_write_pattern(); > + > + /* > + * Detect column address bits. The last number of columns checked > + * is 11, if that doesn't match, is must be 12, no more checks needed. > + */ > + shift = 1 + config->bus_full_width + config->bankgrps; > + for (config->cols = 8; config->cols < 12; config->cols++) { > + if (mctl_check_pattern(1ULL << (config->cols + shift))) > + break; > + } > + memcpy((u32 *)CFG_SYS_SDRAM_BASE, buffer, sizeof(buffer)); > + > + /* reconfigure to make sure that all active banks are accessible */ > + config->banks = 3; > + mctl_core_init(para, config); > + > + memcpy(buffer, (u32 *)CFG_SYS_SDRAM_BASE, sizeof(buffer)); > + mctl_write_pattern(); > + > + /* detect bank bits */ > + shift += config->cols; > + for (config->banks = 2; config->banks < 3; config->banks++) { > + if (mctl_check_pattern(1ULL << (config->banks + shift))) > + break; > + } > + memcpy((u32 *)CFG_SYS_SDRAM_BASE, buffer, sizeof(buffer)); > + > + /* reconfigure to make sure that all active rows are accessible */ > + config->rows = 18; > + mctl_core_init(para, config); > + > + memcpy(buffer, (u32 *)CFG_SYS_SDRAM_BASE, sizeof(buffer)); > + mctl_write_pattern(); > + > + /* detect row address bits */ > + shift += config->banks; > + for (config->rows = 14; config->rows < 18; config->rows++) { > + if (mctl_check_pattern(1ULL << (config->rows + shift))) > + break; > + } > + memcpy((u32 *)CFG_SYS_SDRAM_BASE, buffer, sizeof(buffer)); > +} > + > +/* 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, > + .mr11 = CONFIG_DRAM_SUNXI_MR11, > + .mr12 = CONFIG_DRAM_SUNXI_MR12, > + .mr13 = CONFIG_DRAM_SUNXI_MR13, > + .mr14 = CONFIG_DRAM_SUNXI_MR14, > + .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, > +}; > + > +unsigned long sunxi_dram_init(void) > +{ > + struct dram_config config; > + > + /* Writing to undocumented SYS_CFG area, according to user manual. */ > + setbits_le32(0x03000160, BIT(8)); > + clrbits_le32(0x03000168, 0x3f); > + > + auto_detect_ranks(¶, &config); > + mctl_auto_detect_dram_size(¶, &config); > + > + if (!mctl_core_init(¶, &config)) > + return 0; > + > + debug("cols = 2^%d, rows = 2^%d, banks = %d, bank groups = %d, ranks = > %d, width = %d\n", > + config.cols, config.rows, 1U << config.banks, > + 1U << config.bankgrps, 1U << config.ranks, > + 16U << config.bus_full_width); > + > + return calculate_dram_size(&config); > +} > 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..dec208e22df > --- /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(0x840 << 16 | 0x601, &mctl_ctl->init[3]); /* MR0 / MR1 */ > + writel(0x8 << 16 | 0x0, &mctl_ctl->init[4]); /* MR2 / MR3 */ > + writel(0x0 << 16 | 0x400, &mctl_ctl->init[6]); /* MR4 / MR5 */ > + writel(0x826, &mctl_ctl->init[7]); /* MR6 */ > + > + 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..1e607381023 > --- /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(0x34 << 16 | 0x1b, &mctl_ctl->init[3]); /* MR1/MR2 */ > + writel(0x33 << 16, &mctl_ctl->init[4]); /* MR3 */ > + 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); > +}