[PATCH 7/8] sunxi: A133: add DRAM init code [WIP!]
Jernej Škrabec
jernej.skrabec at gmail.com
Sat Jan 18 09:17:14 CET 2025
Dne petek, 17. januar 2025 ob 02:45:36 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> From: Cody Eksal <masterr3c0rd at 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.
Since this is clearly WIP and SoF missing, just some comments below.
> ---
> .../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 at 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;
para0 - para2 are not needed once auto size, width, rank and (in case of
DDR4) bank groups is implemented.
Mode registers, or MR for short, don't need to be parameterized. They are
constant for specific type of DRAM, except ones that are clock dependent.
Those are overriden in code anyway.
TPR13 is not needed. It holds bits which tell if detection of size, rank
and width is needed and similar things. Those are (or should be) done anyway.
> +};
> +
> +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
I guess exclamation point is a mistake here, but this config shouldn't
be needed (see above.)
> + 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.
> +
Most of new configs are not needed, as discussed above.
> 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 at epochal.quest>
> + *
> + * Uses code from H616 driver, which is
> + * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec at siol.net>
> + *
> + */
> +
> +#define DEBUG
This should go away in final version.
> +
> +#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? */
Needless to say, instead of relying on hardcoded values, it's better to use
autodetection in order to cover multiple version of same board with different
DRAM sizes.
Best regards,
Jernej
> +/* 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);
> +}
>
More information about the U-Boot
mailing list