1063 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1063 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright (C) ASPEED Technology Inc.
 | |
|  */
 | |
| #include <common.h>
 | |
| #include <clk.h>
 | |
| #include <dm.h>
 | |
| #include <errno.h>
 | |
| #include <ram.h>
 | |
| #include <regmap.h>
 | |
| #include <reset.h>
 | |
| #include <asm/io.h>
 | |
| #include <asm/arch/scu_ast2600.h>
 | |
| #include <asm/arch/sdram_ast2600.h>
 | |
| #include <asm/global_data.h>
 | |
| #include <linux/err.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <dt-bindings/clock/ast2600-clock.h>
 | |
| 
 | |
| #define DDR_PHY_TBL_CHG_ADDR            0xaeeddeea
 | |
| #define DDR_PHY_TBL_END                 0xaeededed
 | |
| 
 | |
| #if defined(CONFIG_ASPEED_DDR4_800)
 | |
| u32 ast2600_sdramphy_config[165] = {
 | |
| 	0x1e6e0100,	// start address
 | |
| 	0x00000000,	// phyr000
 | |
| 	0x0c002062,	// phyr004
 | |
| 	0x1a7a0063,	// phyr008
 | |
| 	0x5a7a0063,	// phyr00c
 | |
| 	0x1a7a0063,	// phyr010
 | |
| 	0x1a7a0063,	// phyr014
 | |
| 	0x20000000,	// phyr018
 | |
| 	0x20000000,	// phyr01c
 | |
| 	0x20000000,	// phyr020
 | |
| 	0x20000000,	// phyr024
 | |
| 	0x00000008,	// phyr028
 | |
| 	0x00000000,	// phyr02c
 | |
| 	0x00077600,	// phyr030
 | |
| 	0x00000000,	// phyr034
 | |
| 	0x00000000,	// phyr038
 | |
| 	0x20000000,	// phyr03c
 | |
| 	0x50506000,	// phyr040
 | |
| 	0x50505050,	// phyr044
 | |
| 	0x00002f07,	// phyr048
 | |
| 	0x00003080,	// phyr04c
 | |
| 	0x04000000,	// phyr050
 | |
| 	0x00000200,	// phyr054
 | |
| 	0x03140201,	// phyr058
 | |
| 	0x04800000,	// phyr05c
 | |
| 	0x0800044e,	// phyr060
 | |
| 	0x00000000,	// phyr064
 | |
| 	0x00180008,	// phyr068
 | |
| 	0x00e00400,	// phyr06c
 | |
| 	0x00140206,	// phyr070
 | |
| 	0x1d4c0000,	// phyr074
 | |
| 	0x493e0107,	// phyr078
 | |
| 	0x08060404,	// phyr07c
 | |
| 	0x90000a00,	// phyr080
 | |
| 	0x06420618,	// phyr084
 | |
| 	0x00001002,	// phyr088
 | |
| 	0x05701016,	// phyr08c
 | |
| 	0x10000000,	// phyr090
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e019c,	// new address
 | |
| 	0x20202020,	// phyr09c
 | |
| 	0x20202020,	// phyr0a0
 | |
| 	0x00002020,	// phyr0a4
 | |
| 	0x00002020,	// phyr0a8
 | |
| 	0x00000001,	// phyr0ac
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e01cc,	// new address
 | |
| 	0x01010101,	// phyr0cc
 | |
| 	0x01010101,	// phyr0d0
 | |
| 	0x80808080,	// phyr0d4
 | |
| 	0x80808080,	// phyr0d8
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0288,	// new address
 | |
| 	0x80808080,	// phyr188
 | |
| 	0x80808080,	// phyr18c
 | |
| 	0x80808080,	// phyr190
 | |
| 	0x80808080,	// phyr194
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e02f8,	// new address
 | |
| 	0x90909090,	// phyr1f8
 | |
| 	0x88888888,	// phyr1fc
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0300,	// new address
 | |
| 	0x00000000,	// phyr200
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0194,	// new address
 | |
| 	0x80118260,	// phyr094
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e019c,	// new address
 | |
| 	0x20202020,	// phyr09c
 | |
| 	0x20202020,	// phyr0a0
 | |
| 	0x00002020,	// phyr0a4
 | |
| 	0x80000000,	// phyr0a8
 | |
| 	0x00000001,	// phyr0ac
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0318,	// new address
 | |
| 	0x09222719,	// phyr218
 | |
| 	0x00aa4403,	// phyr21c
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0198,	// new address
 | |
| 	0x08060000,	// phyr098
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e01b0,	// new address
 | |
| 	0x00000000,	// phyr0b0
 | |
| 	0x00000000,	// phyr0b4
 | |
| 	0x00000000,	// phyr0b8
 | |
| 	0x00000000,	// phyr0bc
 | |
| 	0x00000000,	// phyr0c0
 | |
| 	0x00000000,	// phyr0c4
 | |
| 	0x000aff2c,	// phyr0c8
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e01dc,	// new address
 | |
| 	0x00080000,	// phyr0dc
 | |
| 	0x00000000,	// phyr0e0
 | |
| 	0xaa55aa55,	// phyr0e4
 | |
| 	0x55aa55aa,	// phyr0e8
 | |
| 	0xaaaa5555,	// phyr0ec
 | |
| 	0x5555aaaa,	// phyr0f0
 | |
| 	0xaa55aa55,	// phyr0f4
 | |
| 	0x55aa55aa,	// phyr0f8
 | |
| 	0xaaaa5555,	// phyr0fc
 | |
| 	0x5555aaaa,	// phyr100
 | |
| 	0xaa55aa55,	// phyr104
 | |
| 	0x55aa55aa,	// phyr108
 | |
| 	0xaaaa5555,	// phyr10c
 | |
| 	0x5555aaaa,	// phyr110
 | |
| 	0xaa55aa55,	// phyr114
 | |
| 	0x55aa55aa,	// phyr118
 | |
| 	0xaaaa5555,	// phyr11c
 | |
| 	0x5555aaaa,	// phyr120
 | |
| 	0x20202020,	// phyr124
 | |
| 	0x20202020,	// phyr128
 | |
| 	0x20202020,	// phyr12c
 | |
| 	0x20202020,	// phyr130
 | |
| 	0x20202020,	// phyr134
 | |
| 	0x20202020,	// phyr138
 | |
| 	0x20202020,	// phyr13c
 | |
| 	0x20202020,	// phyr140
 | |
| 	0x20202020,	// phyr144
 | |
| 	0x20202020,	// phyr148
 | |
| 	0x20202020,	// phyr14c
 | |
| 	0x20202020,	// phyr150
 | |
| 	0x20202020,	// phyr154
 | |
| 	0x20202020,	// phyr158
 | |
| 	0x20202020,	// phyr15c
 | |
| 	0x20202020,	// phyr160
 | |
| 	0x20202020,	// phyr164
 | |
| 	0x20202020,	// phyr168
 | |
| 	0x20202020,	// phyr16c
 | |
| 	0x20202020,	// phyr170
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0298,	// new address
 | |
| 	0x20200800,	// phyr198
 | |
| 	0x20202020,	// phyr19c
 | |
| 	0x20202020,	// phyr1a0
 | |
| 	0x20202020,	// phyr1a4
 | |
| 	0x20202020,	// phyr1a8
 | |
| 	0x20202020,	// phyr1ac
 | |
| 	0x20202020,	// phyr1b0
 | |
| 	0x20202020,	// phyr1b4
 | |
| 	0x20202020,	// phyr1b8
 | |
| 	0x20202020,	// phyr1bc
 | |
| 	0x20202020,	// phyr1c0
 | |
| 	0x20202020,	// phyr1c4
 | |
| 	0x20202020,	// phyr1c8
 | |
| 	0x20202020,	// phyr1cc
 | |
| 	0x20202020,	// phyr1d0
 | |
| 	0x20202020,	// phyr1d4
 | |
| 	0x20202020,	// phyr1d8
 | |
| 	0x20202020,	// phyr1dc
 | |
| 	0x20202020,	// phyr1e0
 | |
| 	0x20202020,	// phyr1e4
 | |
| 	0x00002020,	// phyr1e8
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0304,	// new address
 | |
| 	0x00000800,	// phyr204
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e027c,	// new address
 | |
| 	0x4e400000,	// phyr17c
 | |
| 	0x59595959,	// phyr180
 | |
| 	0x40404040,	// phyr184
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e02f4,	// new address
 | |
| 	0x00000059,	// phyr1f4
 | |
| 	0xaeededed,	// end
 | |
| };
 | |
| #else
 | |
| u32 ast2600_sdramphy_config[165] = {
 | |
| 	0x1e6e0100,	// start address
 | |
| 	0x00000000,	// phyr000
 | |
| 	0x0c002062,	// phyr004
 | |
| 	0x1a7a0063,	// phyr008
 | |
| 	0x5a7a0063,	// phyr00c
 | |
| 	0x1a7a0063,	// phyr010
 | |
| 	0x1a7a0063,	// phyr014
 | |
| 	0x20000000,	// phyr018
 | |
| 	0x20000000,	// phyr01c
 | |
| 	0x20000000,	// phyr020
 | |
| 	0x20000000,	// phyr024
 | |
| 	0x00000008,	// phyr028
 | |
| 	0x00000000,	// phyr02c
 | |
| 	0x00077600,	// phyr030
 | |
| 	0x00000000,	// phyr034
 | |
| 	0x00000000,	// phyr038
 | |
| 	0x20000000,	// phyr03c
 | |
| 	0x50506000,	// phyr040
 | |
| 	0x50505050,	// phyr044
 | |
| 	0x00002f07,	// phyr048
 | |
| 	0x00003080,	// phyr04c
 | |
| 	0x04000000,	// phyr050
 | |
| 	0x00000200,	// phyr054
 | |
| 	0x03140501,	// phyr058-rtt:40
 | |
| 	0x04800000,	// phyr05c
 | |
| 	0x0800044e,	// phyr060
 | |
| 	0x00000000,	// phyr064
 | |
| 	0x00180008,	// phyr068
 | |
| 	0x00e00400,	// phyr06c
 | |
| 	0x00140206,	// phyr070
 | |
| 	0x1d4c0000,	// phyr074
 | |
| 	0x493e0107,	// phyr078
 | |
| 	0x08060404,	// phyr07c
 | |
| 	0x90000a00,	// phyr080
 | |
| 	0x06420c30,	// phyr084
 | |
| 	0x00001002,	// phyr088
 | |
| 	0x05701016,	// phyr08c
 | |
| 	0x10000000,	// phyr090
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e019c,	// new address
 | |
| 	0x20202020,	// phyr09c
 | |
| 	0x20202020,	// phyr0a0
 | |
| 	0x00002020,	// phyr0a4
 | |
| 	0x00002020,	// phyr0a8
 | |
| 	0x00000001,	// phyr0ac
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e01cc,	// new address
 | |
| 	0x01010101,	// phyr0cc
 | |
| 	0x01010101,	// phyr0d0
 | |
| 	0x80808080,	// phyr0d4
 | |
| 	0x80808080,	// phyr0d8
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0288,	// new address
 | |
| 	0x80808080,	// phyr188
 | |
| 	0x80808080,	// phyr18c
 | |
| 	0x80808080,	// phyr190
 | |
| 	0x80808080,	// phyr194
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e02f8,	// new address
 | |
| 	0x90909090,	// phyr1f8
 | |
| 	0x88888888,	// phyr1fc
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0300,	// new address
 | |
| 	0x00000000,	// phyr200
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0194,	// new address
 | |
| 	0x801112e0,	// phyr094 - bit12=1,15=0,- write window is ok
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e019c,	// new address
 | |
| 	0x20202020,	// phyr09c
 | |
| 	0x20202020,	// phyr0a0
 | |
| 	0x00002020,	// phyr0a4
 | |
| 	0x80000000,	// phyr0a8
 | |
| 	0x00000001,	// phyr0ac
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0318,	// new address
 | |
| 	0x09222719,	// phyr218
 | |
| 	0x00aa4403,	// phyr21c
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0198,	// new address
 | |
| 	0x08060000,	// phyr098
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e01b0,	// new address
 | |
| 	0x00000000,	// phyr0b0
 | |
| 	0x00000000,	// phyr0b4
 | |
| 	0x00000000,	// phyr0b8
 | |
| 	0x00000000,	// phyr0bc
 | |
| 	0x00000000,	// phyr0c0 - ori
 | |
| 	0x00000000,	// phyr0c4
 | |
| 	0x000aff2c,	// phyr0c8
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e01dc,	// new address
 | |
| 	0x00080000,	// phyr0dc
 | |
| 	0x00000000,	// phyr0e0
 | |
| 	0xaa55aa55,	// phyr0e4
 | |
| 	0x55aa55aa,	// phyr0e8
 | |
| 	0xaaaa5555,	// phyr0ec
 | |
| 	0x5555aaaa,	// phyr0f0
 | |
| 	0xaa55aa55,	// phyr0f4
 | |
| 	0x55aa55aa,	// phyr0f8
 | |
| 	0xaaaa5555,	// phyr0fc
 | |
| 	0x5555aaaa,	// phyr100
 | |
| 	0xaa55aa55,	// phyr104
 | |
| 	0x55aa55aa,	// phyr108
 | |
| 	0xaaaa5555,	// phyr10c
 | |
| 	0x5555aaaa,	// phyr110
 | |
| 	0xaa55aa55,	// phyr114
 | |
| 	0x55aa55aa,	// phyr118
 | |
| 	0xaaaa5555,	// phyr11c
 | |
| 	0x5555aaaa,	// phyr120
 | |
| 	0x20202020,	// phyr124
 | |
| 	0x20202020,	// phyr128
 | |
| 	0x20202020,	// phyr12c
 | |
| 	0x20202020,	// phyr130
 | |
| 	0x20202020,	// phyr134
 | |
| 	0x20202020,	// phyr138
 | |
| 	0x20202020,	// phyr13c
 | |
| 	0x20202020,	// phyr140
 | |
| 	0x20202020,	// phyr144
 | |
| 	0x20202020,	// phyr148
 | |
| 	0x20202020,	// phyr14c
 | |
| 	0x20202020,	// phyr150
 | |
| 	0x20202020,	// phyr154
 | |
| 	0x20202020,	// phyr158
 | |
| 	0x20202020,	// phyr15c
 | |
| 	0x20202020,	// phyr160
 | |
| 	0x20202020,	// phyr164
 | |
| 	0x20202020,	// phyr168
 | |
| 	0x20202020,	// phyr16c
 | |
| 	0x20202020,	// phyr170
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0298,	// new address
 | |
| 	0x20200800,	// phyr198
 | |
| 	0x20202020,	// phyr19c
 | |
| 	0x20202020,	// phyr1a0
 | |
| 	0x20202020,	// phyr1a4
 | |
| 	0x20202020,	// phyr1a8
 | |
| 	0x20202020,	// phyr1ac
 | |
| 	0x20202020,	// phyr1b0
 | |
| 	0x20202020,	// phyr1b4
 | |
| 	0x20202020,	// phyr1b8
 | |
| 	0x20202020,	// phyr1bc
 | |
| 	0x20202020,	// phyr1c0
 | |
| 	0x20202020,	// phyr1c4
 | |
| 	0x20202020,	// phyr1c8
 | |
| 	0x20202020,	// phyr1cc
 | |
| 	0x20202020,	// phyr1d0
 | |
| 	0x20202020,	// phyr1d4
 | |
| 	0x20202020,	// phyr1d8
 | |
| 	0x20202020,	// phyr1dc
 | |
| 	0x20202020,	// phyr1e0
 | |
| 	0x20202020,	// phyr1e4
 | |
| 	0x00002020,	// phyr1e8
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e0304,	// new address
 | |
| 	0x00000800,	// phyr204
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e027c,	// new address
 | |
| 	0x4e400000,	// phyr17c
 | |
| 	0x59595959,	// phyr180
 | |
| 	0x40404040,	// phyr184
 | |
| 	0xaeeddeea,	// change address
 | |
| 	0x1e6e02f4,	// new address
 | |
| 	0x00000059,	// phyr1f4
 | |
| 	0xaeededed,	// end
 | |
| };
 | |
| #endif
 | |
| 
 | |
| /* MPLL configuration */
 | |
| #define SCU_MPLL_FREQ_400M	0x0008405F
 | |
| #define SCU_MPLL_EXT_400M	0x0000002F
 | |
| #define SCU_MPLL_FREQ_333M	0x00488299
 | |
| #define SCU_MPLL_EXT_333M	0x0000014C
 | |
| #define SCU_MPLL_FREQ_200M	0x0078007F
 | |
| #define SCU_MPLL_EXT_200M	0x0000003F
 | |
| #define SCU_MPLL_FREQ_100M	0x0078003F
 | |
| #define SCU_MPLL_EXT_100M	0x0000001F
 | |
| 
 | |
| #if defined(CONFIG_ASPEED_DDR4_1600)
 | |
| #define SCU_MPLL_FREQ_CFG	SCU_MPLL_FREQ_400M
 | |
| #define SCU_MPLL_EXT_CFG	SCU_MPLL_EXT_400M
 | |
| #elif defined(CONFIG_ASPEED_DDR4_1333)
 | |
| #define SCU_MPLL_FREQ_CFG	SCU_MPLL_FREQ_333M
 | |
| #define SCU_MPLL_EXT_CFG	SCU_MPLL_EXT_333M
 | |
| #elif defined(CONFIG_ASPEED_DDR4_800)
 | |
| #define SCU_MPLL_FREQ_CFG	SCU_MPLL_FREQ_200M
 | |
| #define SCU_MPLL_EXT_CFG	SCU_MPLL_EXT_200M
 | |
| #elif defined(CONFIG_ASPEED_DDR4_400)
 | |
| #define SCU_MPLL_FREQ_CFG	SCU_MPLL_FREQ_100M
 | |
| #define SCU_MPLL_EXT_CFG	SCU_MPLL_EXT_100M
 | |
| #else
 | |
| #error "undefined DDR4 target rate\n"
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * AC timing and SDRAM mode register setting
 | |
|  * for real chip are derived from the model GDDR4-1600
 | |
|  */
 | |
| #define DDR4_MR01_MODE	0x03010510
 | |
| #define DDR4_MR23_MODE	0x00000000
 | |
| #define DDR4_MR45_MODE	0x04000000
 | |
| #define DDR4_MR6_MODE	0x00000400
 | |
| #define DDR4_TRFC_1600	0x467299f1
 | |
| #define DDR4_TRFC_1333	0x3a5f80c9
 | |
| #define DDR4_TRFC_800	0x23394c78
 | |
| #define DDR4_TRFC_400	0x111c263c
 | |
| 
 | |
| #if defined(CONFIG_ASPEED_DDR4_1600)
 | |
| #define DDR4_TRFC		DDR4_TRFC_1600
 | |
| #define DDR4_PHY_TRAIN_TRFC	0xc30
 | |
| #elif defined(CONFIG_ASPEED_DDR4_1333)
 | |
| #define DDR4_TRFC		DDR4_TRFC_1333
 | |
| #define DDR4_PHY_TRAIN_TRFC	0xa25
 | |
| #elif defined(CONFIG_ASPEED_DDR4_800)
 | |
| #define DDR4_TRFC		DDR4_TRFC_800
 | |
| #define DDR4_PHY_TRAIN_TRFC	0x618
 | |
| #elif defined(CONFIG_ASPEED_DDR4_400)
 | |
| #define DDR4_TRFC		DDR4_TRFC_400
 | |
| #define DDR4_PHY_TRAIN_TRFC	0x30c
 | |
| #else
 | |
| #error "undefined tRFC setting"
 | |
| #endif
 | |
| 
 | |
| /* supported SDRAM size */
 | |
| #define SDRAM_SIZE_1KB		(1024U)
 | |
| #define SDRAM_SIZE_1MB		(SDRAM_SIZE_1KB * SDRAM_SIZE_1KB)
 | |
| #define SDRAM_MIN_SIZE		(256 * SDRAM_SIZE_1MB)
 | |
| #define SDRAM_MAX_SIZE		(2048 * SDRAM_SIZE_1MB)
 | |
| 
 | |
| DECLARE_GLOBAL_DATA_PTR;
 | |
| 
 | |
| static const u32 ddr4_ac_timing[4] = {
 | |
| 	0x040e0307, 0x0f4711f1, 0x0e060304, 0x00001240 };
 | |
| static const u32 ddr_max_grant_params[4] = {
 | |
| 	0x44444444, 0x44444444, 0x44444444, 0x44444444 };
 | |
| 
 | |
| struct dram_info {
 | |
| 	struct ram_info info;
 | |
| 	struct clk ddr_clk;
 | |
| 	struct ast2600_sdrammc_regs *regs;
 | |
| 	struct ast2600_scu *scu;
 | |
| 	struct ast2600_ddr_phy *phy;
 | |
| 	void __iomem *phy_setting;
 | |
| 	void __iomem *phy_status;
 | |
| 	ulong clock_rate;
 | |
| };
 | |
| 
 | |
| static void ast2600_sdramphy_kick_training(struct dram_info *info)
 | |
| {
 | |
| 	u32 data;
 | |
| 	struct ast2600_sdrammc_regs *regs = info->regs;
 | |
| 
 | |
| 	writel(SDRAM_PHYCTRL0_NRST, ®s->phy_ctrl[0]);
 | |
| 	udelay(5);
 | |
| 	writel(SDRAM_PHYCTRL0_NRST | SDRAM_PHYCTRL0_INIT, ®s->phy_ctrl[0]);
 | |
| 	udelay(1000);
 | |
| 
 | |
| 	while (1) {
 | |
| 		data = readl(®s->phy_ctrl[0]) & SDRAM_PHYCTRL0_INIT;
 | |
| 		if (~data)
 | |
| 			break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief	load DDR-PHY configurations table to the PHY registers
 | |
|  * @param[in]	p_tbl - pointer to the configuration table
 | |
|  * @param[in]	info - pointer to the DRAM info struct
 | |
|  *
 | |
|  * There are two sets of MRS (Mode Registers) configuration in ast2600 memory
 | |
|  * system: one is in the SDRAM MC (memory controller) which is used in run
 | |
|  * time, and the other is in the DDR-PHY IP which is used during DDR-PHY
 | |
|  * training.
 | |
|  */
 | |
| static void ast2600_sdramphy_init(u32 *p_tbl, struct dram_info *info)
 | |
| {
 | |
| 	u32 reg_base = (u32)info->phy_setting;
 | |
| 	u32 addr = p_tbl[0];
 | |
| 	u32 data;
 | |
| 	int i = 1;
 | |
| 
 | |
| 	writel(0, &info->regs->phy_ctrl[0]);
 | |
| 	udelay(10);
 | |
| 
 | |
| 	while (1) {
 | |
| 		if (addr < reg_base) {
 | |
| 			debug("invalid DDR-PHY addr: 0x%08x\n", addr);
 | |
| 			break;
 | |
| 		}
 | |
| 		data = p_tbl[i++];
 | |
| 
 | |
| 		if (data == DDR_PHY_TBL_END) {
 | |
| 			break;
 | |
| 		} else if (data == DDR_PHY_TBL_CHG_ADDR) {
 | |
| 			addr = p_tbl[i++];
 | |
| 		} else {
 | |
| 			writel(data, addr);
 | |
| 			addr += 4;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	data = readl(info->phy_setting + 0x84) & ~GENMASK(16, 0);
 | |
| 	data |= DDR4_PHY_TRAIN_TRFC;
 | |
| 	writel(data, info->phy_setting + 0x84);
 | |
| }
 | |
| 
 | |
| static int ast2600_sdramphy_check_status(struct dram_info *info)
 | |
| {
 | |
| 	u32 value, tmp;
 | |
| 	u32 reg_base = (u32)info->phy_status;
 | |
| 	int need_retrain = 0;
 | |
| 
 | |
| 	debug("\nSDRAM PHY training report:\n");
 | |
| 
 | |
| 	/* training status */
 | |
| 	value = readl(reg_base + 0x00);
 | |
| 	debug("rO_DDRPHY_reg offset 0x00 = 0x%08x\n", value);
 | |
| 
 | |
| 	if (value & BIT(3))
 | |
| 		debug("\tinitial PVT calibration fail\n");
 | |
| 
 | |
| 	if (value & BIT(5))
 | |
| 		debug("\truntime calibration fail\n");
 | |
| 
 | |
| 	/* PU & PD */
 | |
| 	value = readl(reg_base + 0x30);
 | |
| 	debug("rO_DDRPHY_reg offset 0x30 = 0x%08x\n", value);
 | |
| 	debug("  PU = 0x%02x\n", value & 0xff);
 | |
| 	debug("  PD = 0x%02x\n", (value >> 16) & 0xff);
 | |
| 
 | |
| 	/* read eye window */
 | |
| 	value = readl(reg_base + 0x68);
 | |
| 	if (0 == (value & GENMASK(7, 0)))
 | |
| 		need_retrain = 1;
 | |
| 
 | |
| 	debug("rO_DDRPHY_reg offset 0x68 = 0x%08x\n", value);
 | |
| 	debug("  rising edge of read data eye training pass window\n");
 | |
| 	tmp = (((value & GENMASK(7, 0)) >> 0) * 100) / 255;
 | |
| 	debug("    B0:%d%%\n", tmp);
 | |
| 	tmp = (((value & GENMASK(15, 8)) >> 8) * 100) / 255;
 | |
| 	debug("    B1:%d%%\n", tmp);
 | |
| 
 | |
| 	value = readl(reg_base + 0xC8);
 | |
| 	debug("rO_DDRPHY_reg offset 0xC8 = 0x%08x\n", value);
 | |
| 	debug("  falling edge of read data eye training pass window\n");
 | |
| 	tmp = (((value & GENMASK(7, 0)) >> 0) * 100) / 255;
 | |
| 	debug("    B0:%d%%\n", tmp);
 | |
| 	tmp = (((value & GENMASK(15, 8)) >> 8) * 100) / 255;
 | |
| 	debug("    B1:%d%%\n", tmp);
 | |
| 
 | |
| 	/* write eye window */
 | |
| 	value = readl(reg_base + 0x7c);
 | |
| 	if (0 == (value & GENMASK(7, 0)))
 | |
| 		need_retrain = 1;
 | |
| 
 | |
| 	debug("rO_DDRPHY_reg offset 0x7C = 0x%08x\n", value);
 | |
| 	debug("  rising edge of write data eye training pass window\n");
 | |
| 	tmp = (((value & GENMASK(7, 0)) >> 0) * 100) / 255;
 | |
| 	debug("    B0:%d%%\n", tmp);
 | |
| 	tmp = (((value & GENMASK(15, 8)) >> 8) * 100) / 255;
 | |
| 	debug("    B1:%d%%\n", tmp);
 | |
| 
 | |
| 	/* read Vref training result */
 | |
| 	value = readl(reg_base + 0x88);
 | |
| 	debug("rO_DDRPHY_reg offset 0x88 = 0x%08x\n", value);
 | |
| 	debug("  read Vref training result\n");
 | |
| 	tmp = (((value & GENMASK(7, 0)) >> 0) * 100) / 127;
 | |
| 	debug("    B0:%d%%\n", tmp);
 | |
| 	tmp = (((value & GENMASK(15, 8)) >> 8) * 100) / 127;
 | |
| 	debug("    B1:%d%%\n", tmp);
 | |
| 
 | |
| 	/* write Vref training result */
 | |
| 	value = readl(reg_base + 0x90);
 | |
| 	debug("rO_DDRPHY_reg offset 0x90 = 0x%08x\n", value);
 | |
| 
 | |
| 	/* gate train */
 | |
| 	value = readl(reg_base + 0x50);
 | |
| 	if ((0 == (value & GENMASK(15, 0))) ||
 | |
| 	    (0 == (value & GENMASK(31, 16)))) {
 | |
| 		need_retrain = 1;
 | |
| 	}
 | |
| 
 | |
| 	debug("rO_DDRPHY_reg offset 0x50 = 0x%08x\n", value);
 | |
| 
 | |
| 	return need_retrain;
 | |
| }
 | |
| 
 | |
| #ifndef CONFIG_ASPEED_BYPASS_SELFTEST
 | |
| #define MC_TEST_PATTERN_N 8
 | |
| static u32 as2600_sdrammc_test_pattern[MC_TEST_PATTERN_N] = {
 | |
| 	0xcc33cc33, 0xff00ff00, 0xaa55aa55, 0x88778877,
 | |
| 	0x92cc4d6e, 0x543d3cde, 0xf1e843c7, 0x7c61d253 };
 | |
| 
 | |
| #define TIMEOUT_DRAM	5000000
 | |
| int ast2600_sdrammc_dg_test(struct dram_info *info, unsigned int datagen, u32 mode)
 | |
| {
 | |
| 	unsigned int data;
 | |
| 	unsigned int timeout = 0;
 | |
| 	struct ast2600_sdrammc_regs *regs = info->regs;
 | |
| 
 | |
| 	writel(0, ®s->ecc_test_ctrl);
 | |
| 
 | |
| 	if (mode == 0)
 | |
| 		writel(0x00000085 | (datagen << 3), ®s->ecc_test_ctrl);
 | |
| 	else
 | |
| 		writel(0x000000C1 | (datagen << 3), ®s->ecc_test_ctrl);
 | |
| 
 | |
| 	do {
 | |
| 		data = readl(®s->ecc_test_ctrl) & GENMASK(13, 12);
 | |
| 
 | |
| 		if (data & BIT(13))
 | |
| 			return 0;
 | |
| 
 | |
| 		if (++timeout > TIMEOUT_DRAM) {
 | |
| 			debug("Timeout!!\n");
 | |
| 			writel(0, ®s->ecc_test_ctrl);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	} while (!data);
 | |
| 
 | |
| 	writel(0, ®s->ecc_test_ctrl);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ast2600_sdrammc_cbr_test(struct dram_info *info)
 | |
| {
 | |
| 	u32 i;
 | |
| 	struct ast2600_sdrammc_regs *regs = info->regs;
 | |
| 
 | |
| 	clrsetbits_le32(®s->test_addr, GENMASK(30, 4), 0x7ffff0);
 | |
| 
 | |
| 	/* single */
 | |
| 	for (i = 0; i < 8; i++)
 | |
| 		if (ast2600_sdrammc_dg_test(info, i, 0))
 | |
| 			return -1;
 | |
| 
 | |
| 	/* burst */
 | |
| 	for (i = 0; i < 8; i++)
 | |
| 		if (ast2600_sdrammc_dg_test(info, i, i))
 | |
| 			return -1;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ast2600_sdrammc_test(struct dram_info *info)
 | |
| {
 | |
| 	struct ast2600_sdrammc_regs *regs = info->regs;
 | |
| 
 | |
| 	u32 pass_cnt = 0;
 | |
| 	u32 fail_cnt = 0;
 | |
| 	u32 target_cnt = 2;
 | |
| 	u32 test_cnt = 0;
 | |
| 	u32 pattern;
 | |
| 	u32 i = 0;
 | |
| 	bool finish = false;
 | |
| 
 | |
| 	debug("sdram mc test:\n");
 | |
| 	while (!finish) {
 | |
| 		pattern = as2600_sdrammc_test_pattern[i++];
 | |
| 		i = i % MC_TEST_PATTERN_N;
 | |
| 		debug("  pattern = %08X : ", pattern);
 | |
| 		writel(pattern, ®s->test_init_val);
 | |
| 
 | |
| 		if (ast2600_sdrammc_cbr_test(info)) {
 | |
| 			debug("fail\n");
 | |
| 			fail_cnt++;
 | |
| 		} else {
 | |
| 			debug("pass\n");
 | |
| 			pass_cnt++;
 | |
| 		}
 | |
| 
 | |
| 		if (++test_cnt == target_cnt)
 | |
| 			finish = true;
 | |
| 	}
 | |
| 	debug("statistics: pass/fail/total:%d/%d/%d\n", pass_cnt, fail_cnt,
 | |
| 	      target_cnt);
 | |
| 
 | |
| 	return fail_cnt;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * scu500[14:13]
 | |
|  *	2b'00: VGA memory size = 16MB
 | |
|  *	2b'01: VGA memory size = 16MB
 | |
|  *	2b'10: VGA memory size = 32MB
 | |
|  *	2b'11: VGA memory size = 64MB
 | |
|  *
 | |
|  * mcr04[3:2]
 | |
|  *	2b'00: VGA memory size = 8MB
 | |
|  *	2b'01: VGA memory size = 16MB
 | |
|  *	2b'10: VGA memory size = 32MB
 | |
|  *	2b'11: VGA memory size = 64MB
 | |
|  */
 | |
| static size_t ast2600_sdrammc_get_vga_mem_size(struct dram_info *info)
 | |
| {
 | |
| 	u32 vga_hwconf;
 | |
| 	size_t vga_mem_size_base = 8 * 1024 * 1024;
 | |
| 
 | |
| 	vga_hwconf =
 | |
| 		(readl(&info->scu->hwstrap1) & SCU_HWSTRAP1_VGA_MEM_MASK) >>
 | |
| 		 SCU_HWSTRAP1_VGA_MEM_SHIFT;
 | |
| 
 | |
| 	if (vga_hwconf == 0) {
 | |
| 		vga_hwconf = 1;
 | |
| 		writel(vga_hwconf << SCU_HWSTRAP1_VGA_MEM_SHIFT,
 | |
| 		       &info->scu->hwstrap1);
 | |
| 	}
 | |
| 
 | |
| 	clrsetbits_le32(&info->regs->config, SDRAM_CONF_VGA_SIZE_MASK,
 | |
| 			((vga_hwconf << SDRAM_CONF_VGA_SIZE_SHIFT) &
 | |
| 			 SDRAM_CONF_VGA_SIZE_MASK));
 | |
| 
 | |
| 	/* no need to reserve VGA memory if efuse[VGA disable] is set */
 | |
| 	if (readl(&info->scu->efuse) & SCU_EFUSE_DIS_VGA)
 | |
| 		return 0;
 | |
| 
 | |
| 	return vga_mem_size_base << vga_hwconf;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Find out RAM size and save it in dram_info
 | |
|  *
 | |
|  * The procedure is taken from Aspeed SDK
 | |
|  */
 | |
| static void ast2600_sdrammc_calc_size(struct dram_info *info)
 | |
| {
 | |
| 	/* The controller supports 256/512/1024/2048 MB ram */
 | |
| 	size_t ram_size = SDRAM_MIN_SIZE;
 | |
| 	const int write_test_offset = 0x100000;
 | |
| 	u32 test_pattern = 0xdeadbeef;
 | |
| 	u32 cap_param = SDRAM_CONF_CAP_2048M;
 | |
| 	u32 refresh_timing_param = DDR4_TRFC;
 | |
| 	const u32 write_addr_base = CONFIG_SYS_SDRAM_BASE + write_test_offset;
 | |
| 
 | |
| 	for (ram_size = SDRAM_MAX_SIZE; ram_size > SDRAM_MIN_SIZE;
 | |
| 	     ram_size >>= 1) {
 | |
| 		writel(test_pattern, write_addr_base + (ram_size >> 1));
 | |
| 		test_pattern = (test_pattern >> 4) | (test_pattern << 28);
 | |
| 	}
 | |
| 
 | |
| 	/* One last write to overwrite all wrapped values */
 | |
| 	writel(test_pattern, write_addr_base);
 | |
| 
 | |
| 	/* Reset the pattern and see which value was really written */
 | |
| 	test_pattern = 0xdeadbeef;
 | |
| 	for (ram_size = SDRAM_MAX_SIZE; ram_size > SDRAM_MIN_SIZE;
 | |
| 	     ram_size >>= 1) {
 | |
| 		if (readl(write_addr_base + (ram_size >> 1)) == test_pattern)
 | |
| 			break;
 | |
| 
 | |
| 		--cap_param;
 | |
| 		refresh_timing_param >>= 8;
 | |
| 		test_pattern = (test_pattern >> 4) | (test_pattern << 28);
 | |
| 	}
 | |
| 
 | |
| 	clrsetbits_le32(&info->regs->ac_timing[1],
 | |
| 			(SDRAM_AC_TRFC_MASK << SDRAM_AC_TRFC_SHIFT),
 | |
| 			((refresh_timing_param & SDRAM_AC_TRFC_MASK)
 | |
| 			 << SDRAM_AC_TRFC_SHIFT));
 | |
| 
 | |
| 	info->info.base = CONFIG_SYS_SDRAM_BASE;
 | |
| 	info->info.size = ram_size - ast2600_sdrammc_get_vga_mem_size(info);
 | |
| 
 | |
| 	clrsetbits_le32(&info->regs->config, SDRAM_CONF_CAP_MASK,
 | |
| 			((cap_param << SDRAM_CONF_CAP_SHIFT) & SDRAM_CONF_CAP_MASK));
 | |
| }
 | |
| 
 | |
| static int ast2600_sdrammc_init_ddr4(struct dram_info *info)
 | |
| {
 | |
| 	const u32 power_ctrl = MCR34_CKE_EN | MCR34_AUTOPWRDN_EN |
 | |
| 		MCR34_MREQ_BYPASS_DIS | MCR34_RESETN_DIS |
 | |
| 		MCR34_ODT_EN | MCR34_ODT_AUTO_ON |
 | |
| 		(0x1 << MCR34_ODT_EXT_SHIFT);
 | |
| 
 | |
| 	/* init SDRAM-PHY only on real chip */
 | |
| 	ast2600_sdramphy_init(ast2600_sdramphy_config, info);
 | |
| 	writel((MCR34_CKE_EN | MCR34_MREQI_DIS | MCR34_RESETN_DIS),
 | |
| 	       &info->regs->power_ctrl);
 | |
| 	udelay(5);
 | |
| 	ast2600_sdramphy_kick_training(info);
 | |
| 	udelay(500);
 | |
| 	writel(SDRAM_RESET_DLL_ZQCL_EN, &info->regs->refresh_timing);
 | |
| 
 | |
| 	writel(MCR30_SET_MR(3), &info->regs->mode_setting_control);
 | |
| 	writel(MCR30_SET_MR(6), &info->regs->mode_setting_control);
 | |
| 	writel(MCR30_SET_MR(5), &info->regs->mode_setting_control);
 | |
| 	writel(MCR30_SET_MR(4), &info->regs->mode_setting_control);
 | |
| 	writel(MCR30_SET_MR(2), &info->regs->mode_setting_control);
 | |
| 	writel(MCR30_SET_MR(1), &info->regs->mode_setting_control);
 | |
| 	writel(MCR30_SET_MR(0) | MCR30_RESET_DLL_DELAY_EN,
 | |
| 	       &info->regs->mode_setting_control);
 | |
| 
 | |
| 	writel(SDRAM_REFRESH_EN | SDRAM_RESET_DLL_ZQCL_EN |
 | |
| 	       (0x5f << SDRAM_REFRESH_PERIOD_SHIFT),
 | |
| 	       &info->regs->refresh_timing);
 | |
| 
 | |
| 	/* wait self-refresh idle */
 | |
| 	while (readl(&info->regs->power_ctrl) &
 | |
| 	       MCR34_SELF_REFRESH_STATUS_MASK)
 | |
| 		;
 | |
| 
 | |
| 	writel(SDRAM_REFRESH_EN | SDRAM_LOW_PRI_REFRESH_EN |
 | |
| 	       SDRAM_REFRESH_ZQCS_EN |
 | |
| 	       (0x5f << SDRAM_REFRESH_PERIOD_SHIFT) |
 | |
| 	       (0x42aa << SDRAM_REFRESH_PERIOD_ZQCS_SHIFT),
 | |
| 	       &info->regs->refresh_timing);
 | |
| 
 | |
| 	writel(power_ctrl, &info->regs->power_ctrl);
 | |
| 	udelay(500);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void ast2600_sdrammc_unlock(struct dram_info *info)
 | |
| {
 | |
| 	writel(SDRAM_UNLOCK_KEY, &info->regs->protection_key);
 | |
| 	while (!readl(&info->regs->protection_key))
 | |
| 		;
 | |
| }
 | |
| 
 | |
| static void ast2600_sdrammc_lock(struct dram_info *info)
 | |
| {
 | |
| 	writel(~SDRAM_UNLOCK_KEY, &info->regs->protection_key);
 | |
| 	while (readl(&info->regs->protection_key))
 | |
| 		;
 | |
| }
 | |
| 
 | |
| static void ast2600_sdrammc_common_init(struct ast2600_sdrammc_regs *regs)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	writel(MCR34_MREQI_DIS | MCR34_RESETN_DIS, ®s->power_ctrl);
 | |
| 	writel(SDRAM_VIDEO_UNLOCK_KEY, ®s->gm_protection_key);
 | |
| 	writel(0x10 << MCR38_RW_MAX_GRANT_CNT_RQ_SHIFT,
 | |
| 	       ®s->arbitration_ctrl);
 | |
| 	writel(0xFFBBFFF4, ®s->req_limit_mask);
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(ddr_max_grant_params); ++i)
 | |
| 		writel(ddr_max_grant_params[i], ®s->max_grant_len[i]);
 | |
| 
 | |
| 	writel(MCR50_RESET_ALL_INTR, ®s->intr_ctrl);
 | |
| 
 | |
| 	writel(0x07FFFFFF, ®s->ecc_range_ctrl);
 | |
| 
 | |
| 	writel(0, ®s->ecc_test_ctrl);
 | |
| 	writel(0x80000001, ®s->test_addr);
 | |
| 	writel(0, ®s->test_fail_dq_bit);
 | |
| 	writel(0, ®s->test_init_val);
 | |
| 
 | |
| 	writel(0xFFFFFFFF, ®s->req_input_ctrl);
 | |
| 	writel(0, ®s->req_high_pri_ctrl);
 | |
| 
 | |
| 	udelay(600);
 | |
| 
 | |
| #ifdef CONFIG_ASPEED_DDR4_DUALX8
 | |
| 	writel(0x37, ®s->config);
 | |
| #else
 | |
| 	writel(0x17, ®s->config);
 | |
| #endif
 | |
| 
 | |
| 	/* load controller setting */
 | |
| 	for (i = 0; i < ARRAY_SIZE(ddr4_ac_timing); ++i)
 | |
| 		writel(ddr4_ac_timing[i], ®s->ac_timing[i]);
 | |
| 
 | |
| 	writel(DDR4_MR01_MODE, ®s->mr01_mode_setting);
 | |
| 	writel(DDR4_MR23_MODE, ®s->mr23_mode_setting);
 | |
| 	writel(DDR4_MR45_MODE, ®s->mr45_mode_setting);
 | |
| 	writel(DDR4_MR6_MODE, ®s->mr6_mode_setting);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Update size info according to the ECC HW setting
 | |
|  *
 | |
|  * Assume SDRAM has been initialized by SPL or the host.  To get the RAM size, we
 | |
|  * don't need to calculate the ECC size again but read from MCR04 and derive the
 | |
|  * size from its value.
 | |
|  */
 | |
| static void ast2600_sdrammc_update_size(struct dram_info *info)
 | |
| {
 | |
| 	struct ast2600_sdrammc_regs *regs = info->regs;
 | |
| 	u32 conf = readl(®s->config);
 | |
| 	u32 cap_param;
 | |
| 	size_t ram_size = SDRAM_MAX_SIZE;
 | |
| 	size_t hw_size;
 | |
| 
 | |
| 	cap_param = (conf & SDRAM_CONF_CAP_MASK) >> SDRAM_CONF_CAP_SHIFT;
 | |
| 	switch (cap_param) {
 | |
| 	case SDRAM_CONF_CAP_2048M:
 | |
| 		ram_size = 2048 * SDRAM_SIZE_1MB;
 | |
| 		break;
 | |
| 	case SDRAM_CONF_CAP_1024M:
 | |
| 		ram_size = 1024 * SDRAM_SIZE_1MB;
 | |
| 		break;
 | |
| 	case SDRAM_CONF_CAP_512M:
 | |
| 		ram_size = 512 * SDRAM_SIZE_1MB;
 | |
| 		break;
 | |
| 	case SDRAM_CONF_CAP_256M:
 | |
| 		ram_size = 256 * SDRAM_SIZE_1MB;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	info->info.base = CONFIG_SYS_SDRAM_BASE;
 | |
| 	info->info.size = ram_size - ast2600_sdrammc_get_vga_mem_size(info);
 | |
| 
 | |
| 	if (0 == (conf & SDRAM_CONF_ECC_SETUP))
 | |
| 		return;
 | |
| 
 | |
| 	hw_size = readl(®s->ecc_range_ctrl) & SDRAM_ECC_RANGE_ADDR_MASK;
 | |
| 	hw_size += (1 << SDRAM_ECC_RANGE_ADDR_SHIFT);
 | |
| 
 | |
| 	info->info.size = hw_size;
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_ASPEED_ECC
 | |
| static void ast2600_sdrammc_ecc_enable(struct dram_info *info)
 | |
| {
 | |
| 	struct ast2600_sdrammc_regs *regs = info->regs;
 | |
| 	size_t conf_size;
 | |
| 	u32 reg;
 | |
| 
 | |
| 	conf_size = CONFIG_ASPEED_ECC_SIZE * SDRAM_SIZE_1MB;
 | |
| 	if (conf_size > info->info.size) {
 | |
| 		printf("warning: ECC configured %dMB but actual size is %dMB\n",
 | |
| 		       CONFIG_ASPEED_ECC_SIZE,
 | |
| 		       info->info.size / SDRAM_SIZE_1MB);
 | |
| 		conf_size = info->info.size;
 | |
| 	} else if (conf_size == 0) {
 | |
| 		conf_size = info->info.size;
 | |
| 	}
 | |
| 
 | |
| 	info->info.size = (((conf_size / 9) * 8) >> 20) << 20;
 | |
| 	writel(((info->info.size >> 20) - 1) << 20, ®s->ecc_range_ctrl);
 | |
| 	reg = readl(®s->config) | SDRAM_CONF_ECC_SETUP;
 | |
| 	writel(reg, ®s->config);
 | |
| 
 | |
| 	writel(0, ®s->test_init_val);
 | |
| 	writel(0x80000001, ®s->test_addr);
 | |
| 	writel(0x221, ®s->ecc_test_ctrl);
 | |
| 	while (0 == (readl(®s->ecc_test_ctrl) & BIT(12)))
 | |
| 		;
 | |
| 	writel(0, ®s->ecc_test_ctrl);
 | |
| 	writel(BIT(31), ®s->intr_ctrl);
 | |
| 	writel(0, ®s->intr_ctrl);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int ast2600_sdrammc_probe(struct udevice *dev)
 | |
| {
 | |
| 	int ret;
 | |
| 	u32 reg;
 | |
| 	struct dram_info *priv = (struct dram_info *)dev_get_priv(dev);
 | |
| 	struct ast2600_sdrammc_regs *regs = priv->regs;
 | |
| 	struct udevice *clk_dev;
 | |
| 
 | |
| 	/* find SCU base address from clock device */
 | |
| 	ret = uclass_get_device_by_driver(UCLASS_CLK,
 | |
| 					  DM_DRIVER_GET(aspeed_ast2600_scu), &clk_dev);
 | |
| 	if (ret) {
 | |
| 		debug("clock device not defined\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	priv->scu = devfdt_get_addr_ptr(clk_dev);
 | |
| 	if (IS_ERR(priv->scu)) {
 | |
| 		debug("%s(): can't get SCU\n", __func__);
 | |
| 		return PTR_ERR(priv->scu);
 | |
| 	}
 | |
| 
 | |
| 	if (readl(&priv->scu->dram_hdshk) & SCU_DRAM_HDSHK_RDY) {
 | |
| 		printf("already initialized, ");
 | |
| 		ast2600_sdrammc_update_size(priv);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	reg = readl(&priv->scu->mpll);
 | |
| 	reg &= ~(SCU_PLL_BYPASS | SCU_PLL_DIV_MASK |
 | |
| 		 SCU_PLL_DENUM_MASK | SCU_PLL_NUM_MASK);
 | |
| 	reg |= (SCU_PLL_RST | SCU_PLL_OFF | SCU_MPLL_FREQ_CFG);
 | |
| 	writel(reg, &priv->scu->mpll);
 | |
| 	writel(SCU_MPLL_EXT_CFG, &priv->scu->mpll_ext);
 | |
| 	udelay(100);
 | |
| 	reg &= ~(SCU_PLL_RST | SCU_PLL_OFF);
 | |
| 	writel(reg, &priv->scu->mpll);
 | |
| 
 | |
| 	while ((readl(&priv->scu->mpll_ext) & BIT(31)) == 0)
 | |
| 		;
 | |
| 
 | |
| 	ast2600_sdrammc_unlock(priv);
 | |
| 	ast2600_sdrammc_common_init(regs);
 | |
| L_ast2600_sdramphy_train:
 | |
| 	ast2600_sdrammc_init_ddr4(priv);
 | |
| 
 | |
| 	/* make sure DDR-PHY is ready before access */
 | |
| 	do {
 | |
| 		reg = readl(priv->phy_status) & BIT(1);
 | |
| 	} while (reg == 0);
 | |
| 
 | |
| 	if (ast2600_sdramphy_check_status(priv) != 0) {
 | |
| 		printf("DDR4 PHY training fail, retrain\n");
 | |
| 		goto L_ast2600_sdramphy_train;
 | |
| 	}
 | |
| 
 | |
| 	ast2600_sdrammc_calc_size(priv);
 | |
| 
 | |
| #ifndef CONFIG_ASPEED_BYPASS_SELFTEST
 | |
| 	if (ast2600_sdrammc_test(priv) != 0) {
 | |
| 		printf("%s: DDR4 init fail\n", __func__);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_ASPEED_ECC
 | |
| 	ast2600_sdrammc_ecc_enable(priv);
 | |
| #endif
 | |
| 
 | |
| 	writel(readl(&priv->scu->dram_hdshk) | SCU_DRAM_HDSHK_RDY,
 | |
| 	       &priv->scu->dram_hdshk);
 | |
| 
 | |
| 	clrbits_le32(®s->intr_ctrl, MCR50_RESET_ALL_INTR);
 | |
| 	ast2600_sdrammc_lock(priv);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ast2600_sdrammc_of_to_plat(struct udevice *dev)
 | |
| {
 | |
| 	struct dram_info *priv = dev_get_priv(dev);
 | |
| 
 | |
| 	priv->regs = (void *)(uintptr_t)devfdt_get_addr_index(dev, 0);
 | |
| 	priv->phy_setting = (void *)(uintptr_t)devfdt_get_addr_index(dev, 1);
 | |
| 	priv->phy_status = (void *)(uintptr_t)devfdt_get_addr_index(dev, 2);
 | |
| 
 | |
| 	priv->clock_rate = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
 | |
| 					  "clock-frequency", 0);
 | |
| 	if (!priv->clock_rate) {
 | |
| 		debug("DDR Clock Rate not defined\n");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ast2600_sdrammc_get_info(struct udevice *dev, struct ram_info *info)
 | |
| {
 | |
| 	struct dram_info *priv = dev_get_priv(dev);
 | |
| 
 | |
| 	*info = priv->info;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct ram_ops ast2600_sdrammc_ops = {
 | |
| 	.get_info = ast2600_sdrammc_get_info,
 | |
| };
 | |
| 
 | |
| static const struct udevice_id ast2600_sdrammc_ids[] = {
 | |
| 	{ .compatible = "aspeed,ast2600-sdrammc" },
 | |
| 	{ }
 | |
| };
 | |
| 
 | |
| U_BOOT_DRIVER(sdrammc_ast2600) = {
 | |
| 	.name = "aspeed_ast2600_sdrammc",
 | |
| 	.id = UCLASS_RAM,
 | |
| 	.of_match = ast2600_sdrammc_ids,
 | |
| 	.ops = &ast2600_sdrammc_ops,
 | |
| 	.of_to_plat = ast2600_sdrammc_of_to_plat,
 | |
| 	.probe = ast2600_sdrammc_probe,
 | |
| 	.priv_auto = sizeof(struct dram_info),
 | |
| };
 |