基于S3C2410平台移植Linux 2.6.14内核&驱动指南
时间:2009-03-24 来源:caocwy
安装交叉编译工具
Ø 下载交叉编译工具
(本文默认所有软件均下载在用户主目录下)
arm-linux-gcc-3.4.1 —— 交叉编译工具
URL: ftp://ftp.handhelds.org/projects/toolchain/arm-linux-gcc-3.4.1.tar.bz2
Ø 安装
//将压缩包移动至根目录下
#mv arm-linux-gcc-3.4.1.tar.bz2 /
//解压,默认路径为 /usr/local/arm /3.4.1
#tar jxvf arm-linux-gcc-3.4.1.tar.gz2
下载Linux内核
下载Linux内核
Ø 下载Linux 2.6.14内核
URL: http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.14.tar.bz2
Ø 解压内核
#cd ~
#tar jxvf linux-2.6.14.tar.bz2
建立环境变量
建立环境变量
Ø 将交叉编译工具路径和内核路径加入环境变量
#vi ~/.bashrc
export KERNEL=~/linux-2.6.14
export PATH=/usr/local/arm/3.4.1/bin:$PATH
内核移植
内核移植
Ø 为Flash进行分区
启动开发板,进入vivi,对vivi进行分区操作
vivi>bon part 0 128k 192k 2240k 5312k:m 64704k
分区信息如下所示:
将vivi写进Flash
vivi>load flash vivi x
分区 | 起始地址 | 分区大小 | 分区作用 |
Part0 | 0x0 | 0x00020000 (128k) | bootloader |
Part1 | 0x00020000 | 0x00010000 (64k) | bootloader params |
Part2 | 0x00030000 | 0x00200000 (2m) | linux kernel |
Part3 | 0x00230000 | 0x00300000 (3m) | root filesystem |
Part4 | 0x00530000 | 0x03A00000 (58m) | yaffs filesystem |
Ø 修改Makefile
#vi $KERNEL/Makefile
找到ARCH与CROSS_COMPILE,修改为
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
Ø 将分区信息加入内核
#vi $KERNEL/arch/arm/mach-s3c2410/devs.c
添加头文件:
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand.h>
#include <asm/arch/nand.h>
添加分区结构:
static struct mtd_partition partition_info[]={
{
name: "loader", //分区名字,任意
size: 0x00020000, //分区大小
offset: 0x0, //分区的起始地址,相对于0x0的偏移
},{
name: "param",
size: 0x00010000,
offset: 0x00020000,
},{
name: "kernel",
size: 0x00200000,
offset: 0x00030000,
},{
name: "rootfs",
size: 0x00300000,
offset: 0x00230000,
},{
name: "yaffs",
size: 0x03A00000,
offset: 0x00530000,
}
};
struct s3c2410_nand_set nandset={
nr_partitions: 5, //分区数量
partitions: partition_info, //分区表
};
struct s3c2410_platform_nand superlpplatform={
//NAND Flash芯片支持
//参数意义可参考S3C2410手册
tacls: 0,
twrph0: 30,
twrph1: 0,
sets: &nandset,
nr_sets: 1,
};
修改s3c_device_nand,以加入NAND Flash驱动:
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand", //设备名称
.id = -1, //有效设备编号,-1表示唯一设备
.num_resources = ARRAY_SIZE(s3c_nand_resource), //寄存器区的个数
.resource = s3c_nand_resource, //寄存器区的首地址
//添加如下信息,表示NAND Flash设备
.dev={
.platform_data=&superlpplatform
}
};
Ø 指定启动时的设备初始化
#vi $KERNEL/arch/arm/mach-s3c2410/mach-smdk2410.c
修改:
static struct platform_device *smdk2410_devices[] __initdata = {
s3c_device_usb,
s3c_device_lcd,
s3c_device_wdt,
s3c_device_i2c,
s3c_device_iis,
//添加如下信息
s3c_device_nand,
};
Ø 禁止Flash ECC效验
内核通过vivi把数据写入NAND Flash,而vivi的ECC效验算法和内核的不同,内核的效验码是由NAND Flash控制器产生的,所以在此必须禁用NAND Flash ECC。
#vi $KERNEL/drivers/mtd/nand/s3c2410.c
找到s3c2410_nand_init_chip函数,将chip->eccmode的值修改为:
NAND_ECC_NONE
Ø 修正Kernel错误
编辑partitions.h,增加list.h头文件。
#vi $KERNEL/include/linux/mtd/partitions.h
在头文件中添加:
#include <linux/list.h>
Ø 加载DEVFS
#vi $KERNEL/fs/Kconfig
查找“Pseudo filesystems”
在其下添加如下语句:
config DEVFS_FS
bool "/dev file system support (OBSOLETE)"
default y
config DEVFS_MOUNT
bool "Automatically mount at boot"
default y
depends on DEVFS_FS
Yaffs2加载
Yaffs2加载
Ø 下载Yaffs2
URL: http://www.aleph1.co.uk/cgi-bin/viewcvs.cgi/yaffs2.tar.gz?view=tar
Ø 解压Yaffs2并将其加入Linux内核
#cd ~
#tar zxvf yaffs2.tar.gz
#cd yaffs2
#sh patch-ker.sh c $KERNEL
USB Host Driver 移植
USB Host Driver 移植
Ø 初始化USB Host Driver
由于Linux 2.6.14内核已经包含了s3c2410的USB驱动,所以只要在平台初始化文件中对USB进行设置、初始化即可。
#vi $KERNEL/arch/arm/mach-s3c2410/mach-smdk2410.c
添加头文件:
#include <asm/arch/usb-control.h>
#include <asm/arch/regs-clock.h>
#include <linux/device.h>
#include <linux/delay.h>
添加USB Host Driver注册信息:
struct s3c2410_hcd_info usb_s3c2410_info = {
.port[0] = {
.flags = S3C_HCDFLG_USED
}
};
int smdk2410_usb_init(void)
{
/* USB */
unsigned long upllvalue = (0x78<<12)|(0x02<<4)|(0x03);
printk("USB Control, (c) 2006 s3c2410\n");
s3c_device_usb.dev.platform_data = &usb_s3c2410_info;
while(upllvalue!=__raw_readl(S3C2410_UPLLCON))
{
__raw_writel(upllvalue,S3C2410_UPLLCON);
mdelay(1);
}
return 0;
}
在smdk2410_map_io函数中调用 usb 的初始化,如下所示:
static void __init smdk2410_map_io(void)
{
…… //代码省略
smdk2410_usb_init();
}
LCD移植
LCD移植
Ø 参考资料
S3C2410X User Manual
作用:参考LCD各寄存器的地址、置位等信息
LCD技术手册
作用:参看LCD的技术指标
(由于本人使用的是Uptech 2410-s开发板,所以使用的液晶屏是Sharp LQ080V3DG01)
Ø 初始化LCD
由于Linux 2.6.14内核已经包含了该驱动,所以只要在平台初始化文件中对LCD进行设置、初始化即可。
#vi $KERNEL/arch/arm/mach-s3c2410/mach-smdk2410.c
添加头文件:
#include <asm/arch/regs-lcd.h>
#include <asm/arch-s3c2410/fb.h>
添加LCD初始化代码:
static struct s3c2410fb_mach_info s3c2410_lcd_info __initdata = {
.fixed_syncs = 0,
.regs = {
//对于寄存器的设置是关键,可参考S3C2410X的手册
//和LCD技术手册中对于LCD技术指标的描述来进行设置。
//其中,寄存器值的宏定义在regs-lcd.h文件中。
.lcdcon1 = S3C2410_LCDCON1_TFT16BPP
| S3C2410_LCDCON1_TFT | S3C2410_LCDCON1_CLKVAL(1),
.lcdcon2 = S3C2410_LCDCON2_VBPD(32)
| S3C2410_LCDCON2_VFPD(9) | S3C2410_LCDCON2_VSPW(1),
.lcdcon3 = S3C2410_LCDCON3_HBPD(47)
| S3C2410_LCDCON3_HFPD(15),
.lcdcon4 = S3C2410_LCDCON4_MVAL(13)
| S3C2410_LCDCON4_HSPW(95),
.lcdcon5 = S3C2410_LCDCON5_FRM565
| S3C2410_LCDCON5_INVVLINE | S3C2410_LCDCON5_HWSWP
| S3C2410_LCDCON5_PWREN | S3C2410_LCDCON5_INVVFRAME,
},
.lpcsel = 0x0,
.gpccon = 0xaaaaaaaa,
.gpccon_mask = 0xffffffff,
.gpcup = 0xffffffff,
.gpcup_mask = 0xffffffff,
.gpdcon = 0xaaaaaaaa,
.gpdcon_mask = 0x0,
.gpdup = 0xffffffff,
.gpdup_mask = 0xffffffff,
.width = 640,
.height = 480,
.xres = {640,640,640},
.yres = {480,480,480},
.bpp = {16,16,16},
};
static void __init smdk2410_lcd_init(void)
{
set_s3c2410fb_info(&s3c2410_lcd_info);
}
最后在MACHINE_START与MACHINE_END中添加如下代码:
.init_machine = smdk2410_lcd_init,
NE2000兼容网卡移植(AX88796)
NE2000兼容网卡移植(AX88796)
Ø 定义网卡I/O地址
在S3C2410平台关于内存地址映射的头文件中增加对网卡的支持。
#vi $KERNEL/include/asm-arm/arch-s3c2410/map.h
添加代码:
#define pAX88796_BASE (0x10000000) //physical base address
#define vAX88796_BASE (0xd1000000) //virtual base address
#define AX88796_BASE (vAX88796_BASE + 0x200) //bank2 base address
Ø 地址映射
将之前定义的网卡物理I/O地址和虚拟I/O地址间的映射关系注册到平台初始化文件中。
#vi $KERNEL/arch/arm/mach-s3c2410/mach-smdk2410.c
修改:
static struct map_desc smdk2410_iodesc[] __initdata = {
//添加如下映射关系
//{虚拟地址, 物理地址, 地址空间大小, 设备类型}
{vAX88796_BASE, pAX88796_BASE, SZ_1M, MT_DEVICE},
};
Ø 修改NE2000驱动文件
(基于Uptech 2410-s平台,其使用的是NE2000兼容的AX88796芯片)
#vi $KERNEL/drivers/net/ne.c
添加头文件:
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/arch/irqs.h>
#include <asm/arch/regs-mem.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/map.h>
修改驱动:
//根据开发板上网卡和S3C2410的连接,确定外部中断
#define IRQ_NE2000 IRQ_EINT2
//定义网卡MAC地址
unsigned char ne_defethaddr[] = { 0x00, 0xd0, 0xcf, 0x00, 0x00, 0x01 };
//确保总线宽度为16位
#if 0
#if defined(CONFIG_PLAT_MAPPI)
# define DCR_VAL 0x4b
#elif defined(CONFIG_PLAT_OAKS32R)
# define DCR_VAL 0x48 //8-bit
#else
# define DCR_VAL 0x49 //16-bit
#endif
#endif
#define DCR_VAL 0x49
/*** 这里的代码主要参考THREEWATER的文章 ***/
/*
NE2000网卡的初始化代码在do_ne_probe函数中,在该函数调用时,网卡设备的基地址空间、寄存器、中断等资源都未指定,所以在这里为该平台进行资源分配。
*/
#ifndef MODULE
int orig_irq = dev->irq;
#endif
//添加如下代码
static int once = 0;
unsigned int value = 0;
if(once)
{
return -ENXIO;
}
//设置BWSCON寄存器:Bus Width & Wait Control Register
//这里AX88796在BANK2空间,设置BANK2的16-bit总线和停止状态等信息
value = __raw_readl(S3C2410_BWSCON);
value &= ~(S3C2410_BWSCON_ST2
| S3C2410_BWSCON_WS2 | S3C2410_BWSCON_DW2_32);
value |= (S3C2410_BWSCON_ST2 | S3C2410_BWSCON_DW2_16);
printk(KERN_INFO"s3c2410_bwscon value %x\n", value);
__raw_writel(value, S3C2410_BWSCON);
value = 0;
//设置BANKCON寄存器:Bank Control Register
//设置AX88796所在BANK2空间的时序信息
value = S3C2410_BANKCON_Tacs4
| S3C2410_BANKCON_Tcos4 | S3C2410_BANKCON_Tacc14
| S3C2410_BANKCON_Tcoh4 | S3C2410_BANKCON_Tcah4
| S3C2410_BANKCON_Tacp6 | S3C2410_BANKCON_PMCnorm;
printk(KERN_INFO"s3c2410_bankcon2 value %x\n", value);
__raw_writel(value, S3C2410_BANKCON2);
//设置AX88796的中断触发方式为低电平有效
set_irq_type(IRQ_NE2000, IRQT_LOW);
printk(KERN_INFO"ax88796 extIRQ = %x, IRQtype= %x\n",
IRQ_NE2000, IRQT_LOW);
s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
s3c2410_gpio_pullup(S3C2410_GPF2, 0);
//指定AX88796的基地址空间和使用的中断号
if(base_addr == 0)
{
dev->base_addr = base_addr = AX88796_BASE;
dev->irq = IRQ_NE2000;
once++;
}
/*
网卡的主要检测工作在ne_probe1函数中完成,其中最主要的是读取和配置网卡的MAC地址等信息。这里对AX88796的MAC地址的设置和典型的网卡不同。一般的网卡通过读取连接在NE2000网卡上的EEPROM的代码,而现在的平台并没有使用配置存储器,所以需要修改ne_probe1函数,设置网卡的MAC地址。
*/
{
……
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
}
//添加并修改如下代码
{
//设置MAC地址
unsigned char *ep;
ep = (unsigned char *)&ne_defethaddr[0];
ne_defethaddr[5]++;
for(i = 0; i < 6; i++)
{
SA_prom[i] = ep[i];
}
SA_prom[14] = SA_prom[15] = 0x57;
wordlength = 2;
}
//取消读取EEPROM的代码
#if 0
for(i = 0; i < 32; i+=2) {
SA_prom[i] = inb(ioaddr + NE_DATAPORT);
SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
if (SA_prom[i] != SA_prom[i+1])