天嵌TQ2440 ARM开发板Uboot移植

 

TQ2440 ARM开发板Uboot移植

 

移植环境

    机:VMWare--Fedora 10 Kernel:2.6.34 
开发板:TQ2440--256MB Nand
编译器:arm-linux-gcc-4.3.3.tgz 
u-boot
u-boot-1.1.6

代码查看工具:Source Insight

移植步骤

本次移植的功能特点包括:

Nand Flash读写
Nor/Nand
自动选择启动
DM9000
网卡支持
Yaffs2
文件系统支持
Uboot
启动菜单

 

1. 清除已编译的文件,配置编译选项

make distclean

修改u-boot/cpu/arm920t/config.mk文件:

PLATFORM_RELFLAGS += -fno-strict-aliasing  -fno-common -ffixed-r8 /
-msoft-float

注释掉 / -msoft-float这一段,不使用软浮点编译。

PLATFORM_CPPFLAGS += -march=armv4修改成

PLATFORM_CPPFLAGS += -march=armv4t

来避免编译时产生target CPU does not support interworking的错误。

2. 建立自己的开发板项目并测试编译。

 

tar xjfv u-boot-1.1.6.tar.bz2 –C /opt/bootloader

cd /opt/bootloader/u-boot-1.1.6/board

cp –fr smdk2410/ tender/

cd tender

mv smdk2410.c tender.c

cd /opt/bootloader/u-boot-1.1.6/include/configs

cp –f smdk2410.h tender.h

U-boot对我们使用的TQ2440开发板和S3C2440芯片并不支持,但是我们可以以具有相似性的SMDK2410为参考,通过以上几个步骤,建立自己开发板的项目。取名为tender 。现在继续。

vim /opt/bootloader/u-boot-1.1.6/Makefile

对自己的开发板进行配置

smdk2410_config   :      unconfig

       @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

 

tender_config         :      unconfig

       @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

 

SX1_config :         unconfig

       @$(MKCONFIG) $(@:_config=) arm arm925t sx1

保存退出。

vim /opt/bootloader/u-boot-1.1.6/board/tender/Makefile

将我们的开发板的编译项进行修改。

LIB  = $(obj)lib$(BOARD).a

COBJS    := tender.o flash.o

SOBJS    := lowlevel_init.o

接着:

vim /opt/bootloader/u-boot-1.1.6/include/configs/tender.h

进入配置头文件,添加与我们使用的开发板相关的宏定义。

#define    CONFIG_TENDER              1

#define CONFIG_ARM920T        1     /* This is an ARM920T Core */

#define    CONFIG_S3C2440              1     /* in a SAMSUNG S3C2440 SoC     */

/* #define CONFIG_SMDK2410         1     on a SAMSUNG SMDK2410 Board  */

查看board目录下的u-boot.lds文件可以知道,我们整个工程的入口位于cpu/arm920t/start.c。接下去所有跳转指令都是位于u-boot的根目录下进行的操作。

cd cpu/arm920t/start.c

#elif defined(CONFIG_S3C2410)

# define pWTCON        0x53000000

# define INTMSK         0x4A000008   /* Interupt-Controller base addresses */

# define INTSUBMSK   0x4A00001C

# define CLKDIVN       0x4C000014   /* clock divisor register */

#elif defined(CONFIG_TENDER)

# define pWTCON        0x53000000

# define INTMSK         0x4A000008   /* Interupt-Controller base addresses */

# define INTSUBMSK   0x4A00001C

# define CLKDIVN      0x4C000014   /* clock divisor register */

S3C2440用户手册中的中断和看门狗寄存器与S3C2410地址相似。进行以上的修改。紧接着进行reset设置,包括关闭看门狗、禁止中断、禁止子中断、配置时钟分频比,同时将总线模式修改为异步模式,原因在于我们使用的分频比为148,根据用户手册说明,HDIV=1的时候,总线工作模式应该为异步。在相应的位置加入如下代码。

#if defined(CONFIG_TENDER)

       ldr     r0, =pWTCON

       mov     r1, #0x0

       str     r1, [r0]

 

       /*

        * mask all IRQs by setting all bits in the INTMR - default

        */

       mov r1, #0xffffffff

       ldr   r0, =INTMSK

       str    r1, [r0]

      

       ldr   r1, =0x7ff

       ldr   r0, =INTSUBMSK

       str    r1, [r0]

# endif

 

       /* FCLK:HCLK:PCLK = 1:4:8 */

       /* default FCLK is 400 MHz ! */

       ldr   r0, =CLKDIVN

       mov r1, #5

       str    r1, [r0]

#endif     /* CONFIG_TENDER */

  mrc p15,0,r0,c1,c0,0    //根据2440Datasheet,设置异步总线模式
  orr r0,r0,#0xc0000000  /*R1_nF:OR:R1_iA*/
  mcr p15,0,r0,c1,c0,0

完成上述任务之后,将会跳到cpu_init_crit函数,

 #ifndef CONFIG_SKIP_LOWLEVEL_INIT

       bl    cpu_init_crit

#endif

在这个函数里面,在完成mmu的关闭的同时,还完成了13个寄存器的初始化,这些寄存器的任务是进行内存管理。

       bl    lowlevel_init

它位于board/tender/lowlevel_init.S文件中,我们的SDRAM是挂载在Bank6上的,修改同文件下的SDRAM配置,使之和开发板的硬件配置一致。检查在board/smdk2440/lowlevel_init.S

一并将之后需要涉及的Nor Flash、网卡和串口进入位宽修改。
#define B1_BWSCON     (DW16)
#define B4_BWSCON     (DW16) //接网卡,位宽为16
#define B5_BWSCON     (DW8)
#define B6_BWSCON     (DW32) //接内存,位宽为32
根据HCLK设置SDRAM 的刷新参数,主要是REFCNT寄存器,开发板HCLK100M,查看SDRAMdatasheet进行计算,
  #define REFCNT   0x1113  改为  #define REFCNT  0x4f4

 

接着,start.c完成了将程序收入C程序中,入口地址为

       ldr   pc, _start_armboot

_start_armboot:      .word start_armboot

函数start_armboot位于lib_arm/board.c文件中。

#cd   lib-arm

#vim board.c

start_armboot首先调用下面这个函数,以完成一系列的初始化。

里面有一个如下的结构体数组,定义了第二阶段运行的具体步骤

init_fnc_t *init_sequence[] = {

         cpu_init,            /* basic cpu dependent setup——cpu/arm920t/cpu.c */

         board_init,                 /* basic board dependent setup——board/tender/tender.c */

         interrupt_init,           /* set up exceptions ——cpu/arm920t/s3c24x0/interrupt.c*/

         env_init,            /* initialize environment——common/env_flash.c */

         init_baudrate,          /* initialze baudrate settings ——lib_arm/board.c*/

         serial_init,                 /* serial communications setup ——cpu/arm920t/s3c24x0/serial.c*/

         console_init_f,          /* stage 1 init of console ——common/console.c*/

         display_banner,                 /* say that we are here ——lib_arm/board.c*/

#if defined(CONFIG_DISPLAY_CPUINFO)

         print_cpuinfo,           /* display cpu info (and speed) */

#endif

#if defined(CONFIG_DISPLAY_BOARDINFO)

         checkboard,              /* display board info */

#endif

         dram_init,                  /* configure available RAM banks ——board/tender/tender.c*/

         display_dram_config,/*显示RAM的配置大小——lib_arm/board.c*/

         NULL,

};

cpu_init中,主要完成对FIQIRQ的栈初始化。无需进行修改。

board_init位于board.ctender.c的文件中。它的主要工作是对时钟的部分初始化及对I/O的初始化。由于S3C2440S3C2410的时钟和GPIO都不一样。因而,查看用户手册

S3C24X0_CLOCK_POWERS3C24X0_GPIO的定义位于include/s3c24x0.h中,

typedef struct {

       S3C24X0_REG32  LOCKTIME;

       S3C24X0_REG32  MPLLCON;

       S3C24X0_REG32  UPLLCON;

       S3C24X0_REG32  CLKCON;

       S3C24X0_REG32  CLKSLOW;

       S3C24X0_REG32  CLKDIVN;

       S3C24X0_REG32  CAMDIVN;

} /*__attribute__((__packed__))*/ S3C24X0_CLOCK_POWER;

 

 

/* I/O PORT (see manual chapter 9) */

typedef struct {

#ifdef CONFIG_S3C2400

       S3C24X0_REG32  PACON;

       S3C24X0_REG32  PADAT;

 

       S3C24X0_REG32  PBCON;

       S3C24X0_REG32  PBDAT;

       S3C24X0_REG32  PBUP;

 

       S3C24X0_REG32  PCCON;

       S3C24X0_REG32  PCDAT;

       S3C24X0_REG32  PCUP;

 

       S3C24X0_REG32  PDCON;

       S3C24X0_REG32  PDDAT;

       S3C24X0_REG32  PDUP;

 

       S3C24X0_REG32  PECON;

       S3C24X0_REG32  PEDAT;

       S3C24X0_REG32  PEUP;

 

       S3C24X0_REG32  PFCON;

       S3C24X0_REG32  PFDAT;

       S3C24X0_REG32  PFUP;

 

       S3C24X0_REG32  PGCON;

       S3C24X0_REG32  PGDAT;

       S3C24X0_REG32  PGUP;

 

       S3C24X0_REG32  OPENCR;

 

       S3C24X0_REG32  MISCCR;

       S3C24X0_REG32  EXTINT;

#endif

#if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)

       S3C24X0_REG32  GPACON;

       S3C24X0_REG32  GPADAT;

       S3C24X0_REG32  res1[2];

       S3C24X0_REG32  GPBCON;

       S3C24X0_REG32  GPBDAT;

       S3C24X0_REG32  GPBUP;

       S3C24X0_REG32  res2;

       S3C24X0_REG32  GPCCON;

       S3C24X0_REG32  GPCDAT;

       S3C24X0_REG32  GPCUP;

       S3C24X0_REG32  res3;

       S3C24X0_REG32  GPDCON;

       S3C24X0_REG32  GPDDAT;

       S3C24X0_REG32  GPDUP;

       S3C24X0_REG32  res4;

       S3C24X0_REG32  GPECON;

       S3C24X0_REG32  GPEDAT;

       S3C24X0_REG32  GPEUP;

       S3C24X0_REG32  res5;

       S3C24X0_REG32  GPFCON;

       S3C24X0_REG32  GPFDAT;

       S3C24X0_REG32  GPFUP;

       S3C24X0_REG32  res6;

       S3C24X0_REG32  GPGCON;

       S3C24X0_REG32  GPGDAT;

       S3C24X0_REG32  GPGUP;

       S3C24X0_REG32  res7;

       S3C24X0_REG32  GPHCON;

       S3C24X0_REG32  GPHDAT;

       S3C24X0_REG32  GPHUP;

       S3C24X0_REG32  res8;

 

       S3C24X0_REG32  MISCCR;

       S3C24X0_REG32  DCLKCON;

       S3C24X0_REG32  EXTINT0;

       S3C24X0_REG32  EXTINT1;

       S3C24X0_REG32  EXTINT2;

       S3C24X0_REG32  EINTFLT0;

       S3C24X0_REG32  EINTFLT1;

       S3C24X0_REG32  EINTFLT2;

       S3C24X0_REG32  EINTFLT3;

       S3C24X0_REG32  EINTMASK;

       S3C24X0_REG32  EINTPEND;

       S3C24X0_REG32  GSTATUS0;

       S3C24X0_REG32  GSTATUS1;

       S3C24X0_REG32  GSTATUS2;

       S3C24X0_REG32  GSTATUS3;

       S3C24X0_REG32  GSTATUS4;

# if defined(CONFIG_S3C2440)

       S3C24X0_REG32  res9;

       S3C24X0_REG32  DSC0;

       S3C24X0_REG32  DSC1;

       S3C24X0_REG32  MSLCON;

       S3C24X0_REG32  GPJCON;

       S3C24X0_REG32  GPJDAT;

       S3C24X0_REG32  GPJUP;

#endif

#endif

} /*__attribute__((__packed__))*/ S3C24X0_GPIO;

由于之前我们的对S3C2440芯片的支持也是直接拷贝s3c2410.h的,因此也需要进入修改。在此先进行简单的修改。也可以直接参考用户手册,进行全部校正。进入s3c2440.h文件中。将所有的2410全都替换成2440即可。回到tender.c文件中。

修改宏定义如下使之满足我们使用的频率:

#define FCLK_SPEED 2

 

#if FCLK_SPEED==0          /* Fout = 203MHz, Fin = 12MHz for Audio */

#define M_MDIV   0xC3

#define M_PDIV    0x4

#define M_SDIV    0x1

#elif FCLK_SPEED==1              /* Fout = 202.8MHz */

#define M_MDIV   0xA1

#define M_PDIV    0x3

#define M_SDIV    0x1

#elif FCLK_SPEED==2              /* Fout = 400MHz */

#define M_MDIV   0x5C

#define M_PDIV    0x1

#define M_SDIV    0x1

#endif

 

#define USB_CLOCK 2

 

#if USB_CLOCK==0

#define U_M_MDIV     0xA1

#define U_M_PDIV      0x3

#define U_M_SDIV      0x1

#elif USB_CLOCK==1

#define U_M_MDIV     0x48

#define U_M_PDIV      0x3

#define U_M_SDIV      0x2

#elif USB_CLOCK==2

#define U_M_MDIV     0x38

#define U_M_PDIV      0x2

#define U_M_SDIV      0x2

#endif

再将我们之前添加的GPJ进入初始化:

       gpio->GPHCON = 0x002AFAAA;

       gpio->GPHUP = 0x000007FF;

       gpio->GPJCON = 0x02AAAAAA;

       gpio->GPJUP = 0x00001FFF;

 

       gd->bd->bi_arch_number = MACH_TYPE_TENDER;

这里还添加了我们自己设置的机器码MACH_TYPE_TENDER位于include/asm-arm/mach-types.h文件中。相应位置加入

MACH_TYPE_TENDER      1111

到此,board的初始化告一段落。

对于:

         interrupt_init,           /* set up exceptions ——cpu/arm920t/s3c24x0/interrupt.c*/

(由于我有编译的时候有出错,这里选在这个文件中,模仿CONFIG_SMDK2410加入CONFIG_TENDER的配置。)

在此文件中定义

                   timer_load_val = get_PCLK()/(2 * 16 * 100);

用到了get_PCLK(),该函数位于cpu/arm920t/s3c24x0/speed.c中,

原型为

ulong get_PCLK(void)

{

    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

 

    return((clk_power->CLKDIVN & 0x1) ? get_HCLK()/2 : get_HCLK());

}

再调用函数get_HCLK()

ulong get_HCLK(void)

{

    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

 

    return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());

}

又调用了get_FCLK()函数

ulong get_FCLK(void)

{

    return(get_PLLCLK(MPLL));

}

层层调用之后的底层函数也是同一文件下的get_PLLCLK(int pllreg),原型为

static ulong get_PLLCLK(int pllreg)

{

    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

    ulong r, m, p, s;

 

    if (pllreg == MPLL)

         r = clk_power->MPLLCON;

    else if (pllreg == UPLL)

         r = clk_power->UPLLCON;

    else

         hang();

 

    m = ((r & 0xFF000) >> 12) + 8;

    p = ((r & 0x003F0) >> 4) + 2;

    s = r & 0x3;

 

    return((CONFIG_SYS_CLK_FREQ * m) / (p << s));

}

我们查看了用户手册,知道时钟最大的区别在用户手册中的如下一段介绍:

MPLL Control Register

Mpll = (2 * m * Fin) / (p * 2s)

m = (MDIV + 8), p = (PDIV + 2), s = SDIV

UPLL Control Register

Upll = (m * Fin) / (p * 2s)

m = (MDIV + 8), p = (PDIV + 2), s = SDIV

PLL Value Selection Guide (MPLLCON)

1. Fout = 2 * m * Fin / (p*2s), Fvco = 2 * m * Fin / p where : m=MDIV+8, p=PDIV+2, s=SDIV

2. 600MHz FVCO 1.2GHz

3. 200MHz FCLKOUT 600MHz

4. Don't set the P or M value as zero, that is, setting the P=000000, M=00000000 can cause malfunction of

the PLL.

5. The proper range of P and M: 1 P 62, 1 M 248

由此我们对以下两个函数进行修改。

static ulong get_PLLCLK(int pllreg)

{

    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

    ulong r, m, p, s;

 

    if (pllreg == MPLL)

       r = clk_power->MPLLCON;

    else if (pllreg == UPLL)

       r = clk_power->UPLLCON;

    else

       hang();

 

    m = ((r & 0xFF000) >> 12) + 8;

    p = ((r & 0x003F0) >> 4) + 2;

    s = r & 0x3;

 

#ifdef      CONFIG_TENDER

       return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s));

#else

return((CONFIG_SYS_CLK_FREQ * m) / (p << s));

#endif

}

及其

ulong get_HCLK(void)

{

    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

 

#ifdef CONFIG_TENDER

       return(get_FCLK()/4);

#else

    return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());

#endif

}

做完这么多事,我们还需要进入关键的一步

uboot的根目录下,使用下述指令,
find . –name /* -type f –print | xargs grep “<s3c2410.h>”

找出所有包含s3c2410.h的头文件,将它们替换成s3c2440.h

完成上述步骤,我们已经完成了对S3C2440的基本支持。

 

 

NorFlash的支持

本开发板使用的NorFlash的型号为EONEN29LV160A,大小2M,扇区为35个。可以参考PDF文档。

tender.h配置文件中的NorFlash部分进入如下修改。

#if 0

#define CONFIG_AMD_LV400   1     /* uncomment this if you have a LV400 flash */

#define CONFIG_AMD_LV800   1     /* uncomment this if you have a LV800 flash */

#endif

#define CONFIG_EON_LV160A  1

 

#define CFG_MAX_FLASH_BANKS  1     /* max number of memory banks */

 

#ifdef CONFIG_EON_LV160A

#define PHYS_FLASH_SIZE              0x00200000 /* 2MB */

#define CFG_MAX_FLASH_SECT     (35) /* max number of sectors on one chip */

#define CFG_ENV_ADDR          (CFG_FLASH_BASE + 0x0F0000) /* addr of environment */

#endif

#ifdef CONFIG_AMD_LV800

#define PHYS_FLASH_SIZE              0x00100000 /* 1MB */

#define CFG_MAX_FLASH_SECT     (19) /* max number of sectors on one chip */

#define CFG_ENV_ADDR          (CFG_FLASH_BASE + 0x0F0000) /* addr of environment */

#endif

#ifdef CONFIG_AMD_LV400

#define PHYS_FLASH_SIZE              0x00080000 /* 512KB */

#define CFG_MAX_FLASH_SECT     (11) /* max number of sectors on one chip */

#define CFG_ENV_ADDR          (CFG_FLASH_BASE + 0x070000) /* addr of environment */

#endif

 

这里配置的NorFlash并不是Uboot所支持的。需要我们进行添加。

进入include/flash.h文件中,在Device IDs位置加入

#define EON_MANUFACT  0X00110011 /* 这里的号码是我自己定义的 */

#define EON_ID_LV160B    0x11491149 /* 同样,此处也是自己定义的 */

 

通过阅读board.c文件,我们知道,在完成init_fnc_t *init_sequence[]这个数组的初始化之后,uboot将会进行flash_init函数调用,即进行Flash的初始化。这个函数位于board/tender/flash.c文件中。

                   flash_info[i].flash_id =

#if defined(CONFIG_AMD_LV400)

                            (AMD_MANUFACT & FLASH_VENDMASK) |

                            (AMD_ID_LV400B & FLASH_TYPEMASK);

#elif defined(CONFIG_AMD_LV800)

                            (AMD_MANUFACT & FLASH_VENDMASK) |

                            (AMD_ID_LV800B & FLASH_TYPEMASK);

#elif defined(CONFIG_EON_LV160A)

                            (EON_MANUFACT & FLASH_VENDMASK) |

                       (EON_ID_LV160A & FLASH_TYPEMASK);

 

flash_init函数下面,flash_print_info函数也需要进行修改。

void flash_print_info (flash_info_t * info)

{

     int i;

 

     switch (info->flash_id & FLASH_VENDMASK) {

     case (AMD_MANUFACT & FLASH_VENDMASK):

              printf ("AMD: ");

              break;

     case (EON_MANUFACT & FLASH_VENDMASK):

              printf ("EON: ");

     default:

              printf ("Unknown Vendor ");

              break;

     }

 

     switch (info->flash_id & FLASH_TYPEMASK) {

     case (AMD_ID_LV400B & FLASH_TYPEMASK):

              printf ("1x Amd29LV400BB (4Mbit)/n");

              break;

     case (AMD_ID_LV800B & FLASH_TYPEMASK):

              printf ("1x Amd29LV800BB (8Mbit)/n");

              break;

     case (EON_ID_LV160A & FLASH_TYPEMASK):

              printf ("1x EN29LV160A (16Mbit)/n");

              break;

     default:

              printf ("Unknown Chip Type/n");

              goto Done;

              break;

     }

编译u-boot,下载到开发板运行,启动时正常显示Nor Flash的信息。

 

NandFlash的支持

本开发板使用的NandFlash芯片是K9F2G08U0A,大小为256MHz

继续根据start_armboot的走向进行修改。完成了前面的NorFlash的初始化后,有如下一段代码:

#if (CONFIG_COMMANDS & CFG_CMD_NAND)

     puts ("NAND:  ");

     nand_init();                /* go init the NAND */

#endif

这里指引我们进入NandFlash的初始化。于是为了对NandFlash进入初始化,我们进入配置文件tender.h中将CONFIG_COMMANDS & CFG_CMD_NAND的宏定义加上

#define CONFIG_COMMANDS /

                       (CONFIG_CMD_DFL         | /

                       CFG_CMD_CACHE  | /

                       CFG_CMD_NAND    | /

                       /*CFG_CMD_EEPROM |*/ /

                       /*CFG_CMD_I2C     |*/ /

                       /*CFG_CMD_USB    |*/ /

                       CFG_CMD_REGINFO  | /

                       CFG_CMD_DATE     | /

                       CFG_CMD_ELF)

这样,start_armboot在运行的时候将会调用位于drivers/nand/nand.c文件中的nand_init函数。下述就是这个函数原型

void nand_init(void)

{

     int i;

     unsigned int size = 0;

     for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {

              nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);

              size += nand_info[i].size;

              if (nand_curr_device == -1)

                       nand_curr_device = i;

     }

     printf("%lu MiB/n", size / (1024 * 1024));

 

在查看这个函数的过程中,我们发现上述两个红色标出的宏都是未定义的。于是,继续在配置文件tender.h最后面加入如下:

#define     CFG_MAX_NAND_DEVICE                1

因为我们只能用一片NandFlash所以都设置为1

我们还发现在nand_init函数中调用了同文件下面的nand_init_chip函数。上面的蓝色部分。

引入了nand_chip函数,我们查看的过程中发现,这个函数引用的结构体nand_bbt_descr中有一个宏NAND_MAX_CHIPS并未在文件中进行声明。依然像上面所说的,

在配置文件tender.h最后面加入如下:

#define     NAND_MAX_CHIPS           1

 

引入的另一个参数是base_address,它的定义位于同文件中如下:

static ulong base_address[CFG_MAX_NAND_DEVICE] = CFG_NAND_BASE_LIST;

这里的CFG_NAND_BASE_LIST也在nand.c文件开头进行了宏定义。

#ifndef CFG_NAND_BASE_LIST

#define CFG_NAND_BASE_LIST { CFG_NAND_BASE }

#endif

经过层层调用,我们会发现,其实源头在CFG_NAND_BASE这个宏,依旧需要我们自己进行定义。于是在tender.h文件中继续添加:

#define     CFG_NAND_BASE             1

完成上述之后,我们接着看nand_init_chip这个函数。

static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,

                               ulong base_addr)

{

         mtd->priv = nand;

 

         nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;

         board_nand_init(nand);

 

         if (nand_scan(mtd, 1) == 0) {

                   if (!mtd->name)

                            mtd->name = (char *)default_nand_name;

         } else

                   mtd->name = NULL;

 

}

因为uboot不知道用户使用的是nandflash是什么型号的,于是留下一个接口函数,board_nand_init(nand); 让用户自己来添加。这个函数需要自己来写。在cpu/arm920t/s3c24x0下建一个文件nand_flash.c,,同时在这个文件下的Makefile文件中将这个文件加入编译否则会出错。每加入一个文件都应该相应的在Makefile中进行修改。

#include <common.h>

#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)

#include <nand.h>

 

#include <s3c2440.h>

#define S3C2440_NFSTAT_READY   (1<<0)

#define S3C2440_NFCONT_nFCE             (1<<1)

/* select chip, for s3c2440 */

static void s3c2440_nand_select_chip(struct mtd_info *mtd, int chip)

{

    S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

    if (chip == -1) {

        s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;

    } else {

        s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;

    }

}

 

/* command and control functions */

static void s3c2440_nand_hwcontrol(struct mtd_info *mtd,int cmd)

{

         S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

         struct nand_chip *chip = mtd->priv;

         switch (cmd){

         case NAND_CTL_SETNCE:

                            s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;

                   case NAND_CTL_CLRNCE:

                            s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;

                            printf("%s: called for NCE/n", __FUNCTION__);

                            break;

                   case NAND_CTL_SETCLE:

                            chip->IO_ADDR_W = (void *)&s3c2440nand->NFCMD;

                            break;

                   case NAND_CTL_SETALE:

                            chip->IO_ADDR_W = (void *)&s3c2440nand->NFADDR;

                            break;

                            /* NAND_CTL_CLRCLE: */

                            /* NAND_CTL_CLRALE: */

                   default:

                            chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;

                            break;

         }

}

 

/* s3c2440_nand_devready()

 *

 *returns 0 if the nand is busy, 1 if it is ready

 */

static int s3c2440_nand_devready(struct mtd_info *mtd)

{

         S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

         return(s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);

}

 

/*

 *Called by driver/nand/nand.c,initialize the interface of nand flash

 */

int board_nand_init(struct nand_chip *chip)

{

         #define TACLS  0

         #define TWRPH0      4

         #define TWRPH1      2

         S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

         s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);

         s3c2440nand->NFCONT = (1<<4)|(0<<1)|(1<<0);

 

         chip->IO_ADDR_R   = (void *)&s3c2440nand->NFDATA;

         chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;

         chip->hwcontrol       = s3c2440_nand_hwcontrol;

         chip->dev_ready = s3c2440_nand_devready;

         chip->select_chip = s3c2440_nand_select_chip;

 

         chip->options   = 0;

         chip->eccmode         = NAND_ECC_SOFT;

         return 0;

}

#endif

我们来看看board_nand_init这个函数,里面的TACLS/TWRPH0/TWRPH1分别为0/4/2代表0HCLK时钟、5HCLK时钟、3HCLK时钟。当HCLK=100MHZ时,(0+5+3/100us=80ns>46ns=(CLE Hold Time=5+CLE Setup Time=12+ALE Hold Time=5+ALE Setup Time=12+WE# Pulse Width=12)ns,也满足K9F2G08U0A的时序要求。

S3C2440_NAND我们在s3c2440.h头文件中进行修改时,由于没有相应在s3c24x0.h文件加入我们的s3c2440NAND寄存器的地址结构。这里会提示未定义。

于是,我们在s3c24x0.h中,加入如下(参考S3C2440用户手册第六)

/* NAND FLASH (see S3C2440 manual chapter 6) */

typedef struct {

         S3C24X0_REG32      NFCONF;

         S3C24X0_REG32      NFCONT;

         S3C24X0_REG32      NFCMD;

         S3C24X0_REG32      NFADDR;

         S3C24X0_REG32      NFDATA;

         S3C24X0_REG32      NFMECCD0;

         S3C24X0_REG32      NFMECCD1;

         S3C24X0_REG32      NFSECCD;

         S3C24X0_REG32      NFSTAT;

         S3C24X0_REG32      NFESTAT0;

         S3C24X0_REG32      NFESTAT1;

         S3C24X0_REG32      NFMECC0;

         S3C24X0_REG32      NFMECC1;

         S3C24X0_REG32      NFSECC;

         S3C24X0_REG32      NFSBLK;

         S3C24X0_REG32      NFEBLK;

} /*__attribute__((__packed__))*/ S3C2440_NAND;

board_nand_init()这个函数的主要功能是对struct nand_chip结构体的函数指针赋值,让它们指向自己为nand驱动编写的一些函数,对未赋值的指针,uboot会在后面为其赋上通用nand驱动函数指针。
nand_init_chip()
接着调用nand_scan().
nand_scan()
定义在drivers/nand/nand_base.c文件中。它首先对struct nand_chip结构体中在board_nand_init()函数中未赋值的指针赋上通用nand驱动函数指针。
通用nand驱动函数nand_select_chip()赋值给struct nand_chip结构体的函数指针用于打开或关闭nand芯片,0为打开,1为关闭。在这个函数中会调用nand_chip结构体中的hwcontrol函数指针,这个指针指向的函数是需要自己编写的。这个函数指针在board_nand_init()函数中被赋值。主要作用是向nand flash发送一些nand flash开启与关闭命令。
nand_scan()
剩余部分初始化nand_chipmtd_info结构体。
nand_scan()
最后在返回时调用drivers/nand/nand_bbt.c文件中的nand_default_bbt()
nand_default_bby()
选择一个坏块描述表,返回时调用本文件中的nand_scan_bbt()
nand_scan_bbt()
寻找建立一个坏块描述表。
最后返回到nand_init(),这样nand驱动的初始化完成了。

 

以上部分是对我们NANDFlash的操作进行了配置,我们回过来看,在start.S中,我们知道它的引导是直接从NorFlash中启动的。为了让它能从NandFlash中启动。我们进行了如下修改。主要是将栈的初始化放到前面,用于后面调用C函数的需要;判断u-boot是不是从内存启动后,调用CopyCode2Ram函数,实现把启动代码拷贝到内存。

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

         bl      cpu_init_crit

#endif

 

         /* Set up the stack                                                     */

stack_setup:

         ldr    r0, _TEXT_BASE                 /* upper 128 KiB: relocated uboot   */

         sub   r0, r0, #CFG_MALLOC_LEN     /* malloc area                      */

         sub   r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */

#ifdef CONFIG_USE_IRQ

         sub   r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

         sub   sp, r0, #12                 /* leave 3 words for abort-stack    */

 

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:                              /* relocate U-Boot to RAM          */

         adr   r0, _start          /* r0 <- current position of code   */

         ldr    r1, _TEXT_BASE                 /* test if we run from flash or RAM */

         cmp     r0, r1                  /* don't reloc during debug         */

         beq     clear_bss          /* stack_setup */

 

         ldr    r2, _armboot_start

         ldr    r3, _bss_start

         sub   r2, r3, r2           /* r2 <- size of armboot            */

#if 1

         bl      CopyCode2Ram

#else

         add  r2, r0, r2           /* r2 <- source end address         */

 

copy_loop:

         ldmia        r0!, {r3-r10}               /* copy from source address [r0]    */

         stmia        r1!, {r3-r10}               /* copy to   target address [r1]    */

         cmp r0, r2                           /* until source end addreee [r2]    */

         ble    copy_loop

#endif

#endif       /* CONFIG_SKIP_RELOCATE_UBOOT */

 

 

clear_bss:

         ldr    r0, _bss_start           /* find start of bss segment        */

         ldr    r1, _bss_end             /* stop here                        */

         mov         r2, #0x00000000               /* clear                            */

 

boad/tender文件夹下面创建文件boot_init.c文件,用于实现从NandFlsh读取字节;判断启动方式,并拷贝启动代码到内存。

#include <common.h>

#include <s3c2440.h>

 

#define BUSY   1

 

#ifdef NAND_LARGEPAGE

#define NAND_SECTOR_SIZE  2048

#else

#define     NAND_SECTOR_SIZE       512

#endif

#define     NAND_BLOCK_MASK                (NAND_SECTOR_SIZE-1)

 

void nand_init_ll(void);

void nand_read_ll(unsigned char *buf, unsigned long start_addr, int size);

 

static void nand_reset(void);

static void wait_idle(void);

static void nand_select_chip(void);

static void nand_deselect_chip(void);

static void write_cmd(int cmd);

static void write_addr(unsigned int addr);

static unsigned char read_data(void);

 

static void nand_reset(void)

{

         nand_select_chip();

         write_cmd(0xff);

         wait_idle();

         nand_deselect_chip();

}

 

static void wait_idle(void)

{

         int i;

         S3C2440_NAND * s3c2440nand =(S3C2440_NAND *)0x4e000000;

         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;

         while(!(*p & BUSY))

         for(i=0;i<10;i++);

}

 

static void nand_select_chip(void)

{

         int i;

         S3C2440_NAND * s3c2440nand =(S3C2440_NAND *)0x4e000000;

         s3c2440nand->NFCONT &= ~(1<<1);

         for(i=0;i<10;i++);

}

 

static void nand_deselect_chip(void)

{

         S3C2440_NAND * s3c2440nand =(S3C2440_NAND *)0x4e000000;

         s3c2440nand->NFCONT |= (1<<1);

}

static void write_cmd(int cmd)

{

         S3C2440_NAND * s3c2440nand =(S3C2440_NAND *)0x4e000000;

         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;

         *p = cmd;

}

static void write_addr(unsigned int addr)

{

         int i;

         S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;

         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;

#ifndef NAND_LARGEPAGE   

         *p = addr & 0xff;

         for(i=0; i<10; i++);

         *p = (addr >> 9) & 0xff;

         for(i=0; i<10; i++);

         *p = (addr >> 17) & 0xff;

         for(i=0; i<10; i++);

         *p = (addr >> 25) & 0xff;

         for(i=0; i<10; i++);

#else

         int col, page;

         col = addr & NAND_BLOCK_MASK;

         page = addr / NAND_SECTOR_SIZE;

         *p = col & 0xff;   /* Column Address A0~A7 */

         for(i=0; i<10; i++); 

         *p = (col >> 8) & 0x0f;  /* Column Address A8~A11 */

         for(i=0; i<10; i++);

         *p = page & 0xff;   /* Row Address A12~A19 */

         for(i=0; i<10; i++);

         *p = (page >> 8) & 0xff; /* Row Address A20~A27 */

         for(i=0; i<10; i++);

         *p = (page >> 16) & 0x03; /* Row Address A28~A29 */

         for(i=0; i<10; i++);

#endif

}

 

/* 读取数据 */

static unsigned char read_data(void)

{

         S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;

         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;

         return *p;

}

 

 

void nand_init_ll(void)

{

         S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;

 

#define TACLS   0

#define TWRPH0  3

#define TWRPH1  0

 

         s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);

 

         s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);

 

         nand_reset();

}

 

 

void nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)

{

    int i, j;

    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {

        return ;

    }

 

    nand_select_chip();

    for(i=start_addr; i < (start_addr + size);) {

 

             write_cmd(0);

             /* Write Address */

             write_addr(i);

#ifdef NAND_LARGEPAGE

                   write_cmd(0x30);

#endif

                   wait_idle();

      for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {

          *buf = read_data();

          buf++;

      }

    }

 

    nand_deselect_chip();

    return ;

}

 

int bBootFrmNORFlash(void)

{

  volatile unsigned int *bwsCON = (volatile unsigned int *)0x48000000;

  unsigned int bwsVal;

  bwsVal = *bwsCON;

  bwsVal &= 0x06;

  if (bwsVal==0){

   return 0;

  }

  else {

   return 1;

  }

}

 

int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)

{

    unsigned int *pdwDest;

    unsigned int *pdwSrc;

    int i;

 

    if (bBootFrmNORFlash())

    {

      pdwDest = (unsigned int *)buf;

      pdwSrc  = (unsigned int *)start_addr;

 

      for (i = 0; i < size / 4; i++)

      {

        pdwDest[i] = pdwSrc[i];

      }

    }

    else

    {

 

   nand_init_ll();

 

      nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));

    }

 return 0;

}

board/smdk2440/Makefile 中添加 boot_Init.c 的编译选项,使他编译到u-boot中:

COBJS    := my2440.o flash.o boot_Init.o

还有一个重要的地方要修改,在 cpu/arm920t/u-boot.lds 中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码,主要目的是防止编译器把CopyCode2Ram的子函数放到4K之后,否则是无法启动的。如下:

.text :
{
      cpu/arm920t/start.o    (.text)
      
board/smdk2440/boot_Init.o (.text)
      board/smdk2440/lowlevel_init.o (.text)    
      *(.text)
}

将环境变量存储到Nand Flash

从上面的启动信息看,有一个警告信息“*** Warning - bad CRC or NAND, using default environment”,这是因为没有将u-boot的环境变量保存nand中的缘故。u-boot在默认的情况下把环境变量都是保存到Nor Flash中的,所以要修改代码,让他保存到Nand中。修改 include/configs/smdk2440.h 文件:

/* 配置环境变量存放设置 */
#define CFG_ENV_ADDR  (CFG_FLASH_BASE + 0x100000) /*
使用NorFlash时,环境变量存放的开始地址 */
//#define  CFG_ENV_IS_IN_FLASH 1                    /*
环境变量保存的位置在NorFlash  */
#define CFG_ENV_IS_IN_NAND 1                        /* 
环境变量保存的位置在NandFlash  */
#define CFG_ENV_OFFSET 0x60000                   /*
使用NandFlash时,环境变量存放的偏移地址 */
#define CFG_ENV_SIZE  0x20000               /*Total Size of Environment Sector
;一般设定为NandFlash的一个Sector的大小*/


4、最后编译u-boot,生成u-boot.bin文件。将u-boot.bin下载到开发板的Nand Flash中,再把开发板调到Nand启动档,保存环境变量后重启开发板,那条警告信息现在没有了。

 

 

DM9000的支持

首先修改 include/configs/tender.h ,在文件中加入这些宏的定义:

/* * Hardware drivers */  屏蔽u-boot默认对CS8900网卡的支持
//#define CONFIG_DRIVER_CS8900 1 /* we have a CS8900 on-board */
//#define CS8900_BASE          0x19000300
//#define CS8900_BUS16         1 /* the Linux driver does accesses as shorts */
 
//
添加u-bootDM9000X网卡的支持
#define CONFIG_DRIVER_DM9000    1 
 
#define CONFIG_DM9000_NO_SROM   1
#define CONFIG_DM9000_BASE      0x20000300  //
网卡片选地址
#define DM9000_IO               CONFIG_DM9000_BASE
#define DM9000_DATA             (CONFIG_DM9000_BASE + 4)   //
网卡数据地址
#define CONFIG_DM9000_USE_16BIT 1
注意:
u-boot-2009.08
可以自动检测DM9000网卡的位数,根据开发板原理图可知网卡的数据位为16位,并且网卡位于CPUBANK4上,所以只需在 board/samsung/my2440/lowlevel_init.S中设置 #define B4_BWSCON (DW16) 即可,不需要此处的 #define CONFIG_DM9000_USE_16BIT 1
 
//
u-boot加上ping命令,用来测试网络通不通
#define CONFIG_COMMANDS /

(CONFIG_CMD_DFL | /

            CFG_CMD_CACHE   | /

            CFG_CMD_NAND    | /

            CFG_CMD_PING    | /
 
//
恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址
#define CONFIG_ETHADDR   08:00:3e:26:0a:5b  //开发板MAC地址
#define CONFIG_NETMASK   255.255.255.0
#define CONFIG_IPADDR    192.168.2.100      //
开发板IP地址
#define CONFIG_SERVERIP  192.168.2.101      //TFTP
主机IP地址

修改MD9000网卡驱动代码 drivers/net/dm9000x.c

//注释掉eth_init函数中的这一部分,不然使用网卡的时候会报“could not establish link”的错误
i = 0;
while (!(phy_read(1) & 0x20)) {    /* autonegation complete bit */
 udelay(1000);
 i++;
 if (i == 10000) {
  printf("could not establish link ");
  return 0;
 }
}

303行左右把下面这段也注释掉

#if 0

         for (i = 0; i < 6; i++)

                   ((u16 *) bd->bi_enetaddr)[i] = read_srom_word(i);

#endif

 

323行左右把下面这段也注释掉

#if 0

         i = 0;

         while (!(phy_read(1) & 0x20)) {        /* autonegation complete bit */

                   udelay(1000);

                   i++;

                   if (i == 10000) {

                            printf("could not establish link/n");

                            return 0;

                   }

         }

#endif

 
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值