接下来是为UBOOT增加驱动。
1) NOR FALSH:
TQ2440的NOR FLASH和UBOOT源文件中用到的AM29LV800相似,只需在/include/configs/TQ2440.h里作一些改动即可。具体参考网上教程。
2) 网卡:
网卡DM9000的驱动在UBOOT中已自带,首先在/include/configs/TQ2440.h中注释掉原网卡CS8900的宏。
添加DM9000的宏,添加CONFIG_CMD_PING以支持PING命令。
添加CONFIG_ETHADDR、CONFIG_NETMASK、CONFIG_IPADDR、CONFIG_SERVERIP等宏来设置网络参数(在UBOOT命令行中用setenv和setenv也可以设置)。
然后修改board/samsung/TQ2440/TQ2440.c的网卡初始化部分为:
1 int board_eth_init(bd_t *bis) 2 { 3 return dm9000_initialize(bis); 4 }
最后因为存在一点小BUG,还要在drivers/net/dm9000x.c里注释掉dm9000_init函数中的以下部分
1 #if 0 2 i = 0; 3 while (!(phy_read(1) & 0x20)) 4 { /* autonegation complete bit */ 5 udelay(1000); i++; 6 if (i == 10000) { 7 printf("could not establish link "); 8 return 0; 9 } 10 } #endif
以及注释掉dm9000_halt函数中的以下部分:
1 static void dm9000_halt(struct eth_device *netdev) 2 { 3 #if 0 4 DM9000_DBG("%sn", __func__); 5 /* RESET devie */ 6 phy_write(0, 0x8000); 7 /* PHY RESET */ 8 DM9000_iow(DM9000_GPR, 0x01); 9 /* Power‐Down PHY */ 10 DM9000_iow(DM9000_IMR, 0x80); 11 /* Disable all interrupt */ 12 DM9000_iow(DM9000_RCR, 0x00); 13 /* Disable RX */ 14 #endif 15 }
3) NAND FLASH
UBOOT的NAND驱动在目录drivers/mtd/nand下。
在目录下可以找到2410的NAND驱动s3c2410_nand.c,文件里面主要是s3c2410_dev_ready()、s3c2410_hwcontrol()、board_nand_init()三个函数。
所以只需要实现2440的这三个函数即可。创建一个s3c2440_nand.c文件,并在MAKEFILE里加上s3c2440_nand.o的目标。文件内容如下(代码是从别处复制的):
1 #include <common.h> 2 #if 0 3 #define DEBUGN 4 printf 5 #else 6 #define DEBUGN(x, args ...) {} 7 #endif 8 #include <nand.h> 9 #include <asm/arch/s3c24x0_cpu.h> 10 #include <asm/arch/s3c2410.h> 11 #include <asm/io.h> 12 #define __REGb(x) (*(volatile unsigned char *)(x)) 13 #define __REGi(x) (*(volatile unsigned int *)(x)) 14 15 #define NF_BASE 0x4e000000 16 #define NFCONF __REGi(NF_BASE + 0x0) 17 #define NFCONT __REGi(NF_BASE + 0x4) 18 #define NFCMD __REGb(NF_BASE + 0x8) 19 #define NFADDR __REGb(NF_BASE + 0xc) 20 #define NFDATA __REGb(NF_BASE + 0x10) 21 #define NFMECCD0 __REGi(NF_BASE + 0x14) 22 #define NFMECCD1 __REGi(NF_BASE + 0x18) 23 #define NFSECCD __REGi(NF_BASE + 0x1C) 24 #define NFSTAT __REGb(NF_BASE + 0x20) 25 #define NFSTAT0 __REGi(NF_BASE + 0x24) 26 #define NFSTAT1 __REGi(NF_BASE + 0x28) 27 #define NFMECC0 __REGi(NF_BASE + 0x2C) 28 #define NFMECC1 __REGi(NF_BASE + 0x30) 29 #define NFSECC __REGi(NF_BASE + 0x34) 30 #define NFSBLK __REGi(NF_BASE + 0x38) 31 #define NFEBLK __REGi(NF_BASE + 0x3c) 32 #define S3C2440_NFCONT_nCE (1<<1) 33 #define S3C2440_ADDR_NCLE 0x0c 34 #define S3C2440_ADDR_NALE 0x08 35 36 37 38 ulong IO_ADDR_W = NF_BASE; 39 static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) 40 { 41 struct nand_chip *chip = mtd->priv; 42 if (ctrl & NAND_CTRL_CHANGE) { 43 IO_ADDR_W = NF_BASE; 44 if (!(ctrl & NAND_CLE)) 45 IO_ADDR_W |= S3C2440_ADDR_NCLE; 46 if (!(ctrl & NAND_ALE)) 47 IO_ADDR_W |= S3C2440_ADDR_NALE; 48 if (ctrl & NAND_NCE) 49 NFCONT &= ~S3C2440_NFCONT_nCE; 50 else 51 NFCONT |= S3C2440_NFCONT_nCE; 52 } 53 if (cmd != NAND_CMD_NONE) 54 writeb(cmd,(void *)IO_ADDR_W); 55 } 56 static int s3c2440_dev_ready(struct mtd_info *mtd) 57 { 58 return (NFSTAT & 0x01); 59 } 60 int board_nand_init(struct nand_chip *nand) 61 { 62 u_int32_t cfg; 63 u_int8_t tacls, twrph0, twrph1; 64 struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); 65 writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON); 66 /* initialize hardware */ 67 twrph0 = 4; 68 twrph1 = 2; 69 tacls = 0; 70 cfg = ((tacls<<12)|(twrph0<<8)|(twrph1<<4)); 71 NFCONF=cfg; 72 cfg = ((1<<6)|(1<<4)|(0<<1)|(1<<0)); 73 NFCONT=cfg; 74 /* initialize nand_chip data structure */ 75 nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010; 76 /* read_buf and write_buf are default */ 77 /* read_byte and write_byte are default */ 78 /* hwcontrol always must be implemented */ 79 nand->cmd_ctrl = s3c2440_hwcontrol; 80 nand->dev_ready = s3c2440_dev_ready; 81 nand->ecc.mode = NAND_ECC_SOFT; 82 return 0; 83 }
然后在TQ2440.h里添加
1 #define CONFIG_CMD_NAND 2 #define CONFIG_CMDLINE_EDITING 3 #ifdef CONFIG_CMDLINE_EDITING 4 #undef CONFIG_AUTO_COMPLETE 5 #else 6 #define CONFIG_AUTO_COMPLETE 7 #endif 8 9 #if defined(CONFIG_CMD_NAND) 10 #define CONFIG_SYS_NAND_BASE 0x4E000000 11 #define CONFIG_SYS_MAX_NAND_DEVICE 1 12 #define CONFIG_MTD_NAND_VERIFY_WRITE 1 13 #define NAND_SAMSUNG_LP_OPTIONS 1 14 #endif
注意,我的TQ2440开发板上用的NAND型号是K9F2G08U0A,是Large Page,因此需要开启NAND_SAMSUNG_LP_OPTIONS选项,不是LP的话需要注释掉。
4) LCD
显示部分的驱动在drivers/video目录下。需要实现三个函数:lcd_enable()、video_hw_init()、video_set_lut()函数。
其中video_hw_init()最重要,负责初始化LCD的硬件,然后填充GraphicDevice结构体,向上层传递显示缓存的地址、大小,显示设备分辨率等参数。
在此目录下新建一个s3c2440_fb.c文件,拷贝以下代码:
1 #include <common.h> 2 #if defined(CONFIG_VIDEO_S3C2440) 3 #include <video_fb.h> 4 #include "videomodes.h" 5 #include <asm/arch/s3c24x0_cpu.h> 6 #include <asm/io.h> 7 /* 8 * Export Graphic Device 9 */ 10 GraphicDevice smi; 11 12 #define LCD_X_SIZE 240 13 #define LCD_Y_SIZE 320 14 #define VIDEO_MEM_SIZE (LCD_Y_SIZE*LCD_X_SIZE)//0x200000 15 #define MVAL (13) 16 #define MVAL_USED (0) //0=each frame 1=rate by MVAL 17 #define INVVDEN (1) //0=normal 1=inverted 18 #define BSWP (0) //Byte swap control 19 #define HWSWP (1) //Half word swap control 20 #define VBPD (12) 21 #define VFPD (4) 22 #define VSPW (5) 23 #define HBPD (22) 24 #define HFPD (33) 25 #define HSPW (44) 26 #define CLKVAL_TFT (6) 27 28 volatile unsigned short LCD_BUFFER[LCD_Y_SIZE][LCD_X_SIZE]; 29 30 void lcd_enable(void) 31 { 32 struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio(); 33 struct s3c24x0_lcd * const lcd = s3c24x0_get_base_lcd(); 34 gpio->GPGUP = gpio->GPGUP & ((~(1 << 4)) | (1 << 4)); 35 gpio->GPGCON = gpio->GPGCON & ((~( 3 << 8)) | ( 3 << 8)); 36 gpio->GPGDAT = gpio->GPGDAT | (1 << 4 ); 37 lcd->LCDCON5 = lcd->LCDCON5 & ((~( 1 << 3)) | (1 << 3)); // PWREN 38 lcd->LCDCON5 = lcd->LCDCON5 & ((~( 1 << 5)) | (0 << 5)); // INVPWREN 39 lcd->LCDCON1 |= 1; 40 gpio->GPBUP = gpio->GPBUP & ((~(1 << 1)) | (1 << 1)); 41 gpio->GPBCON = gpio->GPBCON & ((~( 3 << 2)) | ( 3 << 2)); 42 gpio->GPBDAT = gpio->GPBDAT | (1 << 1 ); 43 } 44 45 void *video_hw_init (void) 46 { 47 struct s3c24x0_lcd * const lcd = s3c24x0_get_base_lcd(); 48 GraphicDevice *pGD = (GraphicDevice *)&smi; 49 int videomode; 50 int bppmode; 51 unsigned long t1, hsynch, vsynch; 52 char *penv; 53 int tmp, i, bits_per_pixel; 54 struct ctfb_res_modes *res_mode; 55 struct ctfb_res_modes var_mode; 56 tmp = 0; 57 videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE; 58 59 /* get video mode via environment */ 60 if ((penv = getenv ("videomode")) != NULL) { 61 /* deceide if it is a string */ 62 if (penv[0] <= '9') { 63 videomode = (int) simple_strtoul (penv, NULL, 16); 64 tmp = 1; 65 } 66 } else { 67 tmp = 1; 68 } 69 if (tmp) { 70 /* parameter are vesa modes */ 71 /* search params */ 72 for (i = 0; i < VESA_MODES_COUNT; i++) { 73 if (vesa_modes[i].vesanr == videomode) 74 break; 75 } 76 if (i == VESA_MODES_COUNT) { 77 printf ("no VESA Mode found, switching to mode 0x%x ", 78 CONFIG_SYS_DEFAULT_VIDEO_MODE); 79 i = 0; 80 } 81 res_mode = (struct ctfb_res_modes *)&res_mode_init[vesa_modes[i].resindex]; 82 bits_per_pixel = vesa_modes[i].bits_per_pixel; 83 } else { 84 res_mode = (struct ctfb_res_modes *) &var_mode; 85 bits_per_pixel = video_get_params (res_mode, penv); 86 } 87 88 /* calculate hsynch and vsynch freq (info only) */ 89 t1 = (res_mode->left_margin + res_mode->xres + res_mode->right_margin + 90 res_mode->hsync_len) / 8; 91 t1 *= 8; 92 t1 *= res_mode->pixclock; 93 t1 /= 1000; 94 hsynch = 1000000000L / t1; 95 t1 *= (res_mode->upper_margin + res_mode->yres + res_mode->lower_margin + 96 res_mode->vsync_len); 97 t1 /= 1000; 98 vsynch = 1000000000L / t1; 99 /* fill in Graphic device struct */ 100 sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, 101 res_mode->yres, bits_per_pixel, (hsynch / 1000), (vsynch / 1000)); 102 pGD->winSizeX = res_mode->xres; 103 pGD->winSizeY = res_mode->yres; 104 pGD->plnSizeX = res_mode->xres; 105 pGD->plnSizeY = res_mode->yres; 106 107 switch (bits_per_pixel) 108 { 109 case 8: 110 pGD->gdfBytesPP = 1; 111 pGD->gdfIndex = GDF__8BIT_INDEX; 112 bppmode = 11; 113 break; 114 case 16: 115 pGD->gdfBytesPP = 2; 116 pGD->gdfIndex = GDF_16BIT_565RGB; 117 bppmode = 12; 118 break; 119 case 24: 120 pGD->gdfBytesPP = 3; 121 pGD->gdfIndex = GDF_24BIT_888RGB; 122 bppmode = 13; 123 break; 124 } 125 126 pGD->frameAdrs = &LCD_BUFFER[0][0];//LCD_VIDEO_ADDR; 127 pGD->memSize = VIDEO_MEM_SIZE; 128 lcd->LCDCON1 = (CLKVAL_TFT << 8) | (MVAL_USED << 7) | (3 << 5) | (bppmode << 1) | 0; 129 lcd->LCDCON2 = (VBPD << 24) | (pGD->winSizeY-1 << 14) | (VFPD << 6) | (VSPW); 130 lcd->LCDCON3 = (HBPD << 19) | (pGD->winSizeX-1 << 8) | (HFPD); 131 lcd->LCDCON4 = (MVAL << 8) | (HSPW); 132 lcd->LCDCON5 = (1 << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 7) | (0 << 6) | (1 << 3) | (BSWP << 1) | (HWSWP); 133 lcd->LCDINTMSK |= (3); 134 lcd->LPCSEL &= (~7); 135 lcd->TPAL = 0x0; 136 writel((pGD->frameAdrs >> 1), &lcd->LCDSADDR1); 137 /* This marks the end of the frame buffer. */ 138 writel((((readl(&lcd->LCDSADDR1))&0x1fffff)+(pGD->winSizeX+0)*pGD->winSizeY), &lcd->LCDSADDR2); 139 writel((pGD->winSizeX & 0x7ff), &lcd->LCDSADDR3); 140 141 /* Clear video memory */ 142 memset((void *)pGD->frameAdrs, 0, pGD->memSize); 143 144 lcd_enable(); 145 return ((void*)&smi); 146 } 147 void video_set_lut (unsigned int index, 148 /* color number */ 149 unsigned char r, /* red */ 150 unsigned char g, /* green */ 151 unsigned char b /* blue */ 152 ) 153 { 154 } 155 #endif /* CONFIG_VIDEO_S3C2440 */
往目录下的MAKEFILE文件里添加: COBJS-$(CONFIG_VIDEO_S3C2440) += s3c2440_fb.o videomodes.o
video目录下的videomodes.c文件的数组 ctfb_vesa_modes vesa_modes 和 ctfb_res_modes res_mode_init 列出了屏幕参数,然后video_hw_init函数根据环境变量指定的屏幕去寻找对应的配置参数(如果没有此变量就用默认设定,在TQ2440.h中),并用于初始化。
添加新参数后的videomodes.c部分代码如下(新增参数在最后):
1 const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = { 2 {0x301, RES_MODE_640x480, 8}, 3 {0x310, RES_MODE_640x480, 15}, 4 {0x311, RES_MODE_640x480, 16}, 5 {0x312, RES_MODE_640x480, 24}, 6 {0x303, RES_MODE_800x600, 8}, 7 {0x313, RES_MODE_800x600, 15}, 8 {0x314, RES_MODE_800x600, 16}, 9 {0x315, RES_MODE_800x600, 24}, 10 {0x305, RES_MODE_1024x768, 8}, 11 {0x316, RES_MODE_1024x768, 15}, 12 {0x317, RES_MODE_1024x768, 16}, 13 {0x318, RES_MODE_1024x768, 24}, 14 {0x161, RES_MODE_1152x864, 8}, 15 {0x162, RES_MODE_1152x864, 15}, 16 {0x163, RES_MODE_1152x864, 16}, 17 {0x307, RES_MODE_1280x1024, 8}, 18 {0x319, RES_MODE_1280x1024, 15}, 19 {0x31A, RES_MODE_1280x1024, 16}, 20 {0x31B, RES_MODE_1280x1024, 24}, 21 {0x213, RES_MODE_320x240, 16}, 22 }; 23 const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { 24 /* x y pixclk le ri up lo hs vs s vmode */ 25 {640, 480, 39721, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, 26 {800, 600, 27778, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, 27 {1024, 768, 15384, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, 28 {960, 720, 13100, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, 29 {1152, 864, 12004, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, 30 {1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, 31 {320, 240, 10000, 33, 22, 4, 12, 44, 5, 0, FB_VMODE_NONINTERLACED}, 32 };
然后修改videomodes.h:
...
#ifndef CONFIG_SYS_DEFAULT_VIDEO_MODE #define CONFIG_SYS_DEFAULT_VIDEO_MODE 0x213 #endif
...
.... #define RES_MODE_320x240 6 #define RES_MODES_COUNT 7 (原值加1) #define VESA_MODES_COUNT 20 (原值加1) ....
最后修改TQ2440.h,增加:
/* LCD settings */ #define CONFIG_VIDEO #define CONFIG_VIDEO_S3C2440 #define CONFIG_VIDEO_LOGO #define VIDEO_FB_16BPP_WORD_SWAP #define CONFIG_SYS_VIDEO_LOGO_MAX_SIZE (320*240+1024+100) #define CONFIG_CMD_BMP #define CONFIG_CFB_CONSOLE #define VIDEO_KBD_INIT_FCT 0 #define VIDEO_TSTC_FCT serial_tstc #define VIDEO_GETC_FCT serial_getc
增加了VIDEO部分后,UBOOT的控制台(console)输出转到了LCD上,而输入还是串口。如果希望串口和LCD同时输出控制台信息怎么办呢。
打开common/console.c文件,修改putc()和puts()函数:
1 void putc(const char c) 2 { 3 #ifdef CONFIG_SILENT_CONSOLE 4 if (gd->flags & GD_FLG_SILENT) 5 return; 6 #endif 7 8 #ifdef CONFIG_DISABLE_CONSOLE 9 if (gd->flags & GD_FLG_DISABLE_CONSOLE) 10 return; 11 #endif 12 13 if (gd->flags & GD_FLG_DEVINIT) { 14 /* Send to the standard output */ 15 fputc(stdout, c); 16 serial_putc(c); //新增添代码 17 } else { 18 /* Send directly to the handler */ 19 serial_putc(c); 20 } 21 } 22 23 void puts(const char *s) 24 { 25 #ifdef CONFIG_SILENT_CONSOLE 26 if (gd->flags & GD_FLG_SILENT) 27 return; 28 #endif 29 30 #ifdef CONFIG_DISABLE_CONSOLE 31 if (gd->flags & GD_FLG_DISABLE_CONSOLE) 32 return; 33 #endif 34 35 if (gd->flags & GD_FLG_DEVINIT) { 36 /* Send to the standard output */ 37 fputs(stdout, s); 38 serial_puts(s); //新增添代码 39 } else { 40 /* Send directly to the handler */ 41 serial_puts(s); 42 } 43 }
5) 总结
最后稍稍总结一下,移植过程中需要修改/增添某个驱动,首先在drivers目录下找到该驱动的类型对应的文件夹
打开MAKEFILE文件,找到按"CONFIG_XXXX"条件编译的目标
例如: "COBJS-$(CONFIG_DRIVER_S3C4510_ETH) += s3c4510b_eth.o ", 打开s3c4510b_eth.c文件,从中找到哪些是主要的函数。
根据自己开发板的情况,新建XXXX.c文件,往里面实现这几个主要的函数。
最后在主头文件里(如TQ2440.h)添加相应的宏,以开启此项功能。