移植U-Boot.1.3.2到S3C2440


 首先,U-Boot1.3.2还没有支持s3c2440,移植仍是用2410的文件稍作修改而成的。
对于1.3.2和1.2.0的差别,我初看了一下,(对于ARM920T内核)应该是增加了对ATMEL公司的AT91RM9200系列处理器的支持。至 于S3C24X0系列的芯片,原理上并没有什么大的变化。

一、在U-Boot中建立自己的开发板类型,并测试编译。
我为开发板取名叫: dumu
 
0 先下载u-boot-1.3.2.tar.bz2,源码在ftp://ftp.denx.de/pub/u-boot/ 下载, 下载后在工作目录下解压U-oot。
 [ddbing@ARM9-Host working]$ tar -xjvf u-boot-1.3.2.tar.bz2  
 
1 进入U-Boot目录,修改Makefile (我在ununtu10.04 下,本人比较喜欢使用vim)
[ddbing@ARM9-Host working]$ cd u-boot-1.3.2
[ddbing@ARM9-Host u-boot-1.3.2]$ vim Makefile
#为 dumu2440建立编译项
2407 smdk2410_config :       unconfig
2408         @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
2409
2410 #add dudebing
2411 dumu244_c0onfig :       unconfig
2412         @$(MKCONFIG) $(@:_config=) arm arm920t dmu2440 dumu s3c24x0
2413 #end

各项的意思如下:
arm: CPU的架构(ARCH)
arm920t: CPU的类型(CPU),其对应于cpu/arm920t子目录。
dumu2440: 开发板的型号(BOARD),对应于board/dumu/dumu2440目录。
dumu: 开发者/或经销商(vender)。
s3c24x0: 片上系统(SOC)。
 
我的开发工具是:arm-linux-gcc-4.3.2,下载在frinendARM的官网下载。解压到,/opt/arm下。
再次修改Makefile,如下,红色为修改:
ifeq ($(ARCH),arm)
 144 #CROSS_COMPILE = arm-linux-
 145 CROSS_COMPILE = /opt/arm/usr/local/arm/4.3.2/bin/arm-none-linux-gnueabi-
 146 endif
然后执行,make编译。看是否有错,若无错,再往下工作。

2 在/board子目录中建立自己的开发板dumu2440目录
由于 我在上一步板子的开发者/或经销商(vender)中填了 dumu ,所以开发板dumu2440目录一定要建在/board子目录中的dumu目录下 ,否则编译会出错。
[ddbing@ARM9-Host u-boot-1.3.4]$ cd board
[ddbing@ARM9-Host board]$ mkdir dumu dumu/dumu2440
[ddbing@ARM9-Host board]$ cp -arf sbc2410x/* dumu/dumu2440/
[ddbing@ARM9-Host board]$ cd dumu/dumu2440/
[ddbing@ARM9-Host dumu2440]$ mv sbc2410x.c dumu2440.c
  
 还 要记得修改自己的开发板 dumu 2440目录下的Makefile文件,不然编译时会出错:
[ddbing@ARM9-Host dumu2440]$ vim Makefile
COBJS    := dumu 2440.o flash.o

最后记得make 一下,成功了,再往下执行。否则要找到错后再往下改,不要把错误堆到一起。

3 在include/configs/中建立配置头文件
[ddbing@ARM9-Host dumu2440]$ cd ../../..
[ddbing@ARM9-Host u-boot-1.3.4]$ cp include/configs/sbc2410x.h include/configs/dumu2440.h
 
4 测试编译能否成功
  1、配置
[ddbing@ARM9-Host u-boot-1.3.4]$ make dumu2440_config
Configuring for dumu2440 board...
可能 出现的问题:
     (1) 如果出现:
      $ make
dumu 2440_config
      Makefile:1927: *** 遗漏分隔符 。 停止。
      请在U-boot的根目录下的Makefile的
        @$(MKCONFIG) $(@:_config=) arm arm920t
dumu 2440 dumu )
       前加上“Tab”键

2、测试编译
[ddbing@ARM9-Host u-boot-1.3.4]$make
测试通过后进行 下一步


二、修 改U-Boot中的文件,以同时匹配2440和2410。

 1 修改/cpu/arm920t/start.S

# include < config. h>
# include < version. h>
# include < status_led. h>     /* 。对于s3c2440V4有4个LED指示灯,我用来指示程序用行的进度。主要是调试使用,可以检测cpu工作的进度 */
. . . . . .
/*
 * the actual start code
 */


start_code:
    /*
序用行的进度。*/
     * set the cpu to SVC32 mode
     */

    mrs    r0, cpsr
    bic    r0, r0, # 0x1f
    orr    r0, r0, # 0xd3
    msr    cpsr, r0

    bl coloured_LED_init
    bl red_LED_on

 (0) 修改寄存器地址定义

# if defined( CONFIG_S3C2400) | | defined( CONFIG_S3C2410) | | defined( CONFIG_S3C2440)
    /* turn off the watchdog */

# if defined( CONFIG_S3C2400)
# define pWTCON        0x15300000
# define INTMSK        0x14400008    /* Interupt-Controller base addresses */
# define CLKDIVN    0x14800014    /* clock divisor register */
# else
# define pWTCON        0x53000000
# define INTMSK        0x4A000008    /* Interupt-Controller base addresses */
# define INTSUBMSK    0x4A00001C
# define CLKDIVN    0x4C000014    /* clock divisor register */
# endif
# define CLK_CTL_BASE 0x4C000000 /*
dumu */
# define MDIV_405 0x7f < < 12 /*
dumu */
# define PSDIV_405 0x21 /*
dumu */
# define MDIV_200 0xa1 < < 12 /*
dumu */
# define PSDIV_200 0x31 /*
dumu */
    
. . . . . .

(1) 修改中断禁止部分

# if defined( CONFIG_S3C2410)
    ldr r1, = 0x7 ff
/*根据2410芯片手 册,INTSUBMSK有11位可用,
                       vivi也是0x7ff,U-Boot一直没改过来。*/

    ldr r0, = INTSUBMSK
    str r1, [ r0]
# endif
# if defined( CONFIG_S3C2440)
    ldr r1, = 0x7fff /*根据2440芯片手册,INTSUBMSK有15位可用*/
    ldr r0, = INTSUBMSK
    str r1, [ r0]

# endif

(2) 修改时钟设置(2440的主频为405MHz。)

# if defined( CONFIG_S3C2440)
    /* FCLK:HCLK:PCLK = 1:4:8 */
    ldr r0, = CLKDIVN
    mov r1, # 5
    str r1, [ r0]
    
    mrc p15, 0, r1, c1, c0, 0 /*read ctrl register
dumu*/
    orr r1, r1, # 0xc0000000 /*Asynchronous
dumu*/
    mcr p15, 0, r1, c1, c0, 0 /*write ctrl register
dumu*/

    /*now, CPU clock is 405.00 Mhz
dumu*/
    mov r1, # CLK_CTL_BASE /*
dumu*/
    mov r2, # MDIV_405 /* mpll_405mhz
dumu*/
    add r2, r2, # PSDIV_405 /* mpll_405mhz
dumu*/
    str r2, [ r1, # 0x04] /* MPLLCON
dumu */

# else
    /* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 12 MHz ! 在这里U-Boot有一个错误:以为默认时钟为120MHz。其实如果没有添加以下设置FCLK的语句,芯片内部的PLL是无效的,即FCLK为 12MHz。S3C24x0的芯片手册说得很明白。我一开始没有注意到这一点,是 CalmArrow 提醒了我并和我讨论过,他也做过实验证实了这点。在这里对CalmArrow表示感谢和敬意 !*/
    ldr    r0, = CLKDIVN
    mov    r1, # 3
    str    r1, [ r0]

    mrc p15, 0, r1, c1, c0, 0 /*read ctrl register
dumu*/
    orr r1, r1, # 0xc0000000 /*Asynchronous
dumu*/
    mcr p15, 0, r1, c1, c0, 0 /*write ctrl register
dumu*/

    /*now, CPU clock is 202.8 Mhz
dumu*/
    mov r1, # CLK_CTL_BASE /*
dumu*/
    mov r2, # MDIV_200 /* mpll_200mhz
dumu*/
    add r2, r2, # PSDIV_200 /* mpll_200mhz
dumu*/
    str r2, [ r1, # 0x04]


# endif
# endif     /* CONFIG_S3C2400 || CONFIG_S3C2410|| CONFIG_S3C2440 */

 红色部分是我添加的。 

   (3) 将从Flash启动改成从NAND Flash启动。
在以下U-Boot的重定向语句段:

# ifdef     CONFIG_AT91RM9200

# 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 stack_setup

    ldr    r2, _armboot_start
    ldr    r3, _bss_start
    sub    r2, r3, r2        /* r2 <- size of armboot */
    add    r2, r0, r2        /* r2 <- source end address*/

copy_loop:
    ldmia     { r3- r10}         /* copy from source address [r0] */
    stmia     { r3- r10}         /* copy to target address [r1] */
    cmp    r0, r2            /* until source end addreee [r2] */
    ble    copy_loop
# endif     /*   */
# endif

的后面添加上:

#ifdef CONFIG_S3C2440_NAND_BOOT
    @ reset NAND
    mov r1, #NAND_CTL_BASE
    ldr r2, = ( ( 7< < 12) | ( 7< < 8) | ( 7< < 4) | ( 0< < 0) )
    str r2, [ r1, #oNFCONF]
    ldr r2, [ r1, #oNFCONF]

    ldr r2, = ( ( 1< < 4) | ( 0< < 1) | ( 1< < 0) ) @ Active low CE Control
    str r2, [ r1, #oNFCONT]
    ldr r2, [ r1, #oNFCONT]

    ldr r2, = ( 0x6) @ RnB Clear
    str r2, [ r1, #oNFSTAT]
    ldr r2, [ r1, #oNFSTAT]
    
    mov r2, #0xff @ RESET command
    strb r2, [ r1, #oNFCMD]

  mov r3, #0 @ wait
nand1:
  add r3, r3, #0x1
  cmp r3, #0xa
  blt nand1

nand2:
  ldr r2, [ r1, #oNFSTAT] @ wait ready
  tst r2, #0x4
  beq nand2


    ldr r2, [ r1, #oNFCONT]
    orr r2, r2, #0x2 @ Flash Memory Chip Disable
    str r2, [ r1, #oNFCONT]

@ get read to call C functions ( for nand_read( ) )
  ldr sp, DW_STACK_START @ setup stack pointer
  mov fp, #0 @ no previous frame, so fp= 0

@ copy U- Boot to RAM
  ldr r0, = TEXT_BASE
  mov r1, #0x0
  mov r2, #0x30000
  bl nand_read_ll
  tst r0, #0x0
  beq ok_nand_read

bad_nand_read:
loop2: b loop2 @ infinite loop


ok_nand_read:
@ verify
  mov r0, #0
  ldr r1, = TEXT_BASE
  mov r2, #0x400 @ 4 bytes * 1024 = 4K- bytes
go_next:
  ldr r3, [ r0] , #4
  ldr r4, [ r1] , #4
  teq r3, r4
  bne notmatch
  subs r2, r2, #4
  beq stack_setup
  bne go_next

notmatch:
loop3: b loop3 @ infinite loop

#endif @ CONFIG_S3C2440_NAND_BOOT

#ifdef CONFIG_S3C2410_NAND_BOOT
@ reset NAND
  mov r1, #NAND_CTL_BASE
  ldr r2, = 0xf830 @ initial value
  str r2, [ r1, #oNFCONF]
  ldr r2, [ r1, #oNFCONF]
  bic r2, r2, #0x800 @ enable chip
  str r2, [ r1, #oNFCONF]
  mov r2, #0xff @ RESET command
  strb r2, [ r1, #oNFCMD]


  mov r3, #0 @ wait
nand1:
  add r3, r3, #0x1
  cmp r3, #0xa
  blt nand1

nand2:
  ldr r2, [ r1, #oNFSTAT] @ wait ready
  tst r2, #0x1
  beq nand2

  ldr r2, [ r1, #oNFCONF]
  orr r2, r2, #0x800 @ disable chip
  str r2, [ r1, #oNFCONF]

@ get read to call C functions ( for nand_read( ) )
  ldr sp, DW_STACK_START @ setup stack pointer
  mov fp, #0 @ no previous frame, so fp= 0

@ copy U- Boot to RAM
  ldr r0, = TEXT_BASE
  mov r1, #0x0
  mov r2, #0x30000
  bl nand_read_ll
  tst r0, #0x0
  beq ok_nand_read

bad_nand_read:
loop2: b loop2 @ infinite loop


ok_nand_read:
@ verify
  mov r0, #0
  ldr r1, = TEXT_BASE
  mov r2, #0x400 @ 4 bytes * 1024 = 4K- bytes
go_next:
  ldr r3, [ r0] , #4
  ldr r4, [ r1] , #4
  teq r3, r4
  bne notmatch
  subs r2, r2, #4
  beq stack_setup
  bne go_next

notmatch:
loop3: b loop3 @ infinite loop

#endif @ CONFIG_S3C2410_NAND_BOOT


在“ldr    pc, _start_armboot”之前加入:

# if defined( CONFIG_ dumu2440_LED)
    @ LED1 on u- boot stage 1 is
    mov r1, # GPIO_CTL_BASE
    add r1, r1, # oGPIO_B
    ldr r2, = 0x155aa
    str r2, [ r1, # oGPIO_CON]
    mov r2, # 0xff
    str r2, [ r1, # oGPIO_UP]
    mov r2, # 0x1c0
    str r2, [ r1, # oGPIO_DAT]
# endif

修改目 的:如果看到只有LED1亮了,说明U-Boot的第一阶段已完成!

. align 2
DW_STACK_START: . word STACK_BASE+ STACK_SIZE- 4

2  在board/ dumu/ dumu2440加入NAND Flash读函数文件,拷贝vivi中的nand_read.c文件到此文件夹即可:

 

# include < config. h>

# define __REGb( x) ( * ( volatile unsigned char * ) ( x) )
# define __REGi( x) ( * ( volatile unsigned int * ) ( x) )
# define NF_BASE 0x4e000000


# if defined( CONFIG_S3C2440)

# define NFCONF __REGi( NF_BASE + 0x0)
# define NFCONT __REGi( NF_BASE + 0x4)
# define NFCMD __REGb( NF_BASE + 0x8)
# define NFADDR __REGb( NF_BASE + 0xC)
# define NFDATA __REGb( NF_BASE + 0x10)
# define NFSTAT __REGb( NF_BASE + 0x20)

//#define GPDAT __REGi(GPIO_CTL_BASE+oGPIO_F+oGPIO_DAT)


# define NAND_CHIP_ENABLE ( NFCONT & = ~ ( 1< < 1) )
# define NAND_CHIP_DISABLE ( NFCONT | = ( 1< < 1) )
# define NAND_CLEAR_RB ( NFSTAT | = ( 1< < 2) )
# define NAND_DETECT_RB { while ( ! ( NFSTAT& ( 1< < 2) ) ) ; }

# define BUSY 4
inline void wait_idle( void ) {
    while ( ! ( NFSTAT & BUSY) ) ;
    NFSTAT | = BUSY;
}

# define NAND_SECTOR_SIZE 512
# define NAND_BLOCK_MASK ( NAND_SECTOR_SIZE - 1)

/* low level nand read function */
int
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 - 1; /* invalid alignment */
    }

    NAND_CHIP_ENABLE;

    for ( i= start_addr; i < ( start_addr + size) ; ) {
        /* READ0 */
        NAND_CLEAR_RB;
        NFCMD = 0;

        /* Write Address */
        NFADDR = i & 0xff;
        NFADDR = ( i > > 9) & 0xff;
        NFADDR = ( i > > 17) & 0xff;
        NFADDR = ( i > > 25) & 0xff;

        NAND_DETECT_RB;

        for ( j= 0; j < NAND_SECTOR_SIZE; j+ + , i+ + ) {
            * buf = ( NFDATA & 0xff) ;
            buf+ + ;
        }
    }
    NAND_CHIP_DISABLE;
    return 0;
}
# endif

# if defined( CONFIG_S3C2410)

# define NFCONF __REGi( NF_BASE + 0x0)
# define NFCMD __REGb( NF_BASE + 0x4)
# define NFADDR __REGb( NF_BASE + 0x8)
# define NFDATA __REGb( NF_BASE + 0xc)
# define NFSTAT __REGb( NF_BASE + 0x10)
# define BUSY 1

inline void wait_idle( void ) {
    int i;
    while ( ! ( NFSTAT & BUSY) )
      for ( i= 0; i< 10; i+ + ) ;
}
/* low level nand read function */
int
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 - 1; /* invalid alignment */
    }
    /* chip Enable */
    NFCONF & = ~ 0x800;
    for ( i= 0; i< 10; i+ + ) ;
    for ( i= start_addr; i < ( start_addr + size) ; ) {
      /* READ0 */
      NFCMD = 0;
      /* Write Address */
      NFADDR = i & 0xff;
      NFADDR = ( i > > 9) & 0xff;
      NFADDR = ( i > > 17) & 0xff;
      NFADDR = ( i > > 25) & 0xff;
      wait_idle( ) ;
      for ( j= 0; j < NAND_SECTOR_SIZE; j+ + , i+ + ) {
 * buf = ( NFDATA & 0xff) ;
 buf+ + ;
      }
    }
    /* chip Disable */
    NFCONF | = 0x800; /* chip disable */
    return 0;
}
# endif

3 修改board/ dumu/ dumu2440/Makefile文件

 

. . . . . .
OBJS : =
dumu2440. o nand_read. o flash. o
. . . . . .

4 修改include/configs/ dumu2440.h文件,添加如下内容:

 

. . . . . .
/*
 * Nandflash Boot
 */

# define STACK_BASE 0x33f00000
# define STACK_SIZE 0x8000
//#define UBOOT_RAM_BASE 0x33f80000

/* NAND Flash Controller */
# define NAND_CTL_BASE 0x4E000000
# define bINT_CTL( Nb) __REG( INT_CTL_BASE + ( Nb) )
/* Offset */
# define oNFCONF 0x00

# if defined( CONFIG_S3C2440)
# define CONFIG_S3C2440_NAND_BOOT 1
/* Offset */
# define oNFCONT 0x04
# define oNFCMD 0x08
# define oNFADDR 0x0c
# define oNFDATA 0x10
# define oNFSTAT 0x20
# define oNFECC 0x2c
# define rNFCONF ( * ( volatile unsigned int * ) 0x4e000000)
# define rNFCONT ( * ( volatile unsigned int * ) 0x4e000004)
# define rNFCMD ( * ( volatile unsigned char * ) 0x4e000008)
# define rNFADDR ( * ( volatile unsigned char * ) 0x4e00000c)
# define rNFDATA ( * ( volatile unsigned char * ) 0x4e000010)
# define rNFSTAT ( * ( volatile unsigned int * ) 0x4e000020)
# define rNFECC ( * ( volatile unsigned int * ) 0x4e00002c)

# if defined( CONFIG_
dumu2440_LED)
/* GPIO */
# define GPIO_CTL_BASE 0x56000000
# define oGPIO_B 0x10
# define oGPIO_CON 0x0 /* R/W, Configures the pins of the port */
# define oGPIO_DAT 0x4 /* R/W, Data register for port */
# define oGPIO_UP 0x8 /* R/W, Pull-up disable register */


# endif

# if defined( CONFIG_S3C2410)
# define CONFIG_S3C2410_NAND_BOOT 1
/* Offset */
# define oNFCONF 0x00
# define oNFCMD 0x04
# define oNFADDR 0x08
# define oNFDATA 0x0c
# define oNFSTAT 0x10
# define oNFECC 0x14
# define rNFCONF ( * ( volatile unsigned int * ) 0x4e000000)
# define rNFCMD ( * ( volatile unsigned char * ) 0x4e000004)
# define rNFADDR ( * ( volatile unsigned char * ) 0x4e000008)
# define rNFDATA ( * ( volatile unsigned char * ) 0x4e00000c)
# define rNFSTAT ( * ( volatile unsigned int * ) 0x4e000010)
# define rNFECC ( * ( volatile unsigned int * ) 0x4e000014)
# define rNFECC0 ( * ( volatile unsigned char * ) 0x4e000014)
# define rNFECC1 ( * ( volatile unsigned char * ) 0x4e000015)
# define rNFECC2 ( * ( volatile unsigned char * ) 0x4e000016)
# endif

/*JFFS2 Support  */
#undef CONFIG_JFFS2_CMDLINE
#define CONFIG_JFFS2_NAND 1
#define CONFIG_JFFS2_DEV  "nand0"
#define CONFIG_JFFS2_PART_SIZE  0x4c0000
#define CONFIG_JFFS2_PART_OFFSET 0x40000
/*JFFS2 Support  */

/* USB Support 080218 */
#define CONFIG_USB_OHCI
#define CONFIG_USB_STORAGE
#define CONFIG_USB_KEYBOARD
#define CONFIG_DOS_PARTITION
#define CFG_DEVICE_DEREGISTER
#define CONFIG_SUPPORT_VFAT
#define LITTLEENDIAN
/* USB Support 080218 */


# endif /* __CONFIG_H */

5 修改board/ dumu/ dumu2440/lowlevel_init.S文件
    依照开发板的内存区的配置情况, 修改board/dumu/dumu2440/lowlevel_init.S文件,我利用友善之臂提供的vivi源码里的信息做了如下 更改:

. . . . . .
# define B1_BWSCON        ( DW16)

# if defined( CONFIG_DRIVER_NE2000)
# define B2_BWSCON         ( DW16 + UBLB)
# e lse
# define B2_BWSCON        ( DW16)
# endif

# define B3_BWSCON        ( DW16 + WAIT + UBLB)
# define B4_BWSCON        ( DW16)
# define B5_BWSCON        ( DW16)
# define B6_BWSCON        ( DW32)
# define B7_BWSCON        ( DW32)
. . . . . .
# i f defined( CONFIG_DRIVER_NE2000)
# define B2_Tacs             0x3    /* 4clk
dumu*/
# define B2_Tcos             0x3    /* 4clk
dumu*/
# define B2_Tacc             0x7    /* 14clk */
# define B2_Tcoh             0x3    /* 4clk
dumu*/
# define B2_Tah             0x3    /* 4clk
dumu*/
# define B2_Tacp             0x3    /* 6clk
dumu*/
# define B2_PMC             0x0    /* normal */
# else
# define B2_Tacs            0x0
# define B2_Tcos            0x0
# define B2_Tacc            0x7
# define B2_Tcoh            0x0
# define B2_Tah            0x0
# define B2_Tacp            0x0
# define B2_PMC            0x0
# endif
……

/* REFRESH parameter */
# define REFEN            0x1    /* Refresh enable */
# define TREFMD            0x0    /* CBR(CAS before RAS)/Auto refresh */
# define Trc            0x3    /* 7clk */
# define Tchr            0x2    /* 3clk */

# if defined( CONFIG_S3C2440)
# define Trp            0x2    /* 4clk */
# define REFCNT            1012
# else

# define Trp            0x0    /* 2clk */
# define REFCNT            0x0459
# endif

6 修改/board/dumu/dumu2440/dumu 2440.c
 修改其对GPIO和PLL的配置(请参阅开发板的硬件说明和芯片手册):

. . . . . .
# define FCLK_SPEED 1

# 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        

# if defined( CONFIG_S3C2410)
/* Fout = 202.8MHz */
# define M_MDIV    0xA1
# define M_PDIV    0x3
# define M_SDIV    0x1
# endif

# if defined( CONFIG_S3C2440)
/* Fout = 405MHz */
# define M_MDIV 0x7f        
# define M_PDIV 0x2
# define M_SDIV 0x1
# endif

# define USB_CLOCK 1

# if USB_CLOCK= = 0
# define U_M_MDIV    0xA1
# define U_M_PDIV    0x3
# define U_M_SDIV    0x1
# elif USB_CLOCK= = 1

# if defined( CONFIG_S3C2410)
# define U_M_MDIV    0x48
# define U_M_PDIV    0x3
# endif

# if defined( CONFIG_S3C2440)
# define U_M_MDIV 0x38
# define U_M_PDIV 0x2
# endif


# define U_M_SDIV    0x2
# endif
. . . . . .
/* set up the I/O ports */
gpio- > GPACON = 0x007FFFFF;
# if defined( CONFIG_
dumu2440_LED)
    gpio- > GPBCON = 0x00055556;
# else

    gpio- > GPBCON = 0x00044556;
# endif
. . . . . .

# if defined( CONFIG_S3C2410)
    /* arch number of SMDK2410-Board */
    gd- > bd- > bi_arch_number = MACH_TYPE_SMDK2410;
# endif

# if defined( CONFIG_S3C2440)
    /* arch number of S3C2440 -Board */
    gd- > bd- > bi_arch_number = MACH_TYPE_S3C2440 ;
# endif

/* adress of boot parameters */
gd- > bd- > bi_boot_params = 0x30000100;
icache_enable( ) ;
dcache_enable( ) ;
# if defined( CONFIG_
dumu2440_LED)
    gpio- > GPBDAT = 0x180;
//
dumu 这里可有可无,不会影响uboot的正常工作。


    
//int board_init (void)设置完成后,LED1和LED2会亮起!

# endif
return 0;
}

7 为了实现NAND Flash的读写,再次修改/include/configs/ dumu2440.h
(请格外注意: 如果编译时报错,在Linux下用 vim等有高亮显示的文本编辑器看看文件的注释是不是为注释应有的颜色( vim中为灰色),如果不是,则将 注释删除。因为#define后面的注释被认为是程序的一部分。建议注释和#define分行写)

. . . . . .
/*
 * High Level Configuration Options
 * (easy to change)
 */

# define CONFIG_ARM920T 1 /* This is an ARM920T Core */
// #define    CONFIG_S3C2410         1    /* in a SAMSUNG S3C2410 SoC */

// #define CONFIG_SBC2410X         1    /* on a friendly-arm SBC-2410X Board */

# define CONFIG_S3C2440 1 /* in a SAMSUNG S3C2440 SoC */
# define CONFIG_
dumu2440 1 /* on a SAMSUNG dumu2440 Board */
# define CONFIG_
dumu2440_LED 1 /* Use the LED on Board */
. . . . . .
/*
 * Command line configuration.
 */

# include < config_cmd_default. h>

# define CONFIG_CMD_ASKENV
# define CONFIG_CMD_CACHE
# define CONFIG_CMD_DATE
# define CONFIG_CMD_DHCP
# define CONFIG_CMD_ELF
# define CONFIG_CMD_PING
# define CONFIG_CMD_NAND
# define CONFIG_CMD_REGINFO

#define CONFIG_CMD_JFFS2

/* JFFS2 Support    080218  */

#define CONFIG_CMD_USB

/* USB Support  080218  */

#define CONFIG_CMD_FAT

/* FAT support  080218  */

. . . . . .
# define CFG_LONGHELP
/* undef to save memory */
# define CFG_PROMPT "[ dumu2440]#"
/*Monitor Command Prompt */
# define CFG_CBSIZE 256
/* Console I/O Buffer Size */
. . . . . .
# define CFG_LOAD_ADDR 0x30008000
 /* default load address */

. . . . . .

/* timeout values are in ticks */
# define CFG_FLASH_ERASE_TOUT    ( 5* CFG_HZ) /* Timeout for Flash Erase */
# define CFG_FLASH_WRITE_TOUT    ( 5* CFG_HZ) /* Timeout for Flash Write */

# define CFG_ENV_IS_IN_NAND 1
# define CFG_ENV_OFFSET 0X30000
# define CFG_NAND_LEGACY

// #define    CFG_ENV_IS_IN_FLASH    1

# define CFG_ENV_SIZE        0x10000    /* Total Size of Environment Sector */

/*----------------------------------------------------------------------
 * NAND flash settings
 */

# if defined( CONFIG_CMD_NAND)
# define CFG_NAND_BASE 0x4E000000
/* NandFlash控制器在SFR区起始寄存器地址 */
# define CFG_MAX_NAND_DEVICE 1
 /* 支持的最在Nand Flash数据 */
# define SECTORSIZE 512
/* 1页的大小 */
# define NAND_SECTOR_SIZE SECTORSIZE
# define NAND_BLOCK_MASK 511

/* 页掩码 */
# define ADDR_COLUMN 1
/* 一个字节的Column地址 */
# define ADDR_PAGE 3
/* 3字节的页块地址!!!!!*/
# define ADDR_COLUMN_PAGE 4

/* 总共4字节的页块地址!!!!! */
# define NAND_ChipID_UNKNOWN 0x00
/* 未知芯片的ID号 */
# define NAND_MAX_FLOORS 1
# define NAND_MAX_CHIPS 1
/* Nand Flash命令层底层接口函数 */
# define WRITE_NAND_ADDRESS( d, adr) { rNFADDR = d; }
# define WRITE_NAND( d, adr) { rNFDATA = d; }
# define READ_NAND( adr) ( rNFDATA)
# define NAND_WAIT_READY( nand) { while ( ! ( rNFSTAT& ( 1< < 0) ) ) ; }
# define WRITE_NAND_COMMAND( d, adr) { rNFCMD = d; }
# define WRITE_NAND_COMMANDW( d, adr)     NF_CmdW( d)

# if defined( CONFIG_S3C2440)
# define NAND_DISABLE_CE( nand) { rNFCONT | = ( 1< < 1) ; }
# define NAND_ENABLE_CE( nand) { rNFCONT & = ~ ( 1< < 1) ; }
# endif
# if defined( CONFIG_S3C2410)
# define NAND_DISABLE_CE( nand) { rNFCONF | = ( 1< < 11) ; }
# define NAND_ENABLE_CE( nand) { rNFCONF & = ~ ( 1< < 11) ; }
# endif

/* the following functions are NOP's because S3C24X0 handles this in hardware */

# define NAND_CTL_CLRALE( nandptr)
# define NAND_CTL_SETALE( nandptr)
# define NAND_CTL_CLRCLE( nandptr)
# define NAND_CTL_SETCLE( nandptr)
/* 允许Nand Flash写校验 */
# define CONFIG_MTD_NAND_VERIFY_WRITE 1
. . . . . .
# endif /* __CONFIG_H */

8、在个文件中添加“CONFIG_S3C2440”, 使得原来s3c2410的代码可以编译进来。

(1)/include /common.h文件的第474行:

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

(2)/include/s3c24x0.h文件的 第85、95、99、110、148、404行:
“#ifdef CONFIG_S3C2410” 改为

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


顺便在其中加入2440 的NAND FLASH 寄存器定义(第160行附近)和CAMDIVN定义(第128行附近):

. . . . . .
typedef struct {
         S3C24X0_REG32 LOCKTIME;
         S3C24X0_REG32 MPLLCON;
         S3C24X0_REG32 UPLLCON;
         S3C24X0_REG32 CLKCON;
         S3C24X0_REG32 CLKSLOW;
         S3C24X0_REG32 CLKDIVN;
# if defined ( CONFIG_S3C2440)
         S3C24X0_REG32 CAMDIVN;
# endif
} /*__attribute__((__packed__))*/ S3C24X0_CLOCK_POWER;
. . . . . .
# if defined( CONFIG_S3C2410)

/* NAND FLASH (see S3C2410 manual chapter 6) */
typedef struct {
         S3C24X0_REG32 NFCONF;
         S3C24X0_REG32 NFCMD;
         S3C24X0_REG32 NFADDR;
         S3C24X0_REG32 NFDATA;
         S3C24X0_REG32 NFSTAT;
         S3C24X0_REG32 NFECC;
} /*__attribute__((__packed__))*/ S3C2410_NAND;
# endif
# if defined ( CONFIG_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 NFMECC0;
         S3C24X0_REG32 NFMECC1;
         S3C24X0_REG32 NFSECC;
         S3C24X0_REG32 NFSTAT;
         S3C24X0_REG32 NFESTAT0;
         S3C24X0_REG32 NFESTAT1;
         S3C24X0_REG32 NFECC;
} /*__attribute__((__packed__))*/ S3C2410_NAND;
# endif

(3)/cpu /arm920t/s3c24x0/interrupts.c文件的第33行:

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

第38行:

# elif defined( CONFIG_S3C2410) | | defined ( CONFIG_S3C2440)

在个文件中添加“defined(CONFIG_ dumu2440) ”, 使得原来SBC2410X的代码可以编译进来。第181行:

# elif defined( CONFIG_SBC2410X) | | /
      defined( CONFIG_SMDK2410) | | /
      defined( CONFIG_VCMA9) | | defined( CONFIG_
dumu2440)

4)/cpu /arm920t/s3c24x0/serial.c文件的第22行:

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

第26行:

# elif defined( CONFIG_S3C2410) | | defined ( CONFIG_S3C2440)

(5)/cpu /arm920t/s3c24x0/speed.c文件的第33行:

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

第37行:

# elif defined( CONFIG_S3C2410) | | defined ( CONFIG_S3C2440)

顺便修改源代码,以匹配s3c2440:

static ulong get_PLLCLK( int pllreg)
{
   . . . . . .

    m = ( ( r & 0xFF000) > > 12) + 8;
    p = ( ( r & 0x003F0) > > 4) + 2;
    s = r & 0x3;
//
dumu

# if defined( CONFIG_S3C2440)
   if ( pllreg = = MPLL)
    return ( ( CONFIG_SYS_CLK_FREQ * m * 2) / ( p < < s) ) ;
    else if ( pllreg = = UPLL)
# endif
//
dumu

    return ( ( CONFIG_SYS_CLK_FREQ * m) / ( p < < s) ) ;
}
. . . . . .
/* return FCLK frequency */
ulong get_FCLK( void )
{
    return ( get_PLLCLK( MPLL) ) ;
}
 
/* return HCLK frequency */
ulong get_HCLK( void )
{
    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER( ) ;
//
dumu

# if defined( CONFIG_S3C2440)
    if ( clk_power- > CLKDIVN & 0x6)
                            {
                            if ( ( clk_power- > CLKDIVN & 0x6) = = 2) return ( get_FCLK( ) / 2) ;
                            if ( ( clk_power- > CLKDIVN & 0x6) = = 6) return ( ( clk_power- > CAMDIVN & 0x100) ? get_FCLK( ) / 6 : get_FCLK( ) / 3) ;
                            if ( ( clk_power- > CLKDIVN & 0x6) = = 4) return ( ( clk_power- > CAMDIVN & 0x200) ? get_FCLK( ) / 8 : get_FCLK( ) / 4) ;
             return ( get_FCLK( ) ) ;
                            }
 
       else return ( get_FCLK( ) ) ;
# else

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

}
. . . . . .

(6)/cpu /arm920t/s3c24x0/usb_ohci.c文件的第45行:

# elif defined( CONFIG_S3C2410) | | defined ( CONFIG_S3C2440)

(7)drivers/rtc /s3c24x0_rtc.c文件的第35行:

# elif defined( CONFIG_S3C2410) | | defined ( CONFIG_S3C2440)

(8)/cpu/arm920t/s3c24x0/usb.c文件的第31行:

# elif defined( CONFIG_S3C2410) | | defined ( CONFIG_S3C2440)

(9)/cpu/arm920t/s3c24x0/i2c.c文件的第35行:

# elif defined( CONFIG_S3C2410) | | defined ( CONFIG_S3C2440)

第66、85、142、150、174行:
将“#ifdef CONFIG_S3C2410”改为

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

(10)drivers/usb /usb_ohci.c文件的第68行附近:


# if defined( CONFIG_ARM920T) | | /
    defined( CONFIG_S3C2400) | | /
    defined( CONFIG_S3C2410) | | /
    defined( CONFIG_S3C2440) | | /
    defined( CONFIG_440EP) | | /
    defined( CONFIG_PCI_OHCI) | | /
    defined( CONFIG_MPC5200)

9、在 include/linux/mtd/nand_ids.h的结构体nand_flash_ids加入

 

static struct nand_flash_dev nand_flash_ids[ ] = {
. . . . . .
    { "Samsung K9F1208U0B" , NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000, 0} ,
    { NULL , }
} ;

 修改include/linux/mtd/nand.h

/*
 * Constants for hardware specific CLE/ALE/NCE function
*/

# if 0

/* Select the chip by setting nCE to low */
# define NAND_CTL_SETNCE        1
/* Deselect the chip by setting nCE to high */
# define NAND_CTL_CLRNCE        2
/* Select the command latch by setting CLE to high */
# define NAND_CTL_SETCLE        3
/* Deselect the command latch by setting CLE to low */
# define NAND_CTL_CLRCLE        4
/* Select the address latch by setting ALE to high */
# define NAND_CTL_SETALE        5
/* Deselect the address latch by setting ALE to low */
# define NAND_CTL_CLRALE        6
/* Set write protection by setting WP to high. Not used! */
# define NAND_CTL_SETWP        7
/* Clear write protection by setting WP to low. Not used! */
# define NAND_CTL_CLRWP        8
# endif

10、修改/lib_arm中的 board.c, 实际开发可不用改这里,只是便于调试。

. . . . . .
# include < common. h>
# include < command. h>
# include < malloc . h>
# include < devices. h>
# include < version. h>
# include < net. h>
# include < s3c2410. h>
 
. . . . . .
 
static int display_banner ( void )
{
# if defined( CONFIG_
dumu2440_LED)     
    S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO( ) ;
         gpio- > GPBDAT = 0x100;
//
ddbing

//在串口初始化和 console初始化完成,串口输出信息之前,LED1、LED2、LED3会亮起!

# endif
    printf ( "/n/n%s/n/n" , version_string) ;
    debug ( "U-Boot code: %08lX -> %08lX BSS: -> %08lX/n" ,
           _armboot_start, _bss_start, _bss_end) ;
# ifdef CONFIG_MODEM_SUPPORT
    debug ( "Modem Support enabled/n" ) ;
# endif
# ifdef CONFIG_USE_IRQ
    debug ( "IRQ Stack: %08lx/n" , IRQ_STACK_START) ;
    debug ( "FIQ Stack: %08lx/n" , FIQ_STACK_START) ;
# endif

    return ( 0) ;
}
 
. . . . . .
void start_armboot ( void )
{
         init_fnc_t * * init_fnc_ptr;
         char * s;
# ifndef CFG_NO_FLASH
         ulong size;
# endif
# if defined( CONFIG_VFD) | | defined( CONFIG_LCD)
         unsigned long addr;
# endif
# if defined( CONFIG_
dumu2440_LED)     
         S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO( ) ;
# endif
. . . . . .
# if defined( CONFIG_
dumu2440_LED)     
         gpio- > GPBDAT = 0x0;
//
ddbing

//在进入命令提示符之 前,四个LED会同时亮起!

# endif
         /* main_loop() can return to retry autoboot, if so just run it again. */
         for ( ; ; ) {
                   main_loop ( ) ;
         }
 
         /* NOTREACHED - no way out of command loop except booting */
}
 

11、 修改common/env_nand.c

. . . . . .
# ifdef CONFIG_INFERNO
# error CONFIG_INFERNO not supported yet
# endif

int nand_legacy_rw ( struct nand_chip* nand, int cmd,
        size_t start, size_t len,
        size_t * retlen, u_char * buf) ;
extern struct nand_chip nand_dev_desc[ CFG_MAX_NAND_DEVICE] ;
extern int nand_legacy_erase( struct nand_chip * nand, size_t ofs, size_t len, int clean) ;

/* info for NAND chips, defined in drivers/nand/nand.c */
extern nand_info_t nand_info[ CFG_MAX_NAND_DEVICE] ;

. . . . . .

# else /* ! CFG_ENV_OFFSET_REDUND */
int saveenv( void )
{
    ulong total;
    int ret = 0;

    puts ( "Erasing Nand..." ) ;
    
// if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE))


if ( nand_legacy_erase( nand_dev_desc + 0, CFG_ENV_OFFSET, CFG_ENV_SIZE, 0) )
        return 1;



    puts ( "Writing to Nand... " ) ;
    total = CFG_ENV_SIZE;
    
// ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);

ret = nand_legacy_rw( nand_dev_desc + 0,
0x00 | 0x02, CFG_ENV_OFFSET, CFG_ENV_SIZE,
& total, ( u_char* ) env_ptr) ;


    if ( ret | | total ! = CFG_ENV_SIZE)
        return 1;

    puts ( "done/n" ) ;
    return ret;
. . . . . .
# else /* ! CFG_ENV_OFFSET_REDUND */
/*
 * The legacy NAND code saved the environment in the first NAND device i.e.,
 * nand_dev_desc + 0. This is also the behaviour using the new NAND code.
 */

void env_relocate_spec ( void )
{
# if ! defined( ENV_IS_EMBEDDED)
    ulong total;
    int ret;

    total = CFG_ENV_SIZE;
    
// ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);

    ret = nand_legacy_rw( nand_dev_desc + 0, 0x01 | 0x02, CFG_ENV_OFFSET, CFG_ENV_SIZE, & total, ( u_char* ) env_ptr) ;

. . . . . .

12、 在/board/ dumu/ dumu2440/ dumu2440.c文件的末尾添加对Nand Flash 的初始化函数(在后面Nand Flash的操作都要用到)
u-boot运行至 第二阶段进入start_armboot()函数。其中nand_init()函数是对nand flash的最初初始化函数。Nand_init()函数在两个文件中实现。其调用与CFG_NAND_LEGACY宏有关,如果没有定义这个宏,系统调 用 drivers/nand/nand.c中的nand_init(); 否则调用自己在board/
dumu/ tekkaman2440/ tekkaman2440.c中的nand_init()函数。这里我选择第二 种方式。

# if defined( CONFIG_CMD_NAND)
typedef enum {
    NFCE_LOW,
    NFCE_HIGH
} NFCE_STATE;

static inline void NF_Conf( u16 conf)
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    nand- > NFCONF = conf;
}


static inline void NF_Cmd( u8 cmd)
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    nand- > NFCMD = cmd;
}

static inline void NF_CmdW( u8 cmd)
{
    NF_Cmd( cmd) ;
    udelay( 1) ;
}

static inline void NF_Addr( u8 addr)
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    nand- > NFADDR = addr;
}


static inline void NF_WaitRB( void )
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    while ( ! ( nand- > NFSTAT & ( 1< < 0) ) ) ;
}

static inline void NF_Write( u8 data)
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    nand- > NFDATA = data;
}

static inline u8 NF_Read( void )
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    return ( nand- > NFDATA) ;
}

static inline u32 NF_Read_ECC( void )
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    return ( nand- > NFECC) ;
}

# if defined( CONFIG_S3C2440)
static inline void NF_Cont( u16 cont)
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    nand- > NFCONT = cont;
}

static inline void NF_SetCE( NFCE_STATE s)
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    switch ( s) {
    case NFCE_LOW:
        nand- > NFCONT & = ~ ( 1< < 1) ;
        break ;
    case NFCE_HIGH:
        nand- > NFCONT | = ( 1< < 1) ;
        break ;
    }
}

static inline void NF_Init_ECC( void )
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    nand- > NFCONT | = ( 1< < 4) ;
}

# else
static inline void NF_SetCE( NFCE_STATE s)
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    switch ( s) {
    case NFCE_LOW:
        nand- > NFCONF & = ~ ( 1< < 11) ;
        break ;
    case NFCE_HIGH:
        nand- > NFCONF | = ( 1< < 11) ;
        break ;
    }
}

static inline void NF_Init_ECC( void )
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;
    nand- > NFCONF | = ( 1< < 12) ;
}
# endif

extern ulong nand_probe( ulong physadr) ;

static inline void NF_Reset( void )
{
    int i;

    NF_SetCE( NFCE_LOW) ;
    NF_Cmd( 0xFF) ;         /* reset command */
    for ( i = 0; i < 10; i+ + ) ;     /* tWB = 100ns. */
    NF_WaitRB( ) ;         /* wait 200~500us; */
    NF_SetCE( NFCE_HIGH) ;
}

static inline void NF_Init( void )
{
# if 0
# define TACLS 0
# define TWRPH0 3
# define TWRPH1 0
# else
# define TACLS 0
# define TWRPH0 4
# define TWRPH1 2
# endif

# if defined( CONFIG_S3C2440)
    NF_Conf( ( TACLS< < 12) | ( TWRPH0< < 8) | ( TWRPH1< < 4) ) ;
    NF_Cont( ( 1< < 6) | ( 1< < 4) | ( 1< < 1) | ( 1< < 0) ) ;
# else
    NF_Conf( ( 1< < 15) | ( 0< < 14) | ( 0< < 13) | ( 1< < 12) | ( 1< < 11) | ( TACLS< < 8) | ( TWRPH0< < 4) | ( TWRPH1< < 0) ) ;
    /*nand->NFCONF = (1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); */
    /* 1 1 1 1, 1 xxx, r xxx, r xxx */
    /* En 512B 4step ECCR nFCE=H tACLS tWRPH0 tWRPH1 */
# endif
    NF_Reset( ) ;
}

void nand_init( void )
{
    S3C2410_NAND * const nand = S3C2410_GetBase_NAND( ) ;

    NF_Init( ) ;
# ifdef DEBUG
    printf ( "NAND flash probing at 0x%.8lX/n" , ( ulong) nand) ;
# endif
    printf ( "%4lu MB/n" , nand_probe( ( ulong) nand) > > 20) ;
}
# endif

三、交叉编译U-Boot。
在U-Boot的根目录下
$make


 这篇文章的移植使用CS8900网卡。tftp可以下载。后面把dm9000和usb下载的部分加上。

此篇主要参考http://blog.21ic.com/user1/1425/archives/2008/50775.html这篇文章,非常感谢!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值