u-boot2020.04移植(3、lowlevel_init.S)

現在開始就進入板級相關的初始化了,跳轉到board/samsung/goni/lowlevel_init.S文件,注意這裏lowlevel_init搜索出來在arch/arm/cpu/armv7/lowlevel_init.S文件裏面也有,而且這個文件還被編譯了,但仔細看一下就會發現,這個文件裏面的lowlevel_init被聲明成了弱屬性,所以真正調用的是board/samsung/goni/lowlevel_init.S文件。

ENTRY(cpu_init_crit)
	/*
	 * Jump to board specific initialization...
	 * The Mask ROM will have already initialized
	 * basic memory. Go here to bump up clock rate and handle
	 * wake up conditions.
	 */
	b	lowlevel_init		@ go setup pll,mux,memory
ENDPROC(cpu_init_crit)

其實在lowlevel_init裏面也不需要幹多少事,但DDR是必須要初始化好的,我們都知道s5pv210的整個IRAM也就96K,而u-boot的大小遠遠超過了96K,所以比較好的辦法就是在BL1階段就初始化好DDR,並且將u-boot搬運到DDR運行,但是BL1的大小最大隻有16K,所以得儘早將DDR初始化好,然後到DDR中運行,這一步本來也是必須的,因爲現在u-boot運行在IRAM內,其運行地址與鏈接地址是不一樣的,只能運行位置無關代碼。

下面看下lowlevel_init得源碼:

board/samsung/goni/lowlevel_init.S

.globl lowlevel_init
lowlevel_init:
	/*這裏將lr得值保存到r11中,以便之後返回,因爲後面也有跳轉得過程,防止lr跳轉後丟失*/
	mov	r11, lr

	/* r5 has always zero */
	mov	r5, #0

	ldr	r7, =S5PC100_GPIO_BASE
	ldr	r8, =S5PC100_GPIO_BASE
	/* Read CPU ID */
	ldr	r2, =S5PC110_PRO_ID
	ldr	r0, [r2]
	mov	r1, #0x00010000
	and	r0, r0, r1
	/*根據手冊可以得出,這裏是不等的*/
	cmp	r0, r5
	beq	100f
	/*這裏將r8重新賦值成了C110的GPIO基地址*/
	ldr	r8, =S5PC110_GPIO_BASE
100:
	/* Turn on KEY_LED_ON [GPJ4(1)] XMSMWEN */
	/*比較結果不相等,這裏比較後,Z位的值是一直保持的*/
	cmp	r7, r8
	/*不相等,不執行*/
	beq	skip_check_didle			@ Support C110 only

    /*讀取復位狀態*/
	ldr	r0, =S5PC110_RST_STAT
	ldr	r1, [r0]
	and	r1, r1, #0x000D0000
	cmp	r1, #(0x1 << 19)			@ DEEPIDLE_WAKEUP
	/*如果是從DEEPIDLE模式喚醒的就直接跳到didle_wakeup過程*/
	beq	didle_wakeup
	cmp	r7, r8

skip_check_didle:
	/*下面這一堆代碼就是點亮了一顆led燈,我們改成自己板子上對應的燈GPJ0_3*/
	addeq	r0, r8, #0x280				@ S5PC100_GPIO_J4
	addne	r0, r8, #0x240				@ S5PC110_GPIO_J0
	ldr	r1, [r0, #0x0]				@ GPIO_CON_OFFSET
	bic	r1, r1, #(0xf << 12)			@ 3 * 4-bit
	orr	r1, r1, #(0x1 << 12)
	str	r1, [r0, #0x0]				@ GPIO_CON_OFFSET

	ldr	r1, [r0, #0x4]				@ GPIO_DAT_OFFSET
	bic	r1, r1, #(1 << 3)
	str	r1, [r0, #0x4]				@ GPIO_DAT_OFFSET

	/*執行到這裏,上面點的那顆led燈,如果亮了的話那就說明前面暫時沒什麼問題了*/
	/* Don't setup at s5pc100 */
	beq	100f

	/*下面這堆都是寫的默認值,不知道具體的作用*/
	/*
	 省略
    */

	/*
	 * Diable ABB block to reduce sleep current at low temperature
	 * Note that it's hidden register setup don't modify it
	 */
	 /*這裏操作的是隱藏寄存器,不要動*/
	ldr	r0, =0xE010C300
	ldr	r1, =0x00800000
	str	r1, [r0]

100:
	/* IO retension release */
	/*這一堆好像與低功耗有關*/
	ldreq	r0, =S5PC100_OTHERS			@ 0xE0108200
	ldrne	r0, =S5PC110_OTHERS			@ 0xE010E000
	ldr	r1, [r0]
	ldreq	r2, =(1 << 31)				@ IO_RET_REL
	ldrne	r2, =((1 << 31) | (1 << 30) | (1 << 29) | (1 << 28))
	orr	r1, r1, r2
	/* Do not release retention here for S5PC110 */
	streq	r1, [r0]

	/* Disable Watchdog */
	ldreq	r0, =S5PC100_WATCHDOG_BASE		@ 0xEA200000
	ldrne	r0, =S5PC110_WATCHDOG_BASE		@ 0xE2700000
	str	r5, [r0]

	/* setting SRAM */
	ldreq	r0, =S5PC100_SROMC_BASE
	ldrne	r0, =S5PC110_SROMC_BASE
	ldr	r1, =0x9
	str	r1, [r0]

	/* S5PC100 has 3 groups of interrupt sources */
	/*s5pv210有四組中斷源,這裏只失能了三組,再添加一組*/
	ldreq	r0, =S5PC100_VIC0_BASE			@ 0xE4000000
	ldrne	r0, =S5PC110_VIC0_BASE			@ 0xF2000000
	add	r1, r0, #0x00100000
	add	r2, r0, #0x00200000
	/*r4還沒被用過,使用r4*/
	add	r4, r0, #0x00300000

	/* Disable all interrupts (VIC0, VIC1 and VIC2, VIC3) */
	mvn	r3, #0x0
	str	r3, [r0, #0x14]				@ INTENCLEAR
	str	r3, [r1, #0x14]				@ INTENCLEAR
	str	r3, [r2, #0x14]				@ INTENCLEAR
	/*增加VIC3*/
	str	r3, [r4, #0x14]				@ INTENCLEAR

	/* Set all interrupts as IRQ */
	str	r5, [r0, #0xc]				@ INTSELECT
	str	r5, [r1, #0xc]				@ INTSELECT
	str	r5, [r2, #0xc]				@ INTSELECT
	/*增加VIC3*/
	str	r5, [r4, #0xc]				@ INTSELECT

	/* Pending Interrupt Clear */
	str	r5, [r0, #0xf00]			@ INTADDRESS
	str	r5, [r1, #0xf00]			@ INTADDRESS
	str	r5, [r2, #0xf00]			@ INTADDRESS
	/*增加VIC3*/
	str	r5, [r4, #0xf00]			@ INTADDRESS

	/* for UART */
	/*由於IROM代碼已經初始化了串口2,而在u-boot裏面我也使用的串口2作爲調試串口,
	所以可以選擇不初始化,但是後面重新配置了時鐘,IROM中的配置在新的時鐘下工作不正常,沒有輸出,
	主要應該就是波特率相關寄存器的設置沒法用了,所以再重新配置一下(參考後面的修改)*/
	bl	uart_asm_init

	bl	internal_ram_init

	cmp	r7, r8
	/* Clear wakeup status register */
	ldreq	r0, =S5PC100_WAKEUP_STAT
	ldrne	r0, =S5PC110_WAKEUP_STAT
	/*這裏是不是感覺什麼都沒做,但實際上手冊說的是寫1清除該位,默認狀態爲0,
	那猜測有效狀態應該是1,將讀出來的值1又寫回去就清除了該位*/
	ldr	r1, [r0]
	str	r1, [r0]

	/* IO retension release */
	/*是不是很熟悉,前面也有一段一模一樣的代碼,不知道意欲何爲*/
	/*
    省略
    */

	/*向下找標號1*/
	b	1f

/*如果前面檢測出是DEEPIDLE模式喚醒的,那麼直接就到這兒了*/
didle_wakeup:
	/* Wait when APLL is locked */
	ldr	r0, =0xE0100100			@ S5PC110_APLL_CON
lockloop:
    /*等待APLL時鐘穩定*/
	ldr	r1, [r0]
	and	r1, r1, #(1 << 29)
	cmp	r1, #(1 << 29)
	bne	lockloop

    /*S5PC110_INFORM0是一個用戶自定義寄存器*/
	ldr	r0, =S5PC110_INFORM0
	ldr	r1, [r0]
    /*這裏將r1的值放到pc指針裏面了,猜測是某個地方進入DEEPIDLE的時候,
    將lr的值保存到了S5PC110_INFORM0裏面,這裏直接讀出來後返回之前執行的地方*/
	mov	pc, r1
	nop
	nop
	nop
	nop
	nop

1:
	/*到了這裏就直接返回了*/
	mov	lr, r11
	mov	pc, lr

從源碼可以看出,目前lowlevel_init主要做的事:

  • 檢測喚醒條件
  • 點了一顆燈顯示當前運行到哪兒了
  • Initialize Async Register

  • IO retension release相關的

  • 關看門狗
  • 設置SRAM
  • 關中斷(這裏中斷和之前arm內核部分的中斷不一樣,這些中斷是三星公司設計的)
  • 初始化了四個串口的引腳
  • 初始化內部RAM
  • 清除喚醒狀態
  • IO retension release相關的

目前CPU好像是運行在400MHz下的,在本文件lowlevel_init.S的末尾有一個時鐘初始化的過程,但是沒有被調用,將其添加上:

   /*添加到這句後面*/
bl	internal_ram_init

	/*初始化時鐘*/
	bl system_clock_init

 由於重新配置了時鐘,而串口2的初始化使用的PCLK時鐘沒有使用串口專有的時鐘,所以也跟着發生了變化,串口2在IROM中的配置已經不能用了,不能輸出信息,重新配置一下:

/*
 * uart_asm_init: Initialize UART's pins
 */
uart_asm_init:
	/* set GPIO to enable UART0-UART4 */
	mov	r0, r8
	ldr	r1, =0x22222222
	str	r1, [r0, #0x0]			@ S5PC100_GPIO_A0_OFFSET
	ldr	r1, =0x00002222
	str	r1, [r0, #0x20]			@ S5PC100_GPIO_A1_OFFSET

	/* Check S5PC100 */
	cmp	r7, r8
	bne	110f

	/* UART_SEL GPK0[5] at S5PC100 */
	/*
    省略
    */

	b	200f
110:
	/*
	 * Note that the following address
	 * 0xE020'0360 is reserved address at S5PC100
	 */
	/* UART_SEL MP0_5[7] at S5PC110 */
	/*下面這堆代碼是在設置MP05_7這個腳,但在我的開發板上懸空的,可以刪除,也可以不管*/
	/*
    省略
    */

	/*配置串口2*/
	ldr r0, =S5PC110_UART_BASE
	/*ULCON 8n1*/
	ldr r1, =0x3
	str r1, [r0, #0x800]
	/*UCON*/
	ldr r1, =0x5
	str r1, [r0, #0x804]
	/*UFCON*/
	ldr r1, =0x217
	str r1, [r0, #0x808]
	/*UMCON*/
	ldr r1, =0x0
	str r1, [r0, #0x80C]
	/*UBRDIV*/
	ldr r1, =34
	str r1, [r0, #0x828]
	/*UDIVSLOT*/
	ldr r1, =0xDFDD
	str r1, [r0, #0x82C]
200:
	mov	pc, lr

接着是初始化DDR,我這個開發板的配置是4片128M,每兩片並聯組成32bit的數據線訪問,總容量512M,從手冊上面可以得知此芯片有兩個DDR控制器,總共能擴展的DDR內存最大1.5G:

圖1 內存映射圖

硬件上前256M在DRAM0區域,後256M在DRAM1區域,爲了讓兩片地址連續起來,前256M的起始地址設置爲0x30000000,後256M的起始地址設爲0x40000000,這樣剛好地址連續,DDR的初始化程序參考三星提供的,下面只對配置部分參數進行說明,關於時間參數不進行講解,配置流程參見手冊DRAM CONTROLLER篇(完整初始化代碼見本文末尾):

/*chip相當於rank,就好像電腦的內存插槽是有正反兩面的,我們只有一面,也就是一個rank*/
#define DMC0_MEMCONTROL		0x00202400 	// MemControl	BL=4, 1Chip, DDR2 Type, dynamic self refresh, force precharge, dynamic power down off

/*30代表起始地址是0x30000000,F0有點像大小的意思代表0xfffffff,1有點像掃描方式,3列的位數是10,2行的位數是14,3bank數是8*/
#define DMC0_MEMCONFIG_0	0x30F01323	// MemConfig0	256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
/*前面說了我們只用了一面,另一面沒用*/
#define DMC0_MEMCONFIG_1	0x30F00312	// MemConfig1		默認值

#define DMC0_TIMINGA_REF	0x00000618	// TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
#define DMC0_TIMING_ROW		0x28233287	// TimingRow	for @200MHz
#define DMC0_TIMING_DATA	0x23240304	// TimingData	CL=3
#define	DMC0_TIMING_PWR		0x09C80232	// TimingPower

#define	DMC1_MEMCONTROL		0x00202400	// MemControl	BL=4, 1chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off

/*除了起始地址,其它描述和DMC0都一樣*/
#define DMC1_MEMCONFIG_0	0x40F01323	// MemConfig0	256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
/*這個也是隻用了一面*/
#define DMC1_MEMCONFIG_1	0x60E00312	// MemConfig1		默認值

#define DMC1_TIMINGA_REF	0x00000618	// TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4
#define DMC1_TIMING_ROW		0x28233289	// TimingRow	for @200MHz
#define DMC1_TIMING_DATA	0x23240304	// TimingData	CL=3
#define	DMC1_TIMING_PWR		0x08280232	// TimingPower

將DDR初始化程序,ddr_init.S文件放到board/samsung/goni/目錄,然後修改該目錄下的Makefile,添加ddr_init.S的編譯:

obj-y	:= goni.o onenand.o
obj-y	+= lowlevel_init.o
/*增加ddr_init*/
obj-y	+= ddr_init.o

然後在時鐘初始化的後面添加上DDR的初始化:

/*...*/
bl	internal_ram_init

	/*初始化時鐘*/
	bl system_clock_init

	/*初始化DDR*/
	bl sdram_asm_init

DDR怎麼驗證有沒有初始化成功呢,可以添加一段測試程序,先向DDR的某個地址(注意這個地址一定要四字節對齊,因爲前面開啓了對齊檢查,否則訪問非對齊地址時會出錯,而且這個錯誤的唯一現象就是你看不到任何輸出,這時你可能會懷疑你的DDR初始化沒有成功,導致寫DDR時出錯,但實際上是因爲訪問了非對齊地址)寫入幾個值,然後再讀出來,我這裏寫了OK兩個字符,通過串口打印查看就可以了:

/*寫入字符'O'*/
	ldr r0, =0x40000000
	ldr r1, =0x4f
	str r1, [r0]
	/*寫入字符'K'*/
	ldr r0, =0x40000004
	ldr r1, =0x4b
	str r1, [r0]
	/*讀出然後通過串口輸出*/
	ldr r0, =0xE2900820
	ldr r1, =0x40000000
	ldr r2, [r1]
	str r2, [r0]
	/*讀出然後通過串口輸出*/
	ldr r0, =0xE2900820
	ldr r1, =0x40000004
	ldr r2, [r1]
	str r2, [r0]

由於硬件設計的原因,開發板上電後還需要進行電源置鎖,不然的話開機按鈕如果松手的話就掉電了,這裏將電源置鎖加上:

/*這個只是簡單的拉了一個IO的電平,不作介紹,添加函數體的實現到lowlevel_init.S文件的末尾,然後在時鐘初始化的前面調用*/
board_power_hold:
	ldr r0, =0xE010E81C
	ldr r1, [r0]
	ldr r2, =((0x1 << 0) | (0x1 << 8) | (0x1 << 9))
	orr r1, r1, r2
	str r1, [r0]
	mov pc, lr

在時鐘初始化前調用: 

/*...*/
bl	internal_ram_init

	/*電源置鎖*/
	bl board_power_hold

	/*初始化時鐘*/
	bl system_clock_init

	/*初始化DDR*/
	bl sdram_asm_init

到了這裏,DDR已經初始化好了,下一步就是將u-boot拷貝到DDR去執行了,我們u-boot是燒寫到sd卡的,三星在IROM代碼中已經提供了將SD卡的內容拷貝到內存的函數實現,我們只需要調用就可以了,這裏參考手冊(S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf):

圖2
圖3

上面圖2表示這個地址存儲了當前的啓動通道地址,圖3就是拷貝函數,三星提供了好幾個這樣的拷貝函數,用於從不同啓動介質中拷貝內容到內存中,這裏只截取了SD/MMC和eMMC的拷貝函數,從上面的圖可以看出CopySDMMCtoMem這個函數的函數體就放在0xD0037F98這個地址,所以我們只需要通過函數指針的方式,訪問這個地址的內容就相當於調用這個函數了,在board/samsung/goni/目錄添加一個copy_image_to_mem.c文件,寫入以下代碼:

#include <configs/s5p_goni.h>

/*
參數1:通道號
參數2:從SD卡的哪個扇區開始拷貝
參數3:拷貝多少個扇區
參數4:拷貝到內存的哪裏
參數5:不知道具體用途
*/
typedef bool (*CopySDMMCtoMemTypeDef)(int, unsigned int, unsigned short, unsigned int*, bool);

void CopySDMMCtoMem(void)
{
    CopySDMMCtoMemTypeDef pFuncCopySDMMCtoMem;

    pFuncCopySDMMCtoMem = (CopySDMMCtoMemTypeDef)(*((volatile unsigned int *)0xD0037F98));
    unsigned int ch = *((volatile unsigned int*)(0xd0037488));

    if(ch == 0xeb000000)
    {
        /*通道0,iNand*/
    }
    else if(ch == 0xeb200000)
    {
        /*通道2,SD卡*/
        pFuncCopySDMMCtoMem(2, 45, 1000,(unsigned int*)CONFIG_SYS_TEXT_BASE, 0); 
    }
}

有個需要注意的地方就是拷貝到內存的地址,這是個C函數,函數的調用需要使用棧,前面start.S裏面設置過一次棧,這裏的內存地址最好不要和當時設置的棧空間衝突(注意棧的生長方向是向下的);然後就是SD卡的起始扇區,這個是和你燒寫u-boot的起始扇區一樣的,因爲我是燒寫到45扇區開始的位置(這個位置不是固定的,只要沒有覆蓋掉BL1就行了),所以這裏設置45,拷貝大小(這裏設置的參數值是扇區數,乘以扇區大小512纔是真正的大小,我這裏拷貝1000*512=500K)只要大於等於u-boot的大小就行了,然後內存地址我設置的是鏈接地址。

順便貼下我的燒寫腳本:

#! /bin/bash

UBOOT=2020.04

#mkv210_image是製作BL1的,因爲BL1前16個字節需要填寫頭信息
./mkv210_image ~/Desktop/s5pv210/u-boot-${UBOOT}/u-boot.bin u-boot-sd.bin
sudo dd iflag=dsync oflag=dsync if=u-boot-sd.bin of=/dev/sdb seek=1
sudo dd iflag=dsync oflag=dsync if=~/Desktop/s5pv210/u-boot-${UBOOT}/u-boot.bin of=/dev/sdb seek=45

在board/samsung/goni/Makefile中添加,讓其編譯進u-boot,就可以調用這個函數進行拷貝了:

obj-y	:= goni.o onenand.o
obj-y	+= lowlevel_init.o
obj-y	+= ddr_init.o
/*添加編譯*/
obj-y	+= copy_image_to_mem.o
/*...*/
bl	internal_ram_init

	/*電源置鎖*/
	bl board_power_hold

	/*初始化時鐘*/
	bl system_clock_init

	/*初始化DDR*/
	bl sdram_asm_init

	/*拷貝u-boot到DDR*/
	bl CopySDMMCtoMem

其實還有很重要的一步,我前面沒有提到,就是我們調用的這個函數是不是都在BL1裏面,也就是是不是在u-boot的前16K,我們可以通過查看System.map文件,這個文件是u-boot編譯後生成的,位於u-boot根目錄:

圖4

 起始地址可以看到是0x34800000,前16K的內容就截止與0x34804000這個地址,所以我們只需要看我們調用的內容有沒有在這個地址內就行了,或者可以反彙編u-boot文件也可以知道,相對來說反彙編u-boot文件要詳細點。

好的,u-boot已經拷貝到DDR裏面了,所以緊接着我們進入DDR的u-boot中去執行,修改arch/arm/cpu/armv7/start.S文件:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
#ifdef CONFIG_CPU_V7A
	bl	cpu_init_cp15
#endif
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
	bl	cpu_init_crit
#endif
#endif

    /*註釋掉*/
	@ bl	_main
	/*修改這裏,跳轉到DDR中去執行*/
	ldr pc, =_main

這部分內容結束,就進入board_init_f了(也就是大家說的u-boot的第一階段),所以編譯看一下效果:

圖5

可以看到已經出來很多信息了,說明我們成功跳轉到DDR去執行了,不過卡在了OneNAND初始化的後面。 

完整DDR初始化代碼:

#include <config.h>

#define ELFIN_GPIO_BASE   S5PC110_GPIO_BASE
 
#define MP1_0DRV_SR_OFFSET 		0x3CC
#define MP1_1DRV_SR_OFFSET 		0x3EC
#define MP1_2DRV_SR_OFFSET 		0x40C
#define MP1_3DRV_SR_OFFSET 		0x42C
#define MP1_4DRV_SR_OFFSET 		0x44C
#define MP1_5DRV_SR_OFFSET 		0x46C
#define MP1_6DRV_SR_OFFSET 		0x48C
#define MP1_7DRV_SR_OFFSET 		0x4AC
#define MP1_8DRV_SR_OFFSET 		0x4CC
 
#define MP2_0DRV_SR_OFFSET 		0x4EC
#define MP2_1DRV_SR_OFFSET 		0x50C
#define MP2_2DRV_SR_OFFSET 		0x52C
#define MP2_3DRV_SR_OFFSET 		0x54C
#define MP2_4DRV_SR_OFFSET 		0x56C
#define MP2_5DRV_SR_OFFSET 		0x58C
#define MP2_6DRV_SR_OFFSET 		0x5AC
#define MP2_7DRV_SR_OFFSET 		0x5CC
#define MP2_8DRV_SR_OFFSET 		0x5EC

/*
 * SDRAM Controller
 */
#define APB_DMC_0_BASE			0xF0000000
#define APB_DMC_1_BASE			0xF1400000
#define ASYNC_MSYS_DMC0_BASE	0xF1E00000
 
#define DMC_CONCONTROL 			0x00
#define DMC_MEMCONTROL 			0x04
#define DMC_MEMCONFIG0 			0x08
#define DMC_MEMCONFIG1 			0x0C
#define DMC_DIRECTCMD 			0x10
#define DMC_PRECHCONFIG 		0x14
#define DMC_PHYCONTROL0 		0x18
#define DMC_PHYCONTROL1 		0x1C
#define DMC_RESERVED 			0x20
#define DMC_PWRDNCONFIG 		0x28
#define DMC_TIMINGAREF 			0x30
#define DMC_TIMINGROW 			0x34
#define DMC_TIMINGDATA 			0x38
#define DMC_TIMINGPOWER 		0x3C
#define DMC_PHYSTATUS 			0x40
#define DMC_CHIP0STATUS 		0x48
#define DMC_CHIP1STATUS 		0x4C
#define DMC_AREFSTATUS 			0x50
#define DMC_MRSTATUS 			0x54
#define DMC_PHYTEST0 			0x58
#define DMC_PHYTEST1 			0x5C
#define DMC_QOSCONTROL0 		0x60
#define DMC_QOSCONFIG0 			0x64
#define DMC_QOSCONTROL1 		0x68
#define DMC_QOSCONFIG1 			0x6C
#define DMC_QOSCONTROL2 		0x70
#define DMC_QOSCONFIG2 			0x74
#define DMC_QOSCONTROL3 		0x78
#define DMC_QOSCONFIG3 			0x7C
#define DMC_QOSCONTROL4 		0x80
#define DMC_QOSCONFIG4 			0x84
#define DMC_QOSCONTROL5 		0x88
#define DMC_QOSCONFIG5 			0x8C
#define DMC_QOSCONTROL6 		0x90
#define DMC_QOSCONFIG6 			0x94
#define DMC_QOSCONTROL7 		0x98
#define DMC_QOSCONFIG7 			0x9C
#define DMC_QOSCONTROL8 		0xA0
#define DMC_QOSCONFIG8 			0xA4
#define DMC_QOSCONTROL9 		0xA8
#define DMC_QOSCONFIG9 			0xAC
#define DMC_QOSCONTROL10 		0xB0
#define DMC_QOSCONFIG10 		0xB4
#define DMC_QOSCONTROL11 		0xB8
#define DMC_QOSCONFIG11 		0xBC
#define DMC_QOSCONTROL12 		0xC0
#define DMC_QOSCONFIG12 		0xC4
#define DMC_QOSCONTROL13 		0xC8
#define DMC_QOSCONFIG13 		0xCC
#define DMC_QOSCONTROL14 		0xD0
#define DMC_QOSCONFIG14 		0xD4
#define DMC_QOSCONTROL15 		0xD8
#define DMC_QOSCONFIG15 		0xDC

#define DMC0_MEMCONTROL		0x00202400 	// MemControl	BL=4, 1Chip, DDR2 Type, dynamic self refresh, force precharge, dynamic power down off

#define DMC0_MEMCONFIG_0	0x30F01323	// MemConfig0	256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
#define DMC0_MEMCONFIG_1	0x30F00312	// MemConfig1		默認值

#define DMC0_TIMINGA_REF	0x00000618	// TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
#define DMC0_TIMING_ROW		0x28233287	// TimingRow	for @200MHz
#define DMC0_TIMING_DATA	0x23240304	// TimingData	CL=3
#define	DMC0_TIMING_PWR		0x09C80232	// TimingPower

#define	DMC1_MEMCONTROL		0x00202400	// MemControl	BL=4, 1chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off

#define DMC1_MEMCONFIG_0	0x40F01323	// MemConfig0	256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
#define DMC1_MEMCONFIG_1	0x60E00312	// MemConfig1		默認值

#define DMC1_TIMINGA_REF	0x00000618	// TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4
#define DMC1_TIMING_ROW		0x28233289	// TimingRow	for @200MHz
#define DMC1_TIMING_DATA	0x23240304	// TimingData	CL=3
#define	DMC1_TIMING_PWR		0x08280232	// TimingPower

.globl sdram_asm_init
sdram_asm_init:	
	ldr	r0, =0xf1e00000
	ldr	r1, =0x0
	str	r1, [r0, #0x0]

	/* DMC0 Drive Strength (Setting 2X) */
	
	ldr	r0, =ELFIN_GPIO_BASE

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_0DRV_SR_OFFSET]		// 寄存器中對應0b10,就是2X

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_1DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_2DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_3DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_4DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_5DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_6DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_7DRV_SR_OFFSET]

	ldr	r1, =0x00002AAA
	str	r1, [r0, #MP1_8DRV_SR_OFFSET]

	
	/* DMC1 Drive Strength (Setting 2X) */
	
	ldr	r0, =ELFIN_GPIO_BASE
	
	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_0DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_1DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_2DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_3DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_4DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_5DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_6DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_7DRV_SR_OFFSET]

	ldr	r1, =0x00002AAA
	str	r1, [r0, #MP2_8DRV_SR_OFFSET]
	
	/* DMC0 initialization at single Type*/
	ldr	r0, =APB_DMC_0_BASE

	ldr	r1, =0x00101000				@PhyControl0 DLL parameter setting, manual 0x00101000
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00000086				@PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0x00101002				@PhyControl0 DLL on
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00101003				@PhyControl0 DLL start
	str	r1, [r0, #DMC_PHYCONTROL0]

find_lock_val:
	ldr	r1, [r0, #DMC_PHYSTATUS]		@Load Phystatus register value
	and	r2, r1, #0x7
	cmp	r2, #0x7				@Loop until DLL is locked
	bne	find_lock_val
	
	and	r1, #0x3fc0 
	mov	r2, r1, LSL #18
	orr	r2, r2, #0x100000
	orr	r2 ,r2, #0x1000	
		
	orr	r1, r2, #0x3				@Force Value locking
	str	r1, [r0, #DMC_PHYCONTROL0]
	
#if 0	/* Memory margin test 10.01.05 */
	orr	r1, r2, #0x1				@DLL off
	str	r1, [r0, #DMC_PHYCONTROL0]
#endif
	/* setting DDR2 */
	ldr	r1, =0x0FFF2010				@ConControl auto refresh off
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =DMC0_MEMCONTROL			@MemControl BL=4, 1 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]
	
	ldr	r1, =DMC0_MEMCONFIG_0			@MemConfig0 256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
	str	r1, [r0, #DMC_MEMCONFIG0]

	ldr	r1, =DMC0_MEMCONFIG_1			@MemConfig1
	str	r1, [r0, #DMC_MEMCONFIG1]

	ldr	r1, =0xFF000000				@PrechConfig
	str	r1, [r0, #DMC_PRECHCONFIG]
	
	ldr	r1, =DMC0_TIMINGA_REF			@TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
	str	r1, [r0, #DMC_TIMINGAREF]
	
	ldr	r1, =DMC0_TIMING_ROW			@TimingRow	for @200MHz
	str	r1, [r0, #DMC_TIMINGROW]

	ldr	r1, =DMC0_TIMING_DATA			@TimingData	CL=3
	str	r1, [r0, #DMC_TIMINGDATA]
	
	ldr	r1, =DMC0_TIMING_PWR			@TimingPower
	str	r1, [r0, #DMC_TIMINGPOWER]

	ldr	r1, =0x07000000				@DirectCmd	chip0 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00020000				@DirectCmd	chip0 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00030000				@DirectCmd	chip0 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000542				@DirectCmd	chip0 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000442				@DirectCmd	chip0 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010780				@DirectCmd	chip0 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x07100000				@DirectCmd	chip1 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00120000				@DirectCmd	chip1 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00130000				@DirectCmd	chip1 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00100542				@DirectCmd	chip1 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00100442				@DirectCmd	chip1 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00110780				@DirectCmd	chip1 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]
		
	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]
		
	ldr	r1, =0x0FF02030				@ConControl	auto refresh on
	str	r1, [r0, #DMC_CONCONTROL]
		
	ldr	r1, =0xFFFF00FF				@PwrdnConfig
	str	r1, [r0, #DMC_PWRDNCONFIG]
		
	ldr	r1, =0x00202400				@MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]
	
// 上面是DRAM0初始化步驟
/*******************************************************************************************/	
// 下面是DRAM1初始化步驟,兩者沒有聯繫,是並列的。

	/* DMC1 initialization */
	ldr	r0, =APB_DMC_1_BASE
	ldr	r1, =0x00101000				@Phycontrol0 DLL parameter setting
	str	r1, [r0, #DMC_PHYCONTROL0]

	
	ldr	r1, =0x00000086				@Phycontrol1 DLL parameter setting
	str	r1, [r0, #DMC_PHYCONTROL1]
	ldr	r1, =0x00101002				@PhyControl0 DLL on
	str	r1, [r0, #DMC_PHYCONTROL0]
	ldr	r1, =0x00101003				@PhyControl0 DLL start
	str	r1, [r0, #DMC_PHYCONTROL0]
	
	
	
find_lock_val1:
	ldr	r1, [r0, #DMC_PHYSTATUS]		@Load Phystatus register value
	and	r2, r1, #0x7
	cmp	r2, #0x7				@Loop until DLL is locked
	bne	find_lock_val1
	
	and	r1, #0x3fc0 
	mov	r2, r1, LSL #18
	orr	r2, r2, #0x100000
	orr	r2, r2, #0x1000
		
	orr	r1, r2, #0x3				@Force Value locking
	str	r1, [r0, #DMC_PHYCONTROL0]
	
#if 0	/* Memory margin test 10.01.05 */
	orr	r1, r2, #0x1				@DLL off
	str	r1, [r0, #DMC_PHYCONTROL0]
#endif

	/* settinf fot DDR2 */
	ldr	r0, =APB_DMC_1_BASE

	ldr	r1, =0x0FFF2010				@auto refresh off
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =DMC1_MEMCONTROL			@MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]

	ldr	r1, =DMC1_MEMCONFIG_0			@MemConfig0	512MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
	str	r1, [r0, #DMC_MEMCONFIG0]

	ldr	r1, =DMC1_MEMCONFIG_1			@MemConfig1
	str	r1, [r0, #DMC_MEMCONFIG1]

	ldr	r1, =0xFF000000
	str	r1, [r0, #DMC_PRECHCONFIG]

	ldr	r1, =DMC1_TIMINGA_REF			@TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4
	str	r1, [r0, #DMC_TIMINGAREF]

	ldr	r1, =DMC1_TIMING_ROW			@TimingRow	for @200MHz
	str	r1, [r0, #DMC_TIMINGROW]

	ldr	r1, =DMC1_TIMING_DATA			@TimingData	CL=3
	str	r1, [r0, #DMC_TIMINGDATA]

	ldr	r1, =DMC1_TIMING_PWR			@TimingPower
	str	r1, [r0, #DMC_TIMINGPOWER]


	ldr	r1, =0x07000000				@DirectCmd	chip0 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00020000				@DirectCmd	chip0 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00030000				@DirectCmd	chip0 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000542				@DirectCmd	chip0 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000442				@DirectCmd	chip0 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010780				@DirectCmd	chip0 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x07100000				@DirectCmd	chip1 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00120000				@DirectCmd	chip1 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00130000				@DirectCmd	chip1 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110440				@DirectCmd	chip1 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00100542				@DirectCmd	chip1 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00100442				@DirectCmd	chip1 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110780				@DirectCmd	chip1 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x0FF02030				@ConControl	auto refresh on
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =0xFFFF00FF				@PwrdnConfig	
	str	r1, [r0, #DMC_PWRDNCONFIG]

	ldr	r1, =DMC1_MEMCONTROL			@MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]
	// 函數返回
	mov pc, lr

歡迎掃碼關注我的微信公衆號

漫長當下
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章