环境:Ubuntu 10.4
ARM-linux-gcc:arm-none-linux-gnueabi v4.5.1
做完核心的内核移植之后,接下来要做的驱动移植,第一步移植的是nand flash驱动。
在做驱动移植之前,我们有必要了解一下linux的platform机制:
从Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。
Linux platform. driver机制和传统的device driver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform. device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。通过platform机制开发底层设备驱动的大致流程如图所示。
platform机制开发驱动流程
platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在include/linux/platform_device.h中,定义原型如下:
struct platform_device {
const char * name; //定义平台设备的名称
int id;
struct device dev;
u32 num_resources;
struct resource * resource; //定义平台设备的资源。
};
下面来看一下platform_device结构体中最重要的一个成员struct resource * resource。struct resource被定义在include/linux/ioport.h中,定义原型如下:
struct resource {
resource_size_t start; //定义资源的起始地址
resource_size_t end; //定义资源的结束地址
const char *name; //定义资源的名称
unsigned long flags; //定义资源的类型,比如MEM,IO,IRQ,DMA类型
struct resource *parent, *sibling, *child; //资源链表指针
};
通过调用函数platform_add_devices()向系统中添加该设备了,该函数内部调用platform_device_register( )进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。
接下来来看platform_driver结构体的原型定义,在include/linux/platform_device.h中,代码如下:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在driver/base/platform.c文件中,具体实现代码如下:
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
总结,通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独自的资源(地址总线和IRQs),都可以用platform_driver实现。如:LCD,网卡、USB、UART等,都可以用platfrom_driver写,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。
以上关于platfrom机制均为网络转载。
现在开始来进行nand flash驱动移植
首先下载nand flash驱动 s3c_nand.c ,此文件包含着nand flash驱动具体的实现,将其复制到drivers/mtd/nand下;
s3c_nand.c 下载地址 s3c_nand.c
在s3c_nand.c中添加nand flash分区信息,分区内容可以自由设定。
#if defined(CONFIG_ARCH_S5PV210)
struct mtd_partition s3c_partition_info[] = {
{
.name= "misc",
.offset= (768*SZ_1K), /* for bootloader */
.size= (256*SZ_1K),
.mask_flags= MTD_CAP_NANDFLASH,
},
{
.name= "recovery",
.offset= MTDPART_OFS_APPEND,
.size= (5*SZ_1M),
},
{
.name= "kernel",
.offset= MTDPART_OFS_APPEND,
.size= (5*SZ_1M),
},
{
.name= "ramdisk",
.offset= MTDPART_OFS_APPEND,
.size= (3*SZ_1M),
},
{
.name= "system",
.offset= MTDPART_OFS_APPEND,
.size= MTDPART_SIZ_FULL,
}
{
.name= "system",
.offset= MTDPART_OFS_APPEND,
.size= (110*SZ_1M),
},
{
.name= "cache",
.offset= MTDPART_OFS_APPEND,
.size= (80*SZ_1M),
},
{
.name= "userdata",
.offset= MTDPART_OFS_APPEND,
.size= MTDPART_SIZ_FULL,
}
#endif
};
struct s3c_nand_mtd_info s3c_nand_mtd_part_info = {
.chip_nr = 1,
.mtd_part_nr = ARRAY_SIZE(s3c_partition_info),
.partition = s3c_partition_info,
};
在s3c_nand.c使用的寄存器都已经改成了S3C_NFCONF类似的名字,内核源码中自带的nand flash寄存器只有S3C2410的一套寄存器,这里可以将所有的S3C2410改为S3C,然后将所有的S3C_NFREG改为S5P_NFREG
结构体s3c_nand_mtd_info在源代码是没有的,所以要将其添加进去;
修改 arch/arm/plat-samsung/include/plat/nand.h 添加如下内容:
struct s3c_nand_mtd_info {
uint chip_nr;
uint mtd_part_nr;
struct matd_partition *partition;
};
为了能使s3c_nand.c正确编译,还要修改该目录下,也就是的drivers/mtd/nand的Makefile和Kconfig:
修改driver/mtd/nand/Kconfig添加如下内容:
config MTD_NAND_S3C
tristate "NAND Flash support for S3C Soc"
depends on ARCH_S5PV210 && MTD_NAND
help
This enables the NAND flash controller on the S3C.
No board specfic support is done by this driver , each board
must advertise a platform_device for the driver to attach.
config MTD_NAND_S3C_DEBUG
bool "S3C NAND driver debug"
depends on MTD_NAND_S3C
help
Enable debugging of the S3C NAND driver
config MTD_NAND_S3C_HWECC
bool "S3C NAND Hardware ECC"
depends on MTD_NAND_S3C
help
Enable the use of the S3C's internal ECC generator when
using NAND. Early versons of the chip have had problems with
incorrect ECC generation, and if using these, the default of
software ECC is preferable
If you lay down a device with the hardware ECC, the you will
currently not be able to switch to software, as there is no
implementation for ECC method used by the S3C
修改drivers/mtd/nand/Makefile添加如下内容:
obj-$(CONFIG_MTD_NAND_S3C) += s3c_nand.o
在/arch/arm/plat-samsung/devs.c中添加nand flash source,platform_device,需要头文件map.h map.h下载,下载将其放在(arch/arm/mach-s5pv210/include/mach)
/* NAND Controller */
static struct resource s3c_nand_resource[] = {
[0] = {
.start= S5PV210_PA_NAND,
.end= S5PV210_PA_NAND + S5PV210_SZ_NAND - 1,
.flags= IORESOURCE_MEM,
}
};
struct platform_device s3c_device_nand = {
.name= "s5pv210-nand",
.id= -1,
.num_resources= ARRAY_SIZE(s3c_nand_resource),
.resource= s3c_nand_resource,
};
在arch/arm/mach-s5pv210/Mach-smdkv210.c添加platform_device并添加头文件#include 。
找到static struct platform_device *smdkv210_devices[] __initdata
添加
#if defined(CONFIG_MTD_NAND_S3C)
&s3c_device_nand,
#endif
添加时钟信息
在Clock.c (archarmmach-s5pv210) 中找到static struct clk init_clocks_off[],添加
{
.name = "nand",
.id = -1,
.parent = &clk_hclk_psys.clk,
.enable = s5pv210_clk_ip1_ctrl,
.ctrlbit = ((1 << 28) | (1 << 24)),
},
添加之后,nand flash驱动才能正确获取时钟。
以上操作完成后,开始配置内核
根目录下make menuconfig
Device Drivers --->
<*> Memory Technology Device (MTD) support --->
MTD partitioning support
<*> Caching block device access to MTD devices
<*> NAND Device Support --->
<*> NAND Flash support for S3C SoCs
S3C NAND Hardware ECC
到了这里,开发板已经支持nand flash驱动。
接下来
配置后保存,make clean ,make zImage
将zImage下载进开发板。从串口信息可以看出nand flash驱动是否完成:
完成配置之后重新编译内核,通过TFTP进行调试,串口输出有一个错误,如下:
S3C NAND Driver, (c) 2008 Samsung Electronics
dev_id == 0xd5 select s3c_nand_oob_mlc
****Nandflash:ChipType= MLC ChipName=samsung-K9GAG08U0D************
S3C NAND Driver is using hardware ECC.
NAND device: Manufacturer ID: 0xec, Chip ID: 0xd5 (Samsung NAND 2GiB 3,3V 8-bit), page size: 4096, OOB size: 218
Driver must set ecc.strength when using hardware ECC
------------[ cut here ]------------
kernel BUG at drivers/mtd/nand/nand_base.c:3382!
Internal error: Oops - BUG: 0 [#1] ARM
从串口错误信息很容易可以知道错误的根源在linux3.8.3内核的drivers/mtd/nand/nand_base.c:3382,进入文件,找到根源,如下所示:
if (mtd->writesize >= chip->ecc.size) {
if (!chip->ecc.strength) {
pr_warn("Driver must set ecc.strength when using hardware ECCn");
BUG();
}
break;
}
因为在内核配置的时候选择了硬件ECC校验,在执行上面代码的时候进入BUG(),而BUG()语句是一个死循环,内核进去之后无法出来。为了使调试通过,暂时先注释掉BUG()语句,使得内核不进入死循环。
重新编译内核,通过TFTP调试,串口输出信息为
S3C NAND Driver, (c) 2008 Samsung Electronics
S3C NAND Driver is using hardware ECC.
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Creating 5 MTD partitions on "s5pv210-nand":
0x0000000c0000-0x000000100000 : "misc"
0x000000100000-0x000000600000 : "recovery"
0x000000600000-0x000000b00000 : "kernel"
0x000000b00000-0x000000e00000 : "ramdisk"
0x000000e00000-0x000010000000 : "system"
|