[PATCH 0/8] sunxi: (early) Allwinner A133 SoC support

The Allwinner A100 SoC has been around for a while, mostly on cheap tablets, but didn't generate much interest in the community so far. There were some efforts by two Allwinner employees in 2020, which led to basic upstream Linux support for that SoC, although this momentum dried up pretty quickly, leaving a lot of peripherals unsupported.
The A100 was silently replaced with the seemingly identical Allwinner A133, which is reportedly a better bin of the A100. So far we assume that both are compatible from a software perspective. There are some more devices with the A133 out there now, so people are working on filling the gaps, and adding U-Boot and TF-A support.
This series is a somewhat early version of a series to support boards with the A133 SoC in U-Boot.
Since the basic SoC .dtsi and its bindings are upstream in the Linux kernel repository, we can go ahead and add support for the pinctrl and clock drivers: this is handled in patches 4 and 5. Similarly ready are some refactoring patches, which will be needed later, those are the first three patches. Patch 6 adds SPL support for the AXP707/803 PMIC, which is often paired with the A133. Pending some review I am tempted to push those patches rather quickly.
Patch 7 is the technically most advanced and challenging patch: to add support for the DRAM controller. Huge thanks and kudos go to Cody, for reverse engineering, debugging and testing this. The patch itself is work in progress, and surely needs some love beyond the missing register bits, to improve formatting, coding style and patch split. However it works for me (TM), and is essential for the SoC support, so I include it here, as an early reference. Cody hinted at sending a proper version, this can then later replace this preliminary patch.
Patch 8 then puts the right values for various existing Allwinner specific configuration options in Kconfig, and adds the remaining bits required to enable configuration and build for boards with the A133 SoC. Due to a lack of an upstream DT for the boards floating around at the moment, this lacks any defconfig for now, but adding support for a board just requires providing the defconfig with the DRAM parameters and generic options, plus the respective .dts file. This has been tested locally on a Liontron H-A133L here, I will send both the .dts and the defconfig when they are ready.
For now please have a look, comment and review on the patches, so that we can start upstream support for this SoC.
Cheers, Andre
Andre Przywara (7): sunxi: clock: improve grouping of default clock register values sunxi: pmic_bus: support alternative I2C address sunxi: H616: DRAM: rename Kconfig parameters to be more generic clk: sunxi: Add support for the A100/A133 CCU pinctrl: sunxi: add Allwinner A100/A133 pinctrl description power: pmic: sunxi: add SPL support for the AXP803 sunxi: add support for the Allwinner A100/A133 SoC
Cody Eksal (1): sunxi: A133: add DRAM init code [WIP!]
arch/arm/cpu/armv8/fel_utils.S | 2 +- .../include/asm/arch-sunxi/clock_sun50i_h6.h | 25 +- .../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 | 254 +++- arch/arm/mach-sunxi/Makefile | 2 + arch/arm/mach-sunxi/board.c | 4 + arch/arm/mach-sunxi/clock_sun50i_h6.c | 3 +- arch/arm/mach-sunxi/cpu_info.c | 2 + arch/arm/mach-sunxi/dram_sun50i_a133.c | 1217 +++++++++++++++++ arch/arm/mach-sunxi/dram_sun50i_h616.c | 26 +- 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 ++ arch/arm/mach-sunxi/pmic_bus.c | 10 +- board/sunxi/board.c | 13 +- common/spl/Kconfig | 6 +- configs/anbernic_rg35xx_h700_defconfig | 20 +- configs/orangepi_zero2_defconfig | 8 +- configs/orangepi_zero2w_defconfig | 16 +- configs/orangepi_zero3_defconfig | 16 +- configs/tanix_tx1_defconfig | 16 +- configs/transpeed-8k618-t_defconfig | 14 +- configs/x96_mate_defconfig | 14 +- drivers/clk/sunxi/Kconfig | 7 + drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk_a100.c | 102 ++ drivers/clk/sunxi/clk_sunxi.c | 5 + drivers/pinctrl/sunxi/Kconfig | 10 + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 47 + drivers/power/Kconfig | 23 +- drivers/power/Makefile | 1 + drivers/power/axp_spl.c | 17 + 34 files changed, 2151 insertions(+), 130 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 create mode 100644 drivers/clk/sunxi/clk_a100.c

With each new SoC added to the clock_sun50i_h6.h header file, we add a list of default values for the bus clock registers. This list gets a bit hard to read, as the spacing between the lines looks confusing.
Tighten the lines by removing empty lines, to make it more obvious which values belong together. Also remove those comments that were more or less duplicating the next code line, and didn't add any information.
This makes it easier to find existing values and to add support for new SoCs.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- .../include/asm/arch-sunxi/clock_sun50i_h6.h | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h index a84a57e5b41..76dd33c9477 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h @@ -267,31 +267,23 @@ struct sunxi_ccm_reg { #define CCM_CPU_AXI_DEFAULT_FACTORS 0x301
#ifdef CONFIG_MACH_SUN50I_H6 /* H6 */ -#define CCM_PLL6_DEFAULT 0xa0006300
-/* psi_ahb1_ahb2 bit field */ +#define CCM_PLL6_DEFAULT 0xa0006300 #define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000102 - -/* ahb3 bit field */ #define CCM_AHB3_DEFAULT 0x03000002 - -/* apb1 bit field */ #define CCM_APB1_DEFAULT 0x03000102 + #elif CONFIG_MACH_SUN50I_H616 /* H616 */ -#define CCM_PLL6_DEFAULT 0xa8003100
-/* psi_ahb1_ahb2 bit field */ +#define CCM_PLL6_DEFAULT 0xa8003100 #define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 - -/* ahb3 bit field */ #define CCM_AHB3_DEFAULT 0x03000002 - -/* apb1 bit field */ #define CCM_APB1_DEFAULT 0x03000102 + #elif CONFIG_MACH_SUN8I_R528 /* R528 */ + #define CCM_PLL6_DEFAULT 0xe8216300 #define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 -//#define CCM_AHB3_DEFAULT 0x03000002 #define CCM_APB1_DEFAULT 0x03000102 #endif

Dne petek, 17. januar 2025 ob 02:45:30 Srednjeevropski standardni čas je Andre Przywara napisal(a):
With each new SoC added to the clock_sun50i_h6.h header file, we add a list of default values for the bus clock registers. This list gets a bit hard to read, as the spacing between the lines looks confusing.
Tighten the lines by removing empty lines, to make it more obvious which values belong together. Also remove those comments that were more or less duplicating the next code line, and didn't add any information.
This makes it easier to find existing values and to add support for new SoCs.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

Some of the X-Power AXP PMICs can be ordered with an alternative I2C address, for instance an AXP717 could be shipped with address 0x34 or with address 0x35. The datasheets for the AXP717 and AXP803 list two possible addresses, and they are always consecutive. For DM (DT) based drivers this is no problem, but the Allwinner SPL code relies on a hardcoded address.
Add a Kconfig variable that will add "1" to the existing address if it is set. This enables to use the AXP717 as used on boards with the new Allwinner A523 chip.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/pmic_bus.c | 7 ++++++- drivers/power/Kconfig | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-sunxi/pmic_bus.c b/arch/arm/mach-sunxi/pmic_bus.c index 8e19324c8ac..f0201f76f42 100644 --- a/arch/arm/mach-sunxi/pmic_bus.c +++ b/arch/arm/mach-sunxi/pmic_bus.c @@ -31,6 +31,11 @@ static struct udevice *pmic; #else static int pmic_i2c_address(void) { + int i2c_offset = 0; + + if (IS_ENABLED(CONFIG_AXP_ALT_I2C_ADDR)) + i2c_offset = 1; + if (IS_ENABLED(CONFIG_AXP152_POWER)) return AXP152_I2C_ADDR; if (IS_ENABLED(CONFIG_AXP305_POWER)) @@ -38,7 +43,7 @@ static int pmic_i2c_address(void) if (IS_ENABLED(CONFIG_AXP313_POWER)) return AXP313_I2C_ADDR; if (IS_ENABLED(CONFIG_AXP717_POWER)) - return AXP717_I2C_ADDR; + return AXP717_I2C_ADDR + i2c_offset;
/* Other AXP2xx and AXP8xx variants */ return AXP209_I2C_ADDR; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 4b81aeb7497..4c2211097fd 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -142,6 +142,14 @@ config SY8106A_POWER
endchoice
+config AXP_ALT_I2C_ADDR + bool "use alternative I2C address" + depends on AXP717_POWER + ---help--- + Some PMICs can be ordered with a different I2C address (+1), to avoid + address clashes or accommodate two PMICs on the same bus. + Select this to use the secondary I2C address for the AXP PMICs. + config AXP_DCDC1_VOLT int "axp pmic dcdc1 voltage" depends on AXP221_POWER || AXP809_POWER || AXP818_POWER

Dne petek, 17. januar 2025 ob 02:45:31 Srednjeevropski standardni čas je Andre Przywara napisal(a):
Some of the X-Power AXP PMICs can be ordered with an alternative I2C address, for instance an AXP717 could be shipped with address 0x34 or with address 0x35. The datasheets for the AXP717 and AXP803 list two possible addresses, and they are always consecutive. For DM (DT) based drivers this is no problem, but the Allwinner SPL code relies on a hardcoded address.
Add a Kconfig variable that will add "1" to the existing address if it is set. This enables to use the AXP717 as used on boards with the new Allwinner A523 chip.
Signed-off-by: Andre Przywara andre.przywara@arm.com
This works until some board vendor start mixing one or another address PMIC. Note that BSP boot0 does address autodetection, so it's not entirely out of the question. Anyway, let's hope we don't see anything like that.
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

On Sat, 18 Jan 2025 08:21:31 +0100 Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi Jernej,
many thanks for the review and your opinion.
Dne petek, 17. januar 2025 ob 02:45:31 Srednjeevropski standardni čas je Andre Przywara napisal(a):
Some of the X-Power AXP PMICs can be ordered with an alternative I2C address, for instance an AXP717 could be shipped with address 0x34 or with address 0x35. The datasheets for the AXP717 and AXP803 list two possible addresses, and they are always consecutive. For DM (DT) based drivers this is no problem, but the Allwinner SPL code relies on a hardcoded address.
Add a Kconfig variable that will add "1" to the existing address if it is set. This enables to use the AXP717 as used on boards with the new Allwinner A523 chip.
Signed-off-by: Andre Przywara andre.przywara@arm.com
This works until some board vendor start mixing one or another address PMIC. Note that BSP boot0 does address autodetection, so it's not entirely out of the question. Anyway, let's hope we don't see anything like that.
I looked at the datasheets for all supported PMICs, and they either state one or two supported addresses, in the latter case both consecutive. Autodetection sounds nice, but also unnecessary: we surely know what address it is for a certain board? And with those A523 boards having two PMICs, autodetection might become sketchy, as we don't know for sure which PMIC we got?
But that got me thinking: what about putting the I2C address in Kconfig directly, with defaults depending on the PMIC type?
config AXP_I2C_ADDR hex "AXP PMIC I2C address" depends on ARCH_SUNXI && !SUNXI_NO_PMIC default 0x36 if AXP305_POWER ....
That's should work seamlessly for all supported PMICs, and we just need one line for the Avaota, same as with this patch here.
What do you think?
Cheers, Andre
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

Dne nedelja, 19. januar 2025 ob 23:25:30 Srednjeevropski standardni čas je Andre Przywara napisal(a):
On Sat, 18 Jan 2025 08:21:31 +0100 Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi Jernej,
many thanks for the review and your opinion.
Dne petek, 17. januar 2025 ob 02:45:31 Srednjeevropski standardni čas je Andre Przywara napisal(a):
Some of the X-Power AXP PMICs can be ordered with an alternative I2C address, for instance an AXP717 could be shipped with address 0x34 or with address 0x35. The datasheets for the AXP717 and AXP803 list two possible addresses, and they are always consecutive. For DM (DT) based drivers this is no problem, but the Allwinner SPL code relies on a hardcoded address.
Add a Kconfig variable that will add "1" to the existing address if it is set. This enables to use the AXP717 as used on boards with the new Allwinner A523 chip.
Signed-off-by: Andre Przywara andre.przywara@arm.com
This works until some board vendor start mixing one or another address PMIC. Note that BSP boot0 does address autodetection, so it's not entirely out of the question. Anyway, let's hope we don't see anything like that.
I looked at the datasheets for all supported PMICs, and they either state one or two supported addresses, in the latter case both consecutive. Autodetection sounds nice, but also unnecessary: we surely know what address it is for a certain board? And with those A523 boards having two PMICs, autodetection might become sketchy, as we don't know for sure which PMIC we got?
Speaking for T527 BSP boot0 - they check version register to make sure that correct PMIC is installed.
But that got me thinking: what about putting the I2C address in Kconfig directly, with defaults depending on the PMIC type?
config AXP_I2C_ADDR hex "AXP PMIC I2C address" depends on ARCH_SUNXI && !SUNXI_NO_PMIC default 0x36 if AXP305_POWER ....
That's should work seamlessly for all supported PMICs, and we just need one line for the Avaota, same as with this patch here.
What do you think?
Yeah, looks more universal and avoids code changes in pmic_bus.c when adding support for new AXP PMIC, which is very nice indeed.
Best regards, Jernej
Cheers, Andre
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

Hi,
On Mon, 20 Jan 2025 at 09:42, Jernej Škrabec jernej.skrabec@gmail.com wrote:
Dne nedelja, 19. januar 2025 ob 23:25:30 Srednjeevropski standardni čas je Andre Przywara napisal(a):
On Sat, 18 Jan 2025 08:21:31 +0100 Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi Jernej,
many thanks for the review and your opinion.
Dne petek, 17. januar 2025 ob 02:45:31 Srednjeevropski standardni čas je Andre Przywara napisal(a):
Some of the X-Power AXP PMICs can be ordered with an alternative I2C address, for instance an AXP717 could be shipped with address 0x34 or with address 0x35. The datasheets for the AXP717 and AXP803 list two possible addresses, and they are always consecutive. For DM (DT) based drivers this is no problem, but the Allwinner SPL code relies on a hardcoded address.
Add a Kconfig variable that will add "1" to the existing address if it is set. This enables to use the AXP717 as used on boards with the new Allwinner A523 chip.
Signed-off-by: Andre Przywara andre.przywara@arm.com
This works until some board vendor start mixing one or another address PMIC. Note that BSP boot0 does address autodetection, so it's not entirely out of the question. Anyway, let's hope we don't see anything like that.
I looked at the datasheets for all supported PMICs, and they either state one or two supported addresses, in the latter case both consecutive. Autodetection sounds nice, but also unnecessary: we surely know what address it is for a certain board? And with those A523 boards having two PMICs, autodetection might become sketchy, as we don't know for sure which PMIC we got?
Speaking for T527 BSP boot0 - they check version register to make sure that correct PMIC is installed.
But that got me thinking: what about putting the I2C address in Kconfig directly, with defaults depending on the PMIC type?
config AXP_I2C_ADDR hex "AXP PMIC I2C address" depends on ARCH_SUNXI && !SUNXI_NO_PMIC default 0x36 if AXP305_POWER ....
That's should work seamlessly for all supported PMICs, and we just need one line for the Avaota, same as with this patch here.
What do you think?
Yeah, looks more universal and avoids code changes in pmic_bus.c when adding support for new AXP PMIC, which is very nice indeed.
Shouldn't this be in the devicetree? [..] Regards, Simon

On Mon, 20 Jan 2025 12:21:28 -0700 Simon Glass sjg@chromium.org wrote:
Hi Simon,
On Mon, 20 Jan 2025 at 09:42, Jernej Škrabec jernej.skrabec@gmail.com wrote:
Dne nedelja, 19. januar 2025 ob 23:25:30 Srednjeevropski standardni čas je Andre Przywara napisal(a):
On Sat, 18 Jan 2025 08:21:31 +0100 Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi Jernej,
many thanks for the review and your opinion.
Dne petek, 17. januar 2025 ob 02:45:31 Srednjeevropski standardni čas je Andre Przywara napisal(a):
Some of the X-Power AXP PMICs can be ordered with an alternative I2C address, for instance an AXP717 could be shipped with address 0x34 or with address 0x35. The datasheets for the AXP717 and AXP803 list two possible addresses, and they are always consecutive. For DM (DT) based drivers this is no problem, but the Allwinner SPL code relies on a hardcoded address.
Add a Kconfig variable that will add "1" to the existing address if it is set. This enables to use the AXP717 as used on boards with the new Allwinner A523 chip.
Signed-off-by: Andre Przywara andre.przywara@arm.com
This works until some board vendor start mixing one or another address PMIC. Note that BSP boot0 does address autodetection, so it's not entirely out of the question. Anyway, let's hope we don't see anything like that.
I looked at the datasheets for all supported PMICs, and they either state one or two supported addresses, in the latter case both consecutive. Autodetection sounds nice, but also unnecessary: we surely know what address it is for a certain board? And with those A523 boards having two PMICs, autodetection might become sketchy, as we don't know for sure which PMIC we got?
Speaking for T527 BSP boot0 - they check version register to make sure that correct PMIC is installed.
But that got me thinking: what about putting the I2C address in Kconfig directly, with defaults depending on the PMIC type?
config AXP_I2C_ADDR hex "AXP PMIC I2C address" depends on ARCH_SUNXI && !SUNXI_NO_PMIC default 0x36 if AXP305_POWER ....
That's should work seamlessly for all supported PMICs, and we just need one line for the Avaota, same as with this patch here.
What do you think?
Yeah, looks more universal and avoids code changes in pmic_bus.c when adding support for new AXP PMIC, which is very nice indeed.
Shouldn't this be in the devicetree?
It is, and the DM based I2C driver used in U-Boot proper does this properly, and works fine. But this here is for the SPL, where we don't have DT support. We need just minimal support to adjust the regulator for the DRAMs. So far there is one fixed address used by each PMIC, so this is simply hardcoded, based on which PMIC is selected. The patch I am now proposing (snippet above) just moves that hardcoding into Kconfig.
Cheers, Andre
[..] Regards, Simon

Hi Andre,
On Mon, 20 Jan 2025 at 17:06, Andre Przywara andre.przywara@arm.com wrote:
On Mon, 20 Jan 2025 12:21:28 -0700 Simon Glass sjg@chromium.org wrote:
Hi Simon,
On Mon, 20 Jan 2025 at 09:42, Jernej Škrabec jernej.skrabec@gmail.com wrote:
Dne nedelja, 19. januar 2025 ob 23:25:30 Srednjeevropski standardni čas je Andre Przywara napisal(a):
On Sat, 18 Jan 2025 08:21:31 +0100 Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi Jernej,
many thanks for the review and your opinion.
Dne petek, 17. januar 2025 ob 02:45:31 Srednjeevropski standardni čas je Andre Przywara napisal(a):
Some of the X-Power AXP PMICs can be ordered with an alternative I2C address, for instance an AXP717 could be shipped with address 0x34 or with address 0x35. The datasheets for the AXP717 and AXP803 list two possible addresses, and they are always consecutive. For DM (DT) based drivers this is no problem, but the Allwinner SPL code relies on a hardcoded address.
Add a Kconfig variable that will add "1" to the existing address if it is set. This enables to use the AXP717 as used on boards with the new Allwinner A523 chip.
Signed-off-by: Andre Przywara andre.przywara@arm.com
This works until some board vendor start mixing one or another address PMIC. Note that BSP boot0 does address autodetection, so it's not entirely out of the question. Anyway, let's hope we don't see anything like that.
I looked at the datasheets for all supported PMICs, and they either state one or two supported addresses, in the latter case both consecutive. Autodetection sounds nice, but also unnecessary: we surely know what address it is for a certain board? And with those A523 boards having two PMICs, autodetection might become sketchy, as we don't know for sure which PMIC we got?
Speaking for T527 BSP boot0 - they check version register to make sure that correct PMIC is installed.
But that got me thinking: what about putting the I2C address in Kconfig directly, with defaults depending on the PMIC type?
config AXP_I2C_ADDR hex "AXP PMIC I2C address" depends on ARCH_SUNXI && !SUNXI_NO_PMIC default 0x36 if AXP305_POWER ....
That's should work seamlessly for all supported PMICs, and we just need one line for the Avaota, same as with this patch here.
What do you think?
Yeah, looks more universal and avoids code changes in pmic_bus.c when adding support for new AXP PMIC, which is very nice indeed.
Shouldn't this be in the devicetree?
It is, and the DM based I2C driver used in U-Boot proper does this properly, and works fine. But this here is for the SPL, where we don't have DT support. We need just minimal support to adjust the regulator for the DRAMs. So far there is one fixed address used by each PMIC, so this is simply hardcoded, based on which PMIC is selected. The patch I am now proposing (snippet above) just moves that hardcoding into Kconfig.
I suppose you could use of-platdata?
Regards, SImon

The H616 DRAM controller requires some board specific parameters, which we declare in Kconfig, let each board specify in their defconfig, and then use in the DRAM init code.
Other DRAM controllers now require a very similar, if not identical parameter set, with so far the same parameter names used.
To help keep the Kconfig file at bay, rename the existing parameter names to drop the H616_ part in there, to make them more naturally reusable for other SoCs.
No functional change, just a rename.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/Kconfig | 58 +++++++++++++------------- arch/arm/mach-sunxi/dram_sun50i_h616.c | 26 ++++++------ configs/anbernic_rg35xx_h700_defconfig | 20 ++++----- configs/orangepi_zero2_defconfig | 8 ++-- configs/orangepi_zero2w_defconfig | 16 +++---- configs/orangepi_zero3_defconfig | 16 +++---- configs/tanix_tx1_defconfig | 16 +++---- configs/transpeed-8k618-t_defconfig | 14 +++---- configs/x96_mate_defconfig | 14 +++---- 9 files changed, 93 insertions(+), 95 deletions(-)
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index f941bf4a493..ba1b1541437 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -52,78 +52,76 @@ config DRAM_SUN50I_H616 like H616.
if DRAM_SUN50I_H616 -config DRAM_SUN50I_H616_DX_ODT - hex "H616 DRAM DX ODT parameter" +config DRAM_SUNXI_DX_ODT + hex "DRAM DX ODT parameter" help DX ODT value from vendor DRAM settings.
-config DRAM_SUN50I_H616_DX_DRI - hex "H616 DRAM DX DRI parameter" +config DRAM_SUNXI_DX_DRI + hex "DRAM DX DRI parameter" help DX DRI value from vendor DRAM settings.
-config DRAM_SUN50I_H616_CA_DRI - hex "H616 DRAM CA DRI parameter" +config DRAM_SUNXI_CA_DRI + hex "DRAM CA DRI parameter" help CA DRI value from vendor DRAM settings.
-config DRAM_SUN50I_H616_ODT_EN - hex "H616 DRAM ODT EN parameter" +config DRAM_SUNXI_ODT_EN + hex "DRAM ODT EN parameter" default 0x1 help ODT EN value from vendor DRAM settings.
-config DRAM_SUN50I_H616_TPR0 - hex "H616 DRAM TPR0 parameter" +config DRAM_SUNXI_TPR0 + hex "DRAM TPR0 parameter" default 0x0 help TPR0 value from vendor DRAM settings.
-config DRAM_SUN50I_H616_TPR2 - hex "H616 DRAM TPR2 parameter" +config DRAM_SUNXI_TPR2 + hex "DRAM TPR2 parameter" default 0x0 help TPR2 value from vendor DRAM settings.
-config DRAM_SUN50I_H616_TPR6 - hex "H616 DRAM TPR6 parameter" +config DRAM_SUNXI_TPR6 + hex "DRAM TPR6 parameter" default 0x3300c080 help TPR6 value from vendor DRAM settings.
-config DRAM_SUN50I_H616_TPR10 - hex "H616 DRAM TPR10 parameter" +config DRAM_SUNXI_TPR10 + hex "DRAM TPR10 parameter" help TPR10 value from vendor DRAM settings. It tells which features should be configured, like write leveling, read calibration, etc.
-config DRAM_SUN50I_H616_TPR11 - hex "H616 DRAM TPR11 parameter" +config DRAM_SUNXI_TPR11 + hex "DRAM TPR11 parameter" default 0x0 help TPR11 value from vendor DRAM settings.
-config DRAM_SUN50I_H616_TPR12 - hex "H616 DRAM TPR12 parameter" +config DRAM_SUNXI_TPR12 + hex "DRAM TPR12 parameter" default 0x0 help TPR12 value from vendor DRAM settings.
choice - prompt "H616 PHY pin mapping selection" - default DRAM_SUN50I_H616_PHY_ADDR_MAP_0 + prompt "DRAM PHY pin mapping selection" + default DRAM_SUNXI_PHY_ADDR_MAP_0
-config DRAM_SUN50I_H616_PHY_ADDR_MAP_0 - bool "H313/H616/H618" +config DRAM_SUNXI_PHY_ADDR_MAP_0 + bool "DRAM PHY address map 0" help - The pin mapping selection used by the H313, H616, H618, and - possibly other dies which use the H616 DRAM controller. + This pin mapping selection should be used by the H313, H616, H618.
-config DRAM_SUN50I_H616_PHY_ADDR_MAP_1 - bool "H700" +config DRAM_SUNXI_PHY_ADDR_MAP_1 + bool "DRAM PHY address map 1" help - The pin mapping selection used by the H700 and possibly other - dies which use the H616 DRAM controller. + This pin mapping selection should be used by the H700. endchoice endif
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index 863c4f1d7a8..b3554cc64bf 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -226,7 +226,7 @@ static void mctl_set_addrmap(const struct dram_config *config) mctl_ctl->addrmap[8] = 0x3F3F; }
-#ifdef CONFIG_DRAM_SUN50I_H616_PHY_ADDR_MAP_1 +#ifdef CONFIG_DRAM_SUNXI_PHY_ADDR_MAP_1 static const u8 phy_init[] = { #ifdef CONFIG_SUNXI_DRAM_H616_DDR3_1333 0x08, 0x02, 0x12, 0x05, 0x15, 0x17, 0x18, 0x0b, @@ -245,7 +245,7 @@ static const u8 phy_init[] = { 0x18, 0x04, 0x1a #endif }; -#else /* CONFIG_DRAM_SUN50I_H616_PHY_ADDR_MAP_0 */ +#else /* CONFIG_DRAM_SUNXI_PHY_ADDR_MAP_0 */ static const u8 phy_init[] = { #ifdef CONFIG_SUNXI_DRAM_H616_DDR3_1333 0x07, 0x0b, 0x02, 0x16, 0x0d, 0x0e, 0x14, 0x19, @@ -264,7 +264,7 @@ static const u8 phy_init[] = { 0x18, 0x03, 0x1a #endif }; -#endif /* CONFIG_DRAM_SUN50I_H616_PHY_ADDR_MAP_0 */ +#endif /* CONFIG_DRAM_SUNXI_PHY_ADDR_MAP_0 */ #define MASK_BYTE(reg, nr) (((reg) >> ((nr) * 8)) & 0x1f) static void mctl_phy_configure_odt(const struct dram_para *para) { @@ -1409,16 +1409,16 @@ static const struct dram_para para = { #elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR4) .type = SUNXI_DRAM_TYPE_LPDDR4, #endif - .dx_odt = CONFIG_DRAM_SUN50I_H616_DX_ODT, - .dx_dri = CONFIG_DRAM_SUN50I_H616_DX_DRI, - .ca_dri = CONFIG_DRAM_SUN50I_H616_CA_DRI, - .odt_en = CONFIG_DRAM_SUN50I_H616_ODT_EN, - .tpr0 = CONFIG_DRAM_SUN50I_H616_TPR0, - .tpr2 = CONFIG_DRAM_SUN50I_H616_TPR2, - .tpr6 = CONFIG_DRAM_SUN50I_H616_TPR6, - .tpr10 = CONFIG_DRAM_SUN50I_H616_TPR10, - .tpr11 = CONFIG_DRAM_SUN50I_H616_TPR11, - .tpr12 = CONFIG_DRAM_SUN50I_H616_TPR12, + .dx_odt = CONFIG_DRAM_SUNXI_DX_ODT, + .dx_dri = CONFIG_DRAM_SUNXI_DX_DRI, + .ca_dri = CONFIG_DRAM_SUNXI_CA_DRI, + .odt_en = CONFIG_DRAM_SUNXI_ODT_EN, + .tpr0 = CONFIG_DRAM_SUNXI_TPR0, + .tpr2 = CONFIG_DRAM_SUNXI_TPR2, + .tpr6 = CONFIG_DRAM_SUNXI_TPR6, + .tpr10 = CONFIG_DRAM_SUNXI_TPR10, + .tpr11 = CONFIG_DRAM_SUNXI_TPR11, + .tpr12 = CONFIG_DRAM_SUNXI_TPR12, };
unsigned long sunxi_dram_init(void) diff --git a/configs/anbernic_rg35xx_h700_defconfig b/configs/anbernic_rg35xx_h700_defconfig index cd3d6bfba06..c5c40a158d3 100644 --- a/configs/anbernic_rg35xx_h700_defconfig +++ b/configs/anbernic_rg35xx_h700_defconfig @@ -2,16 +2,16 @@ CONFIG_ARM=y CONFIG_ARCH_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h700-anbernic-rg35xx-2024" CONFIG_SPL=y -CONFIG_DRAM_SUN50I_H616_DX_ODT=0x08080808 -CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e -CONFIG_DRAM_SUN50I_H616_CA_DRI=0x0e0e -CONFIG_DRAM_SUN50I_H616_ODT_EN=0x7887bbbb -CONFIG_DRAM_SUN50I_H616_TPR2=0x1 -CONFIG_DRAM_SUN50I_H616_TPR6=0x40808080 -CONFIG_DRAM_SUN50I_H616_TPR10=0x402f6633 -CONFIG_DRAM_SUN50I_H616_TPR11=0x1b1f1e1c -CONFIG_DRAM_SUN50I_H616_TPR12=0x06060606 -CONFIG_DRAM_SUN50I_H616_PHY_ADDR_MAP_1=y +CONFIG_DRAM_SUNXI_DX_ODT=0x08080808 +CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e +CONFIG_DRAM_SUNXI_ODT_EN=0x7887bbbb +CONFIG_DRAM_SUNXI_TPR2=0x1 +CONFIG_DRAM_SUNXI_TPR6=0x40808080 +CONFIG_DRAM_SUNXI_TPR10=0x402f6633 +CONFIG_DRAM_SUNXI_TPR11=0x1b1f1e1c +CONFIG_DRAM_SUNXI_TPR12=0x06060606 +CONFIG_DRAM_SUNXI_PHY_ADDR_MAP_1=y CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_LPDDR4=y CONFIG_DRAM_CLK=672 diff --git a/configs/orangepi_zero2_defconfig b/configs/orangepi_zero2_defconfig index f60ee7375da..f2265ea5179 100644 --- a/configs/orangepi_zero2_defconfig +++ b/configs/orangepi_zero2_defconfig @@ -2,10 +2,10 @@ CONFIG_ARM=y CONFIG_ARCH_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h616-orangepi-zero2" CONFIG_SPL=y -CONFIG_DRAM_SUN50I_H616_DX_ODT=0x08080808 -CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e -CONFIG_DRAM_SUN50I_H616_CA_DRI=0x0e0e -CONFIG_DRAM_SUN50I_H616_TPR10=0xf83438 +CONFIG_DRAM_SUNXI_DX_ODT=0x08080808 +CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e +CONFIG_DRAM_SUNXI_TPR10=0xf83438 CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_DDR3_1333=y CONFIG_USB1_VBUS_PIN="PC16" diff --git a/configs/orangepi_zero2w_defconfig b/configs/orangepi_zero2w_defconfig index cbb702d85b3..ec030f32403 100644 --- a/configs/orangepi_zero2w_defconfig +++ b/configs/orangepi_zero2w_defconfig @@ -2,14 +2,14 @@ CONFIG_ARM=y CONFIG_ARCH_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h618-orangepi-zero2w" CONFIG_SPL=y -CONFIG_DRAM_SUN50I_H616_DX_ODT=0x07070707 -CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e -CONFIG_DRAM_SUN50I_H616_CA_DRI=0x0e0e -CONFIG_DRAM_SUN50I_H616_ODT_EN=0xaaaaeeee -CONFIG_DRAM_SUN50I_H616_TPR6=0x48808080 -CONFIG_DRAM_SUN50I_H616_TPR10=0x402f6663 -CONFIG_DRAM_SUN50I_H616_TPR11=0x26262524 -CONFIG_DRAM_SUN50I_H616_TPR12=0x100f100f +CONFIG_DRAM_SUNXI_DX_ODT=0x07070707 +CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e +CONFIG_DRAM_SUNXI_ODT_EN=0xaaaaeeee +CONFIG_DRAM_SUNXI_TPR6=0x48808080 +CONFIG_DRAM_SUNXI_TPR10=0x402f6663 +CONFIG_DRAM_SUNXI_TPR11=0x26262524 +CONFIG_DRAM_SUNXI_TPR12=0x100f100f CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_LPDDR4=y CONFIG_DRAM_CLK=792 diff --git a/configs/orangepi_zero3_defconfig b/configs/orangepi_zero3_defconfig index 4e9b0ec4d33..63f9a735378 100644 --- a/configs/orangepi_zero3_defconfig +++ b/configs/orangepi_zero3_defconfig @@ -2,14 +2,14 @@ CONFIG_ARM=y CONFIG_ARCH_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h618-orangepi-zero3" CONFIG_SPL=y -CONFIG_DRAM_SUN50I_H616_DX_ODT=0x07070707 -CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e -CONFIG_DRAM_SUN50I_H616_CA_DRI=0x0e0e -CONFIG_DRAM_SUN50I_H616_ODT_EN=0xaaaaeeee -CONFIG_DRAM_SUN50I_H616_TPR6=0x44000000 -CONFIG_DRAM_SUN50I_H616_TPR10=0x402f6663 -CONFIG_DRAM_SUN50I_H616_TPR11=0x24242624 -CONFIG_DRAM_SUN50I_H616_TPR12=0x0f0f100f +CONFIG_DRAM_SUNXI_DX_ODT=0x07070707 +CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e +CONFIG_DRAM_SUNXI_ODT_EN=0xaaaaeeee +CONFIG_DRAM_SUNXI_TPR6=0x44000000 +CONFIG_DRAM_SUNXI_TPR10=0x402f6663 +CONFIG_DRAM_SUNXI_TPR11=0x24242624 +CONFIG_DRAM_SUNXI_TPR12=0x0f0f100f CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_LPDDR4=y CONFIG_DRAM_CLK=792 diff --git a/configs/tanix_tx1_defconfig b/configs/tanix_tx1_defconfig index 706306b1444..28cf9513c30 100644 --- a/configs/tanix_tx1_defconfig +++ b/configs/tanix_tx1_defconfig @@ -2,14 +2,14 @@ CONFIG_ARM=y CONFIG_ARCH_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h313-tanix-tx1" CONFIG_SPL=y -CONFIG_DRAM_SUN50I_H616_DX_ODT=0x06060606 -CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0d0d0d0d -CONFIG_DRAM_SUN50I_H616_CA_DRI=0x1919 -CONFIG_DRAM_SUN50I_H616_ODT_EN=0x9988eeee -CONFIG_DRAM_SUN50I_H616_TPR6=0x2fb08080 -CONFIG_DRAM_SUN50I_H616_TPR10=0x402f4469 -CONFIG_DRAM_SUN50I_H616_TPR11=0x0e0f0d0d -CONFIG_DRAM_SUN50I_H616_TPR12=0x11131213 +CONFIG_DRAM_SUNXI_DX_ODT=0x06060606 +CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d +CONFIG_DRAM_SUNXI_CA_DRI=0x1919 +CONFIG_DRAM_SUNXI_ODT_EN=0x9988eeee +CONFIG_DRAM_SUNXI_TPR6=0x2fb08080 +CONFIG_DRAM_SUNXI_TPR10=0x402f4469 +CONFIG_DRAM_SUNXI_TPR11=0x0e0f0d0d +CONFIG_DRAM_SUNXI_TPR12=0x11131213 CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_LPDDR3=y CONFIG_R_I2C_ENABLE=y diff --git a/configs/transpeed-8k618-t_defconfig b/configs/transpeed-8k618-t_defconfig index 1d5a0c264b3..221614762e8 100644 --- a/configs/transpeed-8k618-t_defconfig +++ b/configs/transpeed-8k618-t_defconfig @@ -2,13 +2,13 @@ CONFIG_ARM=y CONFIG_ARCH_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h618-transpeed-8k618-t" CONFIG_SPL=y -CONFIG_DRAM_SUN50I_H616_DX_ODT=0x03030303 -CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e -CONFIG_DRAM_SUN50I_H616_CA_DRI=0x1f12 -CONFIG_DRAM_SUN50I_H616_TPR0=0xc0001002 -CONFIG_DRAM_SUN50I_H616_TPR10=0x2f1107 -CONFIG_DRAM_SUN50I_H616_TPR11=0xddddcccc -CONFIG_DRAM_SUN50I_H616_TPR12=0xeddc7665 +CONFIG_DRAM_SUNXI_DX_ODT=0x03030303 +CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e +CONFIG_DRAM_SUNXI_CA_DRI=0x1f12 +CONFIG_DRAM_SUNXI_TPR0=0xc0001002 +CONFIG_DRAM_SUNXI_TPR10=0x2f1107 +CONFIG_DRAM_SUNXI_TPR11=0xddddcccc +CONFIG_DRAM_SUNXI_TPR12=0xeddc7665 CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_DDR3_1333=y CONFIG_DRAM_CLK=648 diff --git a/configs/x96_mate_defconfig b/configs/x96_mate_defconfig index f876cc91f6e..bd9b611d6f5 100644 --- a/configs/x96_mate_defconfig +++ b/configs/x96_mate_defconfig @@ -2,13 +2,13 @@ CONFIG_ARM=y CONFIG_ARCH_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h616-x96-mate" CONFIG_SPL=y -CONFIG_DRAM_SUN50I_H616_DX_ODT=0x03030303 -CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0e0e0e0e -CONFIG_DRAM_SUN50I_H616_CA_DRI=0x1c12 -CONFIG_DRAM_SUN50I_H616_TPR0=0xc0000c05 -CONFIG_DRAM_SUN50I_H616_TPR10=0x2f0007 -CONFIG_DRAM_SUN50I_H616_TPR11=0xffffdddd -CONFIG_DRAM_SUN50I_H616_TPR12=0xfedf7557 +CONFIG_DRAM_SUNXI_DX_ODT=0x03030303 +CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e +CONFIG_DRAM_SUNXI_CA_DRI=0x1c12 +CONFIG_DRAM_SUNXI_TPR0=0xc0000c05 +CONFIG_DRAM_SUNXI_TPR10=0x2f0007 +CONFIG_DRAM_SUNXI_TPR11=0xffffdddd +CONFIG_DRAM_SUNXI_TPR12=0xfedf7557 CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_DDR3_1333=y CONFIG_R_I2C_ENABLE=y

Dne petek, 17. januar 2025 ob 02:45:32 Srednjeevropski standardni čas je Andre Przywara napisal(a):
The H616 DRAM controller requires some board specific parameters, which we declare in Kconfig, let each board specify in their defconfig, and then use in the DRAM init code.
Other DRAM controllers now require a very similar, if not identical parameter set, with so far the same parameter names used.
To help keep the Kconfig file at bay, rename the existing parameter names to drop the H616_ part in there, to make them more naturally reusable for other SoCs.
No functional change, just a rename.
Signed-off-by: Andre Przywara andre.przywara@arm.com
That's very welcome change.
Reviewed-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

The Allwinner A100 SoC has been around for a while, and has now seemingly been replaced with its close sibling A133.
Add support for the CCU, as far as used by U-Boot proper. Linux has some basic (clock and pinctrl) support for a while, so we can already use the existing binding headers.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/Kconfig | 3 + drivers/clk/sunxi/Kconfig | 7 +++ drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk_a100.c | 102 ++++++++++++++++++++++++++++++++++ drivers/clk/sunxi/clk_sunxi.c | 5 ++ 5 files changed, 118 insertions(+) create mode 100644 drivers/clk/sunxi/clk_a100.c
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index ba1b1541437..78fd74f3f28 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -457,6 +457,9 @@ config MACH_SUN50I_H616 select SUN50I_GEN_H6 imply OF_UPSTREAM
+config MACH_SUN50I_A133 + bool "sun50i (Allwinner A133)" + endchoice
# The sun8i SoCs share a lot, this helps to avoid a lot of "if A23 || A33" diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index 8bdc0944896..f44db76c182 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -122,4 +122,11 @@ config CLK_SUN50I_A64 This enables common clock driver support for platforms based on Allwinner A64 SoC.
+config CLK_SUN50I_A100 + bool "Clock driver for Allwinner A100/A133" + default MACH_SUN50I_A133 + help + This enables common clock driver support for platforms based + on Allwinner A100/A133 SoCs. + endif # CLK_SUNXI diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 90a277489dc..7ff71c756e0 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_CLK_SUN50I_H6) += clk_h6.o obj-$(CONFIG_CLK_SUN50I_H6_R) += clk_h6_r.o obj-$(CONFIG_CLK_SUN50I_H616) += clk_h616.o obj-$(CONFIG_CLK_SUN50I_A64) += clk_a64.o +obj-$(CONFIG_CLK_SUN50I_A100) += clk_a100.o diff --git a/drivers/clk/sunxi/clk_a100.c b/drivers/clk/sunxi/clk_a100.c new file mode 100644 index 00000000000..b641feb8612 --- /dev/null +++ b/drivers/clk/sunxi/clk_a100.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2023-2024 Arm Ltd. + */ + +#include <clk/sunxi.h> +#include <dt-bindings/clock/sun50i-a100-ccu.h> +#include <dt-bindings/reset/sun50i-a100-ccu.h> +#include <linux/bitops.h> + +static struct ccu_clk_gate a100_gates[] = { + [CLK_PLL_PERIPH0] = GATE(0x020, BIT(31) | BIT(27)), + + [CLK_APB1] = GATE_DUMMY, + + [CLK_DE] = GATE(0x600, BIT(31)), + [CLK_BUS_DE] = GATE(0x60c, BIT(0)), + + [CLK_BUS_MMC0] = GATE(0x84c, BIT(0)), + [CLK_BUS_MMC1] = GATE(0x84c, BIT(1)), + [CLK_BUS_MMC2] = GATE(0x84c, BIT(2)), + + [CLK_BUS_UART0] = GATE(0x90c, BIT(0)), + [CLK_BUS_UART1] = GATE(0x90c, BIT(1)), + [CLK_BUS_UART2] = GATE(0x90c, BIT(2)), + [CLK_BUS_UART3] = GATE(0x90c, BIT(3)), + [CLK_BUS_UART4] = GATE(0x90c, BIT(4)), + + [CLK_BUS_I2C0] = GATE(0x91c, BIT(0)), + [CLK_BUS_I2C1] = GATE(0x91c, BIT(1)), + [CLK_BUS_I2C2] = GATE(0x91c, BIT(2)), + [CLK_BUS_I2C3] = GATE(0x91c, BIT(3)), + + [CLK_SPI0] = GATE(0x940, BIT(31)), + [CLK_SPI1] = GATE(0x944, BIT(31)), + [CLK_SPI2] = GATE(0x948, BIT(31)), + + [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)), + [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)), + [CLK_BUS_SPI2] = GATE(0x96c, BIT(2)), + + [CLK_BUS_EMAC] = GATE(0x97c, BIT(0)), + + [CLK_USB_PHY0] = GATE(0xa70, BIT(29)), + [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)), + + [CLK_USB_PHY1] = GATE(0xa74, BIT(29)), + [CLK_USB_OHCI1] = GATE(0xa74, BIT(31)), + + [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)), + [CLK_BUS_OHCI1] = GATE(0xa8c, BIT(1)), + [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)), + [CLK_BUS_EHCI1] = GATE(0xa8c, BIT(5)), + [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)), + + [CLK_TCON_LCD] = GATE(0xb60, BIT(31)), + [CLK_BUS_TCON_LCD] = GATE(0xb7c, BIT(0)), +}; + +static struct ccu_reset a100_resets[] = { + [RST_BUS_DE] = RESET(0x60c, BIT(16)), + + [RST_BUS_MMC0] = RESET(0x84c, BIT(16)), + [RST_BUS_MMC1] = RESET(0x84c, BIT(17)), + [RST_BUS_MMC2] = RESET(0x84c, BIT(18)), + + [RST_BUS_UART0] = RESET(0x90c, BIT(16)), + [RST_BUS_UART1] = RESET(0x90c, BIT(17)), + [RST_BUS_UART2] = RESET(0x90c, BIT(18)), + [RST_BUS_UART3] = RESET(0x90c, BIT(19)), + [RST_BUS_UART4] = RESET(0x90c, BIT(20)), + + [RST_BUS_I2C0] = RESET(0x91c, BIT(16)), + [RST_BUS_I2C1] = RESET(0x91c, BIT(17)), + [RST_BUS_I2C2] = RESET(0x91c, BIT(18)), + [RST_BUS_I2C3] = RESET(0x91c, BIT(19)), + + [RST_BUS_SPI0] = RESET(0x96c, BIT(16)), + [RST_BUS_SPI1] = RESET(0x96c, BIT(17)), + [RST_BUS_SPI2] = RESET(0x96c, BIT(18)), + + [RST_BUS_EMAC] = RESET(0x97c, BIT(16)), + + [RST_USB_PHY0] = RESET(0xa70, BIT(30)), + + [RST_USB_PHY1] = RESET(0xa74, BIT(30)), + + [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)), + [RST_BUS_OHCI1] = RESET(0xa8c, BIT(17)), + [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)), + [RST_BUS_EHCI1] = RESET(0xa8c, BIT(21)), + [RST_BUS_OTG] = RESET(0xa8c, BIT(24)), + + [RST_BUS_TCON_LCD] = RESET(0xb7c, BIT(16)), +}; + +const struct ccu_desc a100_ccu_desc = { + .gates = a100_gates, + .resets = a100_resets, + .num_gates = ARRAY_SIZE(a100_gates), + .num_resets = ARRAY_SIZE(a100_resets), +}; diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c index 2ef4f45dacf..e0765cbc6dc 100644 --- a/drivers/clk/sunxi/clk_sunxi.c +++ b/drivers/clk/sunxi/clk_sunxi.c @@ -122,6 +122,7 @@ extern const struct ccu_desc f1c100s_ccu_desc; extern const struct ccu_desc h3_ccu_desc; extern const struct ccu_desc h6_ccu_desc; extern const struct ccu_desc h616_ccu_desc; +extern const struct ccu_desc a100_ccu_desc; extern const struct ccu_desc h6_r_ccu_desc; extern const struct ccu_desc r40_ccu_desc; extern const struct ccu_desc v3s_ccu_desc; @@ -215,6 +216,10 @@ static const struct udevice_id sunxi_clk_ids[] = { { .compatible = "allwinner,sun50i-h616-r-ccu", .data = (ulong)&h6_r_ccu_desc }, #endif +#ifdef CONFIG_CLK_SUN50I_A100 + { .compatible = "allwinner,sun50i-a100-ccu", + .data = (ulong)&a100_ccu_desc }, +#endif #ifdef CONFIG_CLK_SUNIV_F1C100S { .compatible = "allwinner,suniv-f1c100s-ccu", .data = (ulong)&f1c100s_ccu_desc },

Dne petek, 17. januar 2025 ob 02:45:33 Srednjeevropski standardni čas je Andre Przywara napisal(a):
The Allwinner A100 SoC has been around for a while, and has now seemingly been replaced with its close sibling A133.
Add support for the CCU, as far as used by U-Boot proper. Linux has some basic (clock and pinctrl) support for a while, so we can already use the existing binding headers.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Acked-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

The Allwinner A100 SoC has been around for a while, and has now seemingly been replaced with its close sibling A133.
Add the required mapping between the pinmux group strings and their respective mux value, as far as used by U-Boot proper. Linux has some basic (clock and pinctrl) support for a while, so we can build on the names already used there.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- drivers/pinctrl/sunxi/Kconfig | 10 ++++++ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 47 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+)
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig index cbd61795986..65e8192a99a 100644 --- a/drivers/pinctrl/sunxi/Kconfig +++ b/drivers/pinctrl/sunxi/Kconfig @@ -124,6 +124,16 @@ config PINCTRL_SUN50I_H616_R default MACH_SUN50I_H616 select PINCTRL_SUNXI
+config PINCTRL_SUN50I_A100 + bool "Support for the Allwinner A100/A133 PIO" + default MACH_SUN50I_A133 + select PINCTRL_SUNXI + +config PINCTRL_SUN50I_A100_R + bool "Support for the Allwinner A100/A133 R-PIO" + default MACH_SUN50I_A133 + select PINCTRL_SUNXI + config PINCTRL_SUN20I_D1 bool "Support for the Allwinner D1/R528 PIO" default MACH_SUN8I_R528 diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 37ea93715d1..c38edf7d4f5 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -774,6 +774,41 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_r_pinctrl_desc .num_banks = 1, };
+static const struct sunxi_pinctrl_function sun50i_a100_pinctrl_functions[] = { + { "emac0", 5 }, /* PH0-PH16 */ + { "gpio_in", 0 }, + { "gpio_out", 1 }, + { "mmc0", 2 }, /* PF0-PF5 */ + { "mmc1", 2 }, /* PG0-PG5 */ + { "mmc2", 3 }, /* PC0-PC16 */ + { "spi0", 4 }, /* PC2-PC4, PC7, PC12, PC15-PC16 */ +#if IS_ENABLED(CONFIG_UART0_PORT_F) + { "uart0", 3 }, /* PF2-PF4 */ +#else + { "uart0", 2 }, /* PB9-PB10 */ +#endif +}; + +static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_pinctrl_desc = { + .functions = sun50i_a100_pinctrl_functions, + .num_functions = ARRAY_SIZE(sun50i_a100_pinctrl_functions), + .first_bank = SUNXI_GPIO_A, + .num_banks = 8, +}; + +static const struct sunxi_pinctrl_function sun50i_a100_r_pinctrl_functions[] = { + { "gpio_in", 0 }, + { "gpio_out", 1 }, + { "s_i2c0", 2 }, +}; + +static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_r_pinctrl_desc = { + .functions = sun50i_a100_r_pinctrl_functions, + .num_functions = ARRAY_SIZE(sun50i_a100_r_pinctrl_functions), + .first_bank = SUNXI_GPIO_L, + .num_banks = 1, +}; + static const struct udevice_id sunxi_pinctrl_ids[] = { #ifdef CONFIG_PINCTRL_SUNIV_F1C100S { @@ -936,6 +971,18 @@ static const struct udevice_id sunxi_pinctrl_ids[] = { .compatible = "allwinner,sun50i-h616-r-pinctrl", .data = (ulong)&sun50i_h616_r_pinctrl_desc, }, +#endif +#ifdef CONFIG_PINCTRL_SUN50I_A100 + { + .compatible = "allwinner,sun50i-a100-pinctrl", + .data = (ulong)&sun50i_a100_pinctrl_desc, + }, +#endif +#ifdef CONFIG_PINCTRL_SUN50I_A100_R + { + .compatible = "allwinner,sun50i-a100-r-pinctrl", + .data = (ulong)&sun50i_a100_r_pinctrl_desc, + }, #endif {} };

Dne petek, 17. januar 2025 ob 02:45:34 Srednjeevropski standardni čas je Andre Przywara napisal(a):
The Allwinner A100 SoC has been around for a while, and has now seemingly been replaced with its close sibling A133.
Add the required mapping between the pinmux group strings and their respective mux value, as far as used by U-Boot proper. Linux has some basic (clock and pinctrl) support for a while, so we can build on the names already used there.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Acked-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

The AXP803 has been around for about a decade now, but so far we didn't need SPL support, since the DRAM rail was wired up correctly at reset.
Now some boards using the A133 SoC use the (compatible) AXP707 with DDR4 memory, which requires the SPL to set the required 1.1V voltage manually.
Add the descriptions for the DC/DC regulators of the AXP803, and enable that when CONFIG_AXP803_POWER is enabled.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/mach-sunxi/pmic_bus.c | 3 +++ board/sunxi/board.c | 5 ++++- drivers/power/Kconfig | 17 ++++++++++++----- drivers/power/Makefile | 1 + drivers/power/axp_spl.c | 17 +++++++++++++++++ 5 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-sunxi/pmic_bus.c b/arch/arm/mach-sunxi/pmic_bus.c index f0201f76f42..b8b51dae6b0 100644 --- a/arch/arm/mach-sunxi/pmic_bus.c +++ b/arch/arm/mach-sunxi/pmic_bus.c @@ -20,6 +20,7 @@
#define AXP209_I2C_ADDR 0x34 #define AXP717_I2C_ADDR 0x34 +#define AXP803_I2C_ADDR 0x34
#define AXP305_I2C_ADDR 0x36 #define AXP313_I2C_ADDR 0x36 @@ -44,6 +45,8 @@ static int pmic_i2c_address(void) return AXP313_I2C_ADDR; if (IS_ENABLED(CONFIG_AXP717_POWER)) return AXP717_I2C_ADDR + i2c_offset; + if (IS_ENABLED(CONFIG_AXP803_POWER)) + return AXP803_I2C_ADDR + i2c_offset;
/* Other AXP2xx and AXP8xx variants */ return AXP209_I2C_ADDR; diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 824c322a0dc..a4a84d652d8 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -562,7 +562,8 @@ void sunxi_board_init(void) #if defined CONFIG_AXP152_POWER || defined CONFIG_AXP209_POWER || \ defined CONFIG_AXP221_POWER || defined CONFIG_AXP305_POWER || \ defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER || \ - defined CONFIG_AXP313_POWER || defined CONFIG_AXP717_POWER + defined CONFIG_AXP313_POWER || defined CONFIG_AXP717_POWER || \ + defined CONFIG_AXP803_POWER power_failed = axp_init();
if (IS_ENABLED(CONFIG_AXP_DISABLE_BOOT_ON_POWERON) && !power_failed) { @@ -581,6 +582,8 @@ void sunxi_board_init(void) #endif #ifdef CONFIG_AXP_DCDC2_VOLT power_failed |= axp_set_dcdc2(CONFIG_AXP_DCDC2_VOLT); +#endif +#ifdef CONFIG_AXP_DCDC3_VOLT power_failed |= axp_set_dcdc3(CONFIG_AXP_DCDC3_VOLT); #endif #ifdef CONFIG_AXP_DCDC4_VOLT diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 4c2211097fd..e163217dfdf 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -116,6 +116,12 @@ config AXP717_POWER ---help--- Select this to enable support for the AXP717 PMIC found on some boards.
+config AXP803_POWER + bool "AXP803 PMIC support" + select AXP_PMIC_BUS + ---help--- + Select this to enable support for the AXP803 PMIC found on some boards. + config AXP809_POWER bool "axp809 pmic support" depends on MACH_SUN9I @@ -144,7 +150,7 @@ endchoice
config AXP_ALT_I2C_ADDR bool "use alternative I2C address" - depends on AXP717_POWER + depends on AXP717_POWER || AXP803_POWER ---help--- Some PMICs can be ordered with a different I2C address (+1), to avoid address clashes or accommodate two PMICs on the same bus. @@ -152,8 +158,8 @@ config AXP_ALT_I2C_ADDR
config AXP_DCDC1_VOLT int "axp pmic dcdc1 voltage" - depends on AXP221_POWER || AXP809_POWER || AXP818_POWER - default 3300 if AXP818_POWER || MACH_SUN8I_R40 + depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER + default 3300 if AXP818_POWER || MACH_SUN8I_R40 || AXP803_POWER default 3000 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I ---help--- Set the voltage (mV) to program the axp pmic dcdc1 at, set to 0 to @@ -166,11 +172,12 @@ config AXP_DCDC1_VOLT
config AXP_DCDC2_VOLT int "axp pmic dcdc2 voltage" - depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER || AXP717_POWER + depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER || AXP717_POWER || AXP803_POWER default 900 if AXP818_POWER default 1400 if AXP152_POWER || AXP209_POWER default 1000 if AXP313_POWER default 1000 if AXP717_POWER + default 1000 if AXP803_POWER default 1200 if MACH_SUN6I default 1100 if MACH_SUN8I default 0 if MACH_SUN9I @@ -227,7 +234,7 @@ config AXP_DCDC4_VOLT
config AXP_DCDC5_VOLT int "axp pmic dcdc5 voltage" - depends on AXP221_POWER || AXP809_POWER || AXP818_POWER + depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER default 1500 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I ---help--- Set the voltage (mV) to program the axp pmic dcdc5 at, set to 0 to diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 3f4d56f5139..3363191fdc8 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_AXP313_POWER) += axp_spl.o obj-$(CONFIG_AXP717_POWER) += axp_spl.o obj-$(CONFIG_AXP809_POWER) += axp809.o obj-$(CONFIG_AXP818_POWER) += axp818.o +obj-$(CONFIG_AXP803_POWER) += axp_spl.o endif obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o obj-$(CONFIG_SY8106A_POWER) += sy8106a.o diff --git a/drivers/power/axp_spl.c b/drivers/power/axp_spl.c index 3c86eb20ab4..7c51a9b3dfb 100644 --- a/drivers/power/axp_spl.c +++ b/drivers/power/axp_spl.c @@ -36,6 +36,23 @@ static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = { #define AXP_SHUTDOWN_REG 0x27 #define AXP_SHUTDOWN_MASK BIT(0)
+#elif defined(CONFIG_AXP803_POWER) /* AXP803 */ + +static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = { + { 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA }, + { 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 }, + { 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 }, +}; + +#define AXP_CHIP_VERSION 0x3 +#define AXP_CHIP_VERSION_MASK 0xcf +#define AXP_CHIP_ID 0x41 +#define AXP_SHUTDOWN_REG 0x32 +#define AXP_SHUTDOWN_MASK BIT(7) + #elif defined(CONFIG_AXP313_POWER) /* AXP313 */
static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = {

Dne petek, 17. januar 2025 ob 02:45:35 Srednjeevropski standardni čas je Andre Przywara napisal(a):
The AXP803 has been around for about a decade now, but so far we didn't need SPL support, since the DRAM rail was wired up correctly at reset.
Now some boards using the A133 SoC use the (compatible) AXP707 with DDR4 memory, which requires the SPL to set the required 1.1V voltage manually.
Add the descriptions for the DC/DC regulators of the AXP803, and enable that when CONFIG_AXP803_POWER is enabled.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Acked-by: Jernej Skrabec jernej.skrabec@gmail.com
Best regards, Jernej

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

Dne petek, 17. januar 2025 ob 02:45:36 Srednjeevropski standardni čas je Andre Przywara napisal(a):
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.
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@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@epochal.quest
- Uses code from H616 driver, which is
- (C) Copyright 2020 Jernej Skrabec jernej.skrabec@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);
+}

On 2025/01/18 4:17 am, Jernej Škrabec wrote:
Dne petek, 17. januar 2025 ob 02:45:36 Srednjeevropski standardni čas je Andre Przywara napisal(a):
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.
Since this is clearly WIP and SoF missing, just some comments below.
Hi Jernej; thank you for your feedback! Some replies and clarifications below.
*snip*
+/* 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.
This is partially true; para1/para2/tpr13 do store DRAM organization parameters, but para0 also stores a value that's written during `phy_dx_delay_compensation`; it's most similar to `odt_en` on the H616. I have further revisions of this code that implement DRAM detection and fix some issues with DRAM detection, and those do away with para1/para2/tpr13.
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.
Not all are overridden in the case of DDR4; quite a few are provided by the DRAM config. It's very possible these are similar across different devices, but considering the relatively small number of DDR4 sunxi devices in general, this seemed hard to say for sure.
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.)
Good catch.
- 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@epochal.quest
- Uses code from H616 driver, which is
- (C) Copyright 2020 Jernej Skrabec jernej.skrabec@siol.net
- */
+#define DEBUG
This should go away in final version.
Will not be present in final versions.
+#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>
*snip*
+/* 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.
I have some working auto scan code that I hope to submit in the future; these were unused when this patch was pulled into Andre's patch series due to it not functioning as expected.
A newer, rewritten driver is available at [1], though I still need to find some time to clean it up and properly submit it as I had to make some silly patches to fix edge cases on systems with the full 4GiB of RAM.
Thanks for your comments! - Cody
[1] https://github.com/BrokenR3C0RD/u-boot/blob/allwinner-a133/arch/arm/mach-sun...
Best regards, Jernej

The Allwinner A100 SoC has been around for a while, mostly on cheap tablets, but didn't generate much interest in the community so far. There were some efforts by two Allwinner employees in 2020, which led to basic upstream Linux support for that SoC, although this momentum dried up pretty quickly, leaving a lot of peripherals unsupported.
The A100 was silently replaced with the seemingly identical Allwinner A133, which is reportedly a better bin of the A100. So far we assume that both are compatible from a software perspective. There are some more devices with the A133 out there now, so people are working on filling the gaps, and adding U-Boot (and TF-A) support.
Based on the just added pinctrl, clock and DRAM support, this adds the missing bits, mostly addresses and values for the SPL.
The A133 seems to be an predecessor to the H6, so we can share a lot of code with that (and the H616 code), and just need to adjust some details.
Signed-off-by: Andre Przywara andre.przywara@arm.com --- arch/arm/cpu/armv8/fel_utils.S | 2 +- arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 7 +++++++ arch/arm/mach-sunxi/Kconfig | 11 +++++++++-- arch/arm/mach-sunxi/board.c | 4 ++++ arch/arm/mach-sunxi/clock_sun50i_h6.c | 3 ++- arch/arm/mach-sunxi/cpu_info.c | 2 ++ board/sunxi/board.c | 8 ++++++-- common/spl/Kconfig | 6 +++--- 8 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S index 939869b9ffa..c9f3990226e 100644 --- a/arch/arm/cpu/armv8/fel_utils.S +++ b/arch/arm/cpu/armv8/fel_utils.S @@ -41,7 +41,7 @@ ENTRY(return_to_fel) str w2, [x1]
ldr w0, =0xfa50392f // CPU hotplug magic -#ifdef CONFIG_MACH_SUN50I_H616 +#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) ldr w2, =(SUNXI_R_CPUCFG_BASE + 0x1c0) str w0, [x2], #0x4 #elif CONFIG_MACH_SUN50I_H6 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h index 76dd33c9477..6c1d36836dd 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h @@ -285,6 +285,13 @@ struct sunxi_ccm_reg { #define CCM_PLL6_DEFAULT 0xe8216300 #define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 #define CCM_APB1_DEFAULT 0x03000102 + +#elif CONFIG_MACH_SUN50I_A133 /* A133 */ + +#define CCM_PLL6_DEFAULT 0xb8003100 +#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 +#define CCM_AHB3_DEFAULT 0x03000002 +#define CCM_APB1_DEFAULT 0x03000102 #endif
/* apb2 bit field */ diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index ccf0c9a403e..b203244eb95 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -300,6 +300,7 @@ config SUNXI_SRAM_ADDRESS config SUNXI_RVBAR_ADDRESS hex depends on ARM64 + default 0x08100040 if MACH_SUN50I_A133 default 0x09010040 if SUN50I_GEN_H6 default 0x017000a0 ---help--- @@ -326,8 +327,8 @@ config SUNXI_RVBAR_ALTERNATIVE config SUNXI_BL31_BASE hex default 0x00044000 if MACH_SUN50I || MACH_SUN50I_H5 - default 0x00104000 if MACH_SUN50I_H6 default 0x40000000 if MACH_SUN50I_H616 + default 0x00104000 if SUN50I_GEN_H6 default 0x0 help Address where BL31 (TF-A) is loaded, or zero if BL31 is not used. @@ -409,7 +410,7 @@ config MACH_SUNXI_H3_H5 # TODO: try out A80's 8GiB DRAM space config SUNXI_DRAM_MAX_SIZE hex - default 0x100000000 if MACH_SUN50I_H616 + default 0x100000000 if MACH_SUN50I_H616 || MACH_SUN50I_A133 default 0xC0000000 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN50I_H6 default 0x80000000
@@ -606,6 +607,10 @@ config MACH_SUN50I_H616
config MACH_SUN50I_A133 bool "sun50i (Allwinner A133)" + select ARM64 + select DRAM_SUN50I_A133 + select SUN50I_GEN_H6 + imply OF_UPSTREAM
endchoice
@@ -894,6 +899,7 @@ config SYS_CLK_FREQ default 888000000 if MACH_SUN50I_H6 default 1008000000 if MACH_SUN50I_H616 default 1008000000 if MACH_SUN8I_R528 + default 1008000000 if MACH_SUN50I_A133
config SYS_CONFIG_NAME default "suniv" if MACH_SUNIV @@ -907,6 +913,7 @@ config SYS_CONFIG_NAME default "sun50i" if MACH_SUN50I default "sun50i" if MACH_SUN50I_H6 default "sun50i" if MACH_SUN50I_H616 + default "sun50i" if MACH_SUN50I_A133
config SYS_BOARD default "sunxi" diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index 701899ee4b2..264dd6a7460 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -134,6 +134,10 @@ static int gpio_init(void) sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H616_GPH_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H616_GPH_UART0); sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I_A133) + sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_H616_GPH_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN50I_H616_GPH_UART0); + sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP); #elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A83T) sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0); diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c index b424a7893ea..9797dca1e1d 100644 --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c @@ -90,7 +90,8 @@ void clock_set_pll1(unsigned int clk) /* clk = 24*n/p, p is ignored if clock is >288MHz */ val = CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2; val |= CCM_PLL1_CTRL_N(clk / 24000000); - if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) + if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) || + IS_ENABLED(CONFIG_MACH_SUN50I_A133)) val |= CCM_PLL1_OUT_EN; if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN; diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c index 310dca06e57..3f4735d4717 100644 --- a/arch/arm/mach-sunxi/cpu_info.c +++ b/arch/arm/mach-sunxi/cpu_info.c @@ -104,6 +104,8 @@ int print_cpuinfo(void) puts("CPU: Allwinner H6 (SUN50I)\n"); #elif defined CONFIG_MACH_SUN50I_H616 puts("CPU: Allwinner H616 (SUN50I)\n"); +#elif defined CONFIG_MACH_SUN50I_A133 + puts("CPU: Allwinner A133 (SUN50I)\n"); #else #warning Please update cpu_info.c with correct CPU information puts("CPU: SUNXI Family\n"); diff --git a/board/sunxi/board.c b/board/sunxi/board.c index a4a84d652d8..7ed1f96a4fb 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -113,10 +113,14 @@ void i2c_init_board(void) clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(8), SUN50I_GPL_R_TWI); sunxi_gpio_set_cfgpin(SUNXI_GPL(9), SUN50I_GPL_R_TWI); -#elif CONFIG_MACH_SUN50I_H616 +#elif defined(CONFIG_MACH_SUN50I_H616) clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_H616_GPL_R_TWI); sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_H616_GPL_R_TWI); +#elif CONFIG_MACH_SUN50I_A133 + clock_twi_onoff(5, 1); + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_GPL_R_TWI); + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_GPL_R_TWI); #else clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_H3_GPL_R_TWI); @@ -434,7 +438,7 @@ static void mmc_pinmux_setup(int sdc) sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); sunxi_gpio_set_drv(pin, 2); } -#elif defined(CONFIG_MACH_SUN50I_H616) +#elif defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) /* SDC2: PC0-PC1, PC5-PC6, PC8-PC11, PC13-PC16 */ for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(16); pin++) { if (pin > SUNXI_GPC(1) && pin < SUNXI_GPC(5)) diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 4e56d9909c8..ce075a58193 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -81,8 +81,8 @@ config SPL_MAX_SIZE default 0xec00 if OMAP34XX default 0x10000 if ARCH_MX6 && !MX6_OCRAM_256KB default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x10000 - default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x20000 && !MACH_SUN50I_H616 - default 0xbfa0 if MACH_SUN50I_H616 + default 0xbfa0 if MACH_SUN50I_H616 || MACH_SUN50I_A133 + default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x20000 default 0x7000 if RCAR_GEN3 default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0 default 0x10000 if ASPEED_AST2600 @@ -397,7 +397,7 @@ config SPL_STACK default 0x91ffb8 if ARCH_MX6 && !MX6_OCRAM_256KB default 0x118000 if MACH_SUN50I_H6 default 0x52a00 if MACH_SUN50I_H616 - default 0x40000 if MACH_SUN8I_R528 + default 0x40000 if MACH_SUN8I_R528 || MACH_SUN50I_A133 default 0x54000 if MACH_SUN50I || MACH_SUN50I_H5 default 0x18000 if MACH_SUN9I default 0x8000 if ARCH_SUNXI

Dne petek, 17. januar 2025 ob 02:45:37 Srednjeevropski standardni čas je Andre Przywara napisal(a):
The Allwinner A100 SoC has been around for a while, mostly on cheap tablets, but didn't generate much interest in the community so far. There were some efforts by two Allwinner employees in 2020, which led to basic upstream Linux support for that SoC, although this momentum dried up pretty quickly, leaving a lot of peripherals unsupported.
The A100 was silently replaced with the seemingly identical Allwinner A133, which is reportedly a better bin of the A100. So far we assume that both are compatible from a software perspective. There are some more devices with the A133 out there now, so people are working on filling the gaps, and adding U-Boot (and TF-A) support.
Based on the just added pinctrl, clock and DRAM support, this adds the missing bits, mostly addresses and values for the SPL.
The A133 seems to be an predecessor to the H6, so we can share a lot of code with that (and the H616 code), and just need to adjust some details.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Pretty standard stuff, just one remark below.
Acked-by: Jernej Skrabec jernej.skrabec@gmail.com
arch/arm/cpu/armv8/fel_utils.S | 2 +- arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 7 +++++++ arch/arm/mach-sunxi/Kconfig | 11 +++++++++-- arch/arm/mach-sunxi/board.c | 4 ++++ arch/arm/mach-sunxi/clock_sun50i_h6.c | 3 ++- arch/arm/mach-sunxi/cpu_info.c | 2 ++ board/sunxi/board.c | 8 ++++++-- common/spl/Kconfig | 6 +++--- 8 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S index 939869b9ffa..c9f3990226e 100644 --- a/arch/arm/cpu/armv8/fel_utils.S +++ b/arch/arm/cpu/armv8/fel_utils.S @@ -41,7 +41,7 @@ ENTRY(return_to_fel) str w2, [x1]
ldr w0, =0xfa50392f // CPU hotplug magic -#ifdef CONFIG_MACH_SUN50I_H616 +#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) ldr w2, =(SUNXI_R_CPUCFG_BASE + 0x1c0) str w0, [x2], #0x4 #elif CONFIG_MACH_SUN50I_H6 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h index 76dd33c9477..6c1d36836dd 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h @@ -285,6 +285,13 @@ struct sunxi_ccm_reg { #define CCM_PLL6_DEFAULT 0xe8216300 #define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 #define CCM_APB1_DEFAULT 0x03000102
+#elif CONFIG_MACH_SUN50I_A133 /* A133 */
+#define CCM_PLL6_DEFAULT 0xb8003100 +#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 +#define CCM_AHB3_DEFAULT 0x03000002 +#define CCM_APB1_DEFAULT 0x03000102 #endif
/* apb2 bit field */ diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index ccf0c9a403e..b203244eb95 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -300,6 +300,7 @@ config SUNXI_SRAM_ADDRESS config SUNXI_RVBAR_ADDRESS hex depends on ARM64
- default 0x08100040 if MACH_SUN50I_A133 default 0x09010040 if SUN50I_GEN_H6 default 0x017000a0 ---help---
@@ -326,8 +327,8 @@ config SUNXI_RVBAR_ALTERNATIVE config SUNXI_BL31_BASE hex default 0x00044000 if MACH_SUN50I || MACH_SUN50I_H5
- default 0x00104000 if MACH_SUN50I_H6 default 0x40000000 if MACH_SUN50I_H616
- default 0x00104000 if SUN50I_GEN_H6 default 0x0 help Address where BL31 (TF-A) is loaded, or zero if BL31 is not used.
@@ -409,7 +410,7 @@ config MACH_SUNXI_H3_H5 # TODO: try out A80's 8GiB DRAM space config SUNXI_DRAM_MAX_SIZE hex
- default 0x100000000 if MACH_SUN50I_H616
- default 0x100000000 if MACH_SUN50I_H616 || MACH_SUN50I_A133 default 0xC0000000 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN50I_H6 default 0x80000000
@@ -606,6 +607,10 @@ config MACH_SUN50I_H616
config MACH_SUN50I_A133 bool "sun50i (Allwinner A133)"
- select ARM64
- select DRAM_SUN50I_A133
- select SUN50I_GEN_H6
- imply OF_UPSTREAM
endchoice
@@ -894,6 +899,7 @@ config SYS_CLK_FREQ default 888000000 if MACH_SUN50I_H6 default 1008000000 if MACH_SUN50I_H616 default 1008000000 if MACH_SUN8I_R528
- default 1008000000 if MACH_SUN50I_A133
Since 1008000000 looks like being default for most supported SoCs, why not make it default in last line? Upcomming A523 will have it too and it would allow to make diff smaller.
Best regards, Jernej
config SYS_CONFIG_NAME default "suniv" if MACH_SUNIV @@ -907,6 +913,7 @@ config SYS_CONFIG_NAME default "sun50i" if MACH_SUN50I default "sun50i" if MACH_SUN50I_H6 default "sun50i" if MACH_SUN50I_H616
- default "sun50i" if MACH_SUN50I_A133
config SYS_BOARD default "sunxi" diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index 701899ee4b2..264dd6a7460 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -134,6 +134,10 @@ static int gpio_init(void) sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H616_GPH_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H616_GPH_UART0); sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I_A133)
- sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_H616_GPH_UART0);
- sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN50I_H616_GPH_UART0);
- sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A83T) sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0); diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c index b424a7893ea..9797dca1e1d 100644 --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c @@ -90,7 +90,8 @@ void clock_set_pll1(unsigned int clk) /* clk = 24*n/p, p is ignored if clock is >288MHz */ val = CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2; val |= CCM_PLL1_CTRL_N(clk / 24000000);
- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN;IS_ENABLED(CONFIG_MACH_SUN50I_A133)) val |= CCM_PLL1_OUT_EN;
diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c index 310dca06e57..3f4735d4717 100644 --- a/arch/arm/mach-sunxi/cpu_info.c +++ b/arch/arm/mach-sunxi/cpu_info.c @@ -104,6 +104,8 @@ int print_cpuinfo(void) puts("CPU: Allwinner H6 (SUN50I)\n"); #elif defined CONFIG_MACH_SUN50I_H616 puts("CPU: Allwinner H616 (SUN50I)\n"); +#elif defined CONFIG_MACH_SUN50I_A133
- puts("CPU: Allwinner A133 (SUN50I)\n");
#else #warning Please update cpu_info.c with correct CPU information puts("CPU: SUNXI Family\n"); diff --git a/board/sunxi/board.c b/board/sunxi/board.c index a4a84d652d8..7ed1f96a4fb 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -113,10 +113,14 @@ void i2c_init_board(void) clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(8), SUN50I_GPL_R_TWI); sunxi_gpio_set_cfgpin(SUNXI_GPL(9), SUN50I_GPL_R_TWI); -#elif CONFIG_MACH_SUN50I_H616 +#elif defined(CONFIG_MACH_SUN50I_H616) clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_H616_GPL_R_TWI); sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_H616_GPL_R_TWI); +#elif CONFIG_MACH_SUN50I_A133
- clock_twi_onoff(5, 1);
- sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_GPL_R_TWI);
- sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_GPL_R_TWI);
#else clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_H3_GPL_R_TWI); @@ -434,7 +438,7 @@ static void mmc_pinmux_setup(int sdc) sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); sunxi_gpio_set_drv(pin, 2); } -#elif defined(CONFIG_MACH_SUN50I_H616) +#elif defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) /* SDC2: PC0-PC1, PC5-PC6, PC8-PC11, PC13-PC16 */ for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(16); pin++) { if (pin > SUNXI_GPC(1) && pin < SUNXI_GPC(5)) diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 4e56d9909c8..ce075a58193 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -81,8 +81,8 @@ config SPL_MAX_SIZE default 0xec00 if OMAP34XX default 0x10000 if ARCH_MX6 && !MX6_OCRAM_256KB default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x10000
- default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x20000 && !MACH_SUN50I_H616
- default 0xbfa0 if MACH_SUN50I_H616
- default 0xbfa0 if MACH_SUN50I_H616 || MACH_SUN50I_A133
- default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x20000 default 0x7000 if RCAR_GEN3 default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0 default 0x10000 if ASPEED_AST2600
@@ -397,7 +397,7 @@ config SPL_STACK default 0x91ffb8 if ARCH_MX6 && !MX6_OCRAM_256KB default 0x118000 if MACH_SUN50I_H6 default 0x52a00 if MACH_SUN50I_H616
- default 0x40000 if MACH_SUN8I_R528
- default 0x40000 if MACH_SUN8I_R528 || MACH_SUN50I_A133 default 0x54000 if MACH_SUN50I || MACH_SUN50I_H5 default 0x18000 if MACH_SUN9I default 0x8000 if ARCH_SUNXI

On Sat, 18 Jan 2025 08:35:55 +0100 Jernej Škrabec jernej.skrabec@gmail.com wrote:
Dne petek, 17. januar 2025 ob 02:45:37 Srednjeevropski standardni čas je Andre Przywara napisal(a):
The Allwinner A100 SoC has been around for a while, mostly on cheap tablets, but didn't generate much interest in the community so far. There were some efforts by two Allwinner employees in 2020, which led to basic upstream Linux support for that SoC, although this momentum dried up pretty quickly, leaving a lot of peripherals unsupported.
The A100 was silently replaced with the seemingly identical Allwinner A133, which is reportedly a better bin of the A100. So far we assume that both are compatible from a software perspective. There are some more devices with the A133 out there now, so people are working on filling the gaps, and adding U-Boot (and TF-A) support.
Based on the just added pinctrl, clock and DRAM support, this adds the missing bits, mostly addresses and values for the SPL.
The A133 seems to be an predecessor to the H6, so we can share a lot of code with that (and the H616 code), and just need to adjust some details.
Signed-off-by: Andre Przywara andre.przywara@arm.com
Pretty standard stuff, just one remark below.
Acked-by: Jernej Skrabec jernej.skrabec@gmail.com
arch/arm/cpu/armv8/fel_utils.S | 2 +- arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 7 +++++++ arch/arm/mach-sunxi/Kconfig | 11 +++++++++-- arch/arm/mach-sunxi/board.c | 4 ++++ arch/arm/mach-sunxi/clock_sun50i_h6.c | 3 ++- arch/arm/mach-sunxi/cpu_info.c | 2 ++ board/sunxi/board.c | 8 ++++++-- common/spl/Kconfig | 6 +++--- 8 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S index 939869b9ffa..c9f3990226e 100644 --- a/arch/arm/cpu/armv8/fel_utils.S +++ b/arch/arm/cpu/armv8/fel_utils.S @@ -41,7 +41,7 @@ ENTRY(return_to_fel) str w2, [x1]
ldr w0, =0xfa50392f // CPU hotplug magic -#ifdef CONFIG_MACH_SUN50I_H616 +#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) ldr w2, =(SUNXI_R_CPUCFG_BASE + 0x1c0) str w0, [x2], #0x4 #elif CONFIG_MACH_SUN50I_H6 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h index 76dd33c9477..6c1d36836dd 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h @@ -285,6 +285,13 @@ struct sunxi_ccm_reg { #define CCM_PLL6_DEFAULT 0xe8216300 #define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 #define CCM_APB1_DEFAULT 0x03000102
+#elif CONFIG_MACH_SUN50I_A133 /* A133 */
+#define CCM_PLL6_DEFAULT 0xb8003100 +#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 +#define CCM_AHB3_DEFAULT 0x03000002 +#define CCM_APB1_DEFAULT 0x03000102 #endif
/* apb2 bit field */ diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index ccf0c9a403e..b203244eb95 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -300,6 +300,7 @@ config SUNXI_SRAM_ADDRESS config SUNXI_RVBAR_ADDRESS hex depends on ARM64
- default 0x08100040 if MACH_SUN50I_A133 default 0x09010040 if SUN50I_GEN_H6 default 0x017000a0 ---help---
@@ -326,8 +327,8 @@ config SUNXI_RVBAR_ALTERNATIVE config SUNXI_BL31_BASE hex default 0x00044000 if MACH_SUN50I || MACH_SUN50I_H5
- default 0x00104000 if MACH_SUN50I_H6 default 0x40000000 if MACH_SUN50I_H616
- default 0x00104000 if SUN50I_GEN_H6 default 0x0 help Address where BL31 (TF-A) is loaded, or zero if BL31 is not used.
@@ -409,7 +410,7 @@ config MACH_SUNXI_H3_H5 # TODO: try out A80's 8GiB DRAM space config SUNXI_DRAM_MAX_SIZE hex
- default 0x100000000 if MACH_SUN50I_H616
- default 0x100000000 if MACH_SUN50I_H616 || MACH_SUN50I_A133 default 0xC0000000 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN50I_H6 default 0x80000000
@@ -606,6 +607,10 @@ config MACH_SUN50I_H616
config MACH_SUN50I_A133 bool "sun50i (Allwinner A133)"
- select ARM64
- select DRAM_SUN50I_A133
- select SUN50I_GEN_H6
- imply OF_UPSTREAM
endchoice
@@ -894,6 +899,7 @@ config SYS_CLK_FREQ default 888000000 if MACH_SUN50I_H6 default 1008000000 if MACH_SUN50I_H616 default 1008000000 if MACH_SUN8I_R528
- default 1008000000 if MACH_SUN50I_A133
Since 1008000000 looks like being default for most supported SoCs, why not make it default in last line? Upcomming A523 will have it too and it would allow to make diff smaller.
Yeah, that's a good idea, indeed 1008 MHz is the most common case.
In my A523 tree I actually went for 792 MHz for the A523, since that's the highest frequency supported at the default 900mV. But that's another discussion, and we can probably program DCDC1 to something higher.
Cheers, Andre
Best regards, Jernej
config SYS_CONFIG_NAME default "suniv" if MACH_SUNIV @@ -907,6 +913,7 @@ config SYS_CONFIG_NAME default "sun50i" if MACH_SUN50I default "sun50i" if MACH_SUN50I_H6 default "sun50i" if MACH_SUN50I_H616
- default "sun50i" if MACH_SUN50I_A133
config SYS_BOARD default "sunxi" diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index 701899ee4b2..264dd6a7460 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -134,6 +134,10 @@ static int gpio_init(void) sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H616_GPH_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H616_GPH_UART0); sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I_A133)
- sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_H616_GPH_UART0);
- sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN50I_H616_GPH_UART0);
- sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A83T) sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0); diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c index b424a7893ea..9797dca1e1d 100644 --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c @@ -90,7 +90,8 @@ void clock_set_pll1(unsigned int clk) /* clk = 24*n/p, p is ignored if clock is >288MHz */ val = CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2; val |= CCM_PLL1_CTRL_N(clk / 24000000);
- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN;IS_ENABLED(CONFIG_MACH_SUN50I_A133)) val |= CCM_PLL1_OUT_EN;
diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c index 310dca06e57..3f4735d4717 100644 --- a/arch/arm/mach-sunxi/cpu_info.c +++ b/arch/arm/mach-sunxi/cpu_info.c @@ -104,6 +104,8 @@ int print_cpuinfo(void) puts("CPU: Allwinner H6 (SUN50I)\n"); #elif defined CONFIG_MACH_SUN50I_H616 puts("CPU: Allwinner H616 (SUN50I)\n"); +#elif defined CONFIG_MACH_SUN50I_A133
- puts("CPU: Allwinner A133 (SUN50I)\n");
#else #warning Please update cpu_info.c with correct CPU information puts("CPU: SUNXI Family\n"); diff --git a/board/sunxi/board.c b/board/sunxi/board.c index a4a84d652d8..7ed1f96a4fb 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -113,10 +113,14 @@ void i2c_init_board(void) clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(8), SUN50I_GPL_R_TWI); sunxi_gpio_set_cfgpin(SUNXI_GPL(9), SUN50I_GPL_R_TWI); -#elif CONFIG_MACH_SUN50I_H616 +#elif defined(CONFIG_MACH_SUN50I_H616) clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_H616_GPL_R_TWI); sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_H616_GPL_R_TWI); +#elif CONFIG_MACH_SUN50I_A133
- clock_twi_onoff(5, 1);
- sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_GPL_R_TWI);
- sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_GPL_R_TWI);
#else clock_twi_onoff(5, 1); sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_H3_GPL_R_TWI); @@ -434,7 +438,7 @@ static void mmc_pinmux_setup(int sdc) sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); sunxi_gpio_set_drv(pin, 2); } -#elif defined(CONFIG_MACH_SUN50I_H616) +#elif defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) /* SDC2: PC0-PC1, PC5-PC6, PC8-PC11, PC13-PC16 */ for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(16); pin++) { if (pin > SUNXI_GPC(1) && pin < SUNXI_GPC(5)) diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 4e56d9909c8..ce075a58193 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -81,8 +81,8 @@ config SPL_MAX_SIZE default 0xec00 if OMAP34XX default 0x10000 if ARCH_MX6 && !MX6_OCRAM_256KB default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x10000
- default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x20000 && !MACH_SUN50I_H616
- default 0xbfa0 if MACH_SUN50I_H616
- default 0xbfa0 if MACH_SUN50I_H616 || MACH_SUN50I_A133
- default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x20000 default 0x7000 if RCAR_GEN3 default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0 default 0x10000 if ASPEED_AST2600
@@ -397,7 +397,7 @@ config SPL_STACK default 0x91ffb8 if ARCH_MX6 && !MX6_OCRAM_256KB default 0x118000 if MACH_SUN50I_H6 default 0x52a00 if MACH_SUN50I_H616
- default 0x40000 if MACH_SUN8I_R528
- default 0x40000 if MACH_SUN8I_R528 || MACH_SUN50I_A133 default 0x54000 if MACH_SUN50I || MACH_SUN50I_H5 default 0x18000 if MACH_SUN9I default 0x8000 if ARCH_SUNXI
participants (4)
-
Andre Przywara
-
Cody Eksal
-
Jernej Škrabec
-
Simon Glass