static int __init s3c_nand_init(void)
{
printk("S3C NAND Driver,(c) 2008 Samsung Electronicsn");
platform_driver_register(&s3c2450_nand_driver);
platform_driver_register(&s3c6400_nand_driver);
return platform_driver_register(&s3c6410_nand_driver);
}
static void __exit s3c_nand_exit(void)
{
platform_driver_unregister(&s3c2450_nand_driver);
platform_driver_unregister(&s3c6400_nand_driver);
platform_driver_unregister(&s3c6410_nand_driver);
}
?
static struct platform_driver s3c6410_nand_driver = {
.probe = s3c6410_nand_probe, .remove = s3c_nand_remove,.suspend = s3c_nand_suspend,.resume = s3c_nand_resume,.driver = {
.name = "s3c6410-nand",.owner = THIS_MODULE,},};
static int s3c6410_nand_probe(struct platform_device *dev)
{
return s3c_nand_probe(dev,TYPE_S3C6410);
}
?
其中:
enum s3c_cpu_type {
TYPE_S3C2450,/* including s3c2416 */
TYPE_S3C6400,TYPE_S3C6410,/* including s3c6430/31 */
};
?
/* s3c_nand_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code checks to see if
* it can allocate all necessary resources then calls the
* nand layer to look for devices
*/
static int s3c_nand_probe(struct platform_device *pdev,enum s3c_cpu_type cpu_type)
{
struct s3c_nand_mtd_info *plat_info = pdev->dev.platform_data;
struct mtd_partition *partition_info = (struct mtd_partition *)plat_info->partition;
struct nand_chip *nand;
struct resource *res;
int err = 0;
int ret = 0;
int i,j,size;
#if defined(CONFIG_MTD_NAND_S3C_HWECC)
struct nand_flash_dev *type = NULL;
u_char tmp;
u_char dev_id;
#endif
/* get the clock source and enable it */
s3c_nand.clk = clk_get(&pdev->dev,"nand");
if (IS_ERR(s3c_nand.clk)) {
dev_err(&pdev->dev,"failed to get clock");
err = -ENOENT;
goto exit_error;
}
clk_enable(s3c_nand.clk);
/* allocate and map the resource */
/* currently we assume we have the one resource */
res = pdev->resource;
size = res->end - res->start + 1;
//申请I/O内存,设备通常会提供一组寄存器来用于控制设备、读写设备和获取设备状态,这些寄存器为与I/O空间,也可能位于内存空间。当位于IO空间时,通常称为IO端口,位于内存空间时,对应的内存空间称为 IO 内存。
//在内核中访问I/O 内存前,需要首先使用ioremap()函数将设备所处的物理地址映射到虚拟地址。ioremap()函数原型如下:
void *ioremap(unsigned long offset,unsigned long size); ioremap()与vmlloc()类似,也需要建立页表,但是它并不进行vmlloc()函数中所执行的内存分配行为。ioremap()返回一个特殊的虚拟地址。。。。。在设备的物理地址被映射到虚拟地址之后,尽管尅有直接通过指针访问这些地址,但是可以使用linux内核的一组函数来完成设备内存映射的虚拟地址的读写。
s3c_nand.area = request_mem_region(res->start,size,pdev->name);
if (s3c_nand.area == NULL) {
dev_err(&pdev->dev,"cannot reserve register regionn");
err = -ENOENT;
goto exit_error;
}
s3c_nand.cpu_type = cpu_type;
s3c_nand.device = &pdev->dev;
s3c_nand.regs = ioremap(res->start,size);
if (s3c_nand.regs == NULL) {
dev_err(&pdev->dev,"cannot reserve register regionn");
err = -EIO;
goto exit_error;
}
/* allocate memory for MTD device structure and private data */
s3c_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),GFP_KERNEL);
if (!s3c_mtd) {
printk("Unable to allocate NAND MTD dev structure.n");
return -ENOMEM;
}
/* Get pointer to private data */
nand = (struct nand_chip *) (&s3c_mtd[1]);
/* Initialize structures */
memset((char *) s3c_mtd,sizeof(struct mtd_info));
memset((char *) nand,sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
s3c_mtd->priv = nand;
for (i = 0; i < plat_info->chip_nr; i++) {
nand->IO_ADDR_R = (char *)(s3c_nand.regs + S3C_NFDATA);
nand->IO_ADDR_W = (char *)(s3c_nand.regs + S3C_NFDATA);
nand->cmd_ctrl = s3c_nand_hwcontrol;
nand->dev_ready = s3c_nand_device_ready;
nand->scan_bbt = s3c_nand_scan_bbt;
nand->options = 0;
#if defined(CONFIG_MTD_NAND_S3C_CACHEDPROG)
nand->options |= NAND_CACHEPRG;
#endif
#if defined(CONFIG_MTD_NAND_S3C_HWECC)
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.hwctl = s3c_nand_enable_hwecc;
nand->ecc.calculate = s3c_nand_calculate_ecc;
nand->ecc.correct = s3c_nand_correct_data;
s3c_nand_hwcontrol(0,NAND_CMD_READID,NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
s3c_nand_hwcontrol(0,0x00,NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
s3c_nand_hwcontrol(0,NAND_NCE | NAND_ALE);
s3c_nand_hwcontrol(0,NAND_CMD_NONE,NAND_NCE | NAND_CTRL_CHANGE);
s3c_nand_device_ready(0);
tmp = readb(nand->IO_ADDR_R); /* Maf. ID */
dev_id = tmp = readb(nand->IO_ADDR_R); /* Device ID */
for (j = 0; nand_flash_ids[j].name != NULL; j++) {
if (tmp == nand_flash_ids[j].id) {
type = &nand_flash_ids[j];
break;
}
}
if (!type) {
printk("Unknown NAND Device.n");
goto exit_error;
}
nand->cellinfo = readb(nand->IO_ADDR_R); /* the 3rd byte */
tmp = readb(nand->IO_ADDR_R); /* the 4th byte */
if (!type->pagesize) {
if (((nand->cellinfo >> 2) & 0x3) == 0) {
nand_type = S3C_NAND_TYPE_SLC;
nand->ecc.size = 512;
nand->ecc.bytes = 4;
if ((1024 << (tmp & 0x3)) > 512) {
nand->ecc.read_page = s3c_nand_read_page_1bit;
nand->ecc.write_page = s3c_nand_write_page_1bit;
nand->ecc.read_oob = s3c_nand_read_oob_1bit;
nand->ecc.write_oob = s3c_nand_write_oob_1bit;
nand->ecc.layout = &s3c_nand_oob_64;
} else {
nand->ecc.layout = &s3c_nand_oob_16;
}
} else {
nand_type = S3C_NAND_TYPE_MLC;
nand->options |= NAND_NO_SUBPAGE_WRITE; /* NOP = 1 if MLC */
nand->ecc.read_page = s3c_nand_read_page_4bit;
nand->ecc.write_page = s3c_nand_write_page_4bit;
nand->ecc.size = 512;
nand->ecc.bytes = 8; /* really 7 bytes */
nand->ecc.layout = &s3c_nand_oob_mlc_64;
if((1024 << (tmp & 0x3)) > 2048)
nand->ecc.layout = &s3c_nand_oob_mlc_128;
}
} else {
/*
nand_type = S3C_NAND_TYPE_SLC;
nand->ecc.size = 512;
nand->cellinfo = 0;
nand->ecc.bytes = 4;
nand->ecc.layout = &s3c_nand_oob_16;
*/
nand_type = S3C_NAND_TYPE_MLC;
nand->options |= NAND_NO_SUBPAGE_WRITE; /* NOP = 1 if MLC */
nand->ecc.read_page = s3c_nand_read_page_4bit;
nand->ecc.write_page = s3c_nand_write_page_4bit;
nand->ecc.size = 512;
nand->ecc.bytes = 8; /* really 7 bytes */
nand->ecc.layout = &s3c_nand_oob_mlc_64;
if(dev_id==0xd5)
nand->ecc.layout = &s3c_nand_oob_mlc_128;
}
printk("S3C NAND Driver is using hardware ECC.n");
#else
nand->ecc.mode = NAND_ECC_SOFT;
printk("S3C NAND Driver is using software ECC.n");
#endif
if (nand_scan(s3c_mtd,1)) {
ret = -ENXIO;
goto exit_error;
}
/* Register the partitions */
add_mtd_partitions(s3c_mtd,partition_info,plat_info->mtd_part_nr);
}
pr_debug("initialized okn");
return 0;
exit_error:
kfree(s3c_mtd);
return ret;
}
drivers/mtd/nand/s3c_nand.c是一个platform驱动,我们在S3C6410的BSP中只需要添加相关的NAND的platform的设备和分区信息即可。
在BSP中添加相应的信息如下:[ 下面有关于这部分的分析]
static void __init smdk6410_map_io(void)
{
s3c_device_nand.name = "s3c6410-nand";
s3c64xx_init_io(smdk6410_iodesc,ARRAY_SIZE(smdk6410_iodesc));
s3c24xx_init_clocks(12000000);
s3c24xx_init_uarts(smdk6410_uartcfgs,ARRAY_SIZE(smdk6410_uartcfgs));
}
smdk6410_map_io() 在下面出现。
MACHINE_START(SMDK6410,"SMDK6410")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C_PA_UART & 0xfff00000,.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,.boot_params = S3C64XX_PA_SDRAM + 0x100,.init_irq = s3c6410_init_irq,.map_io = smdk6410_map_io, .init_machine = smdk6410_machine_init,.timer = &s3c24xx_timer,MACHINE_END
?
struct platform_device s3c_device_nand = {
.name = "s3c-nand",.id = -1,.num_resources = ARRAY_SIZE(s3c_nand_resource),.resource = s3c_nand_resource,};
其中关于资源地信息如下:
/* NAND Controller */
static struct resource s3c_nand_resource[] = {
[0] = {
.start = S3C64XX_PA_NAND,.end = S3C64XX_PA_NAND + S3C64XX_SZ_NAND - 1,.flags = IORESOURCE_MEM,}
};
在宏定义的文件/* linux/arch/arm/mach-s3c6400/include/mach/map.h 中,有
/* NAND flash controller */
#define S3C64XX_PA_NAND (0x70200000)
#define S3C64XX_SZ_NAND SZ_1M
?
上面这些信息的出处在
关于其大小的确定,在datashee的 Memory mapping 一节:
?
在开发板的硬件硬件手册有 更详细的说明。
***************************************************************************************************************
下面的内容参考 了ARM LINUX 静态映射分析
内核提供了一个比较重要的结构体, machine_desc,这个结构体在内核移植的过程中,起着重要的作用。内核通过它来进行体系架构相关部分的初始话。
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head.S,head-common.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,struct tag *,char **,struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
machine_desc 通过宏 MACHINE_START 进行初始化。
MACHINE_START(SMDK6410,MACHINE_END
其中 MACHINE_START 宏定义如下:
#define MACHINE_START(_type,_name)
static const struct machine_desc __mach_desc_##_type
__used
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_##_type,
.name = _name,#define MACHINE_END
};
?
其中,MACHINE_TYPE 宏的是用了 GCC 扩展。在预编译时,进行字符替换。
?
在mach-types.h文件中
?
#define MACH_TYPE_SMDK6410???????????? 1626
上面 map_io函数,即是,内核提供给用户创建IO资源到内核虚拟地址静态映射表的 接口函数。在系统初始化 ,被调用
start_kernel()->setup_arch(&command_line)->paging_init(mdesc)>devicemaps_init()->?mdesc->map_io() 中调用。
?
而init_irq 的初始化流程为:
start_kernrl()->init_IRQ()->init_arch_irq()->setup_arch()->init_arch_irq
而上面的init_arch_irq 指针的赋值则在???
start_kernel()->setup_arch()-》
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
接下来,继续分析 smdk6410_map_io()
static void __init smdk6410_map_io(void)
{
s3c_device_nand.name = "s3c6410-nand";
s3c64xx_init_io(smdk6410_iodesc,ARRAY_SIZE(smdk6410_iodesc));
s3c24xx_init_clocks(12000000);
s3c24xx_init_uarts(smdk6410_uartcfgs,ARRAY_SIZE(smdk6410_uartcfgs));
}
?
/* read cpu identification code */
void __init s3c64xx_init_io(struct map_desc *mach_desc,int size)
{
unsigned long idcode;
/* initialise the io descriptors we need for initialisation */
iotable_init(s3c_iodesc,ARRAY_SIZE(s3c_iodesc));
iotable_init(mach_desc,size);
idcode = __raw_readl(S3C_VA_SYS + 0x118);
s3c_init_cpu(idcode,cpu_ids,ARRAY_SIZE(cpu_ids));
}
?
/*
* Create the architecture specific mappings
*/
void __init iotable_init(struct map_desc *io_desc,int nr)
{
int i;
for (i = 0; i < nr; i++)
create_mapping(io_desc + i);
}
由上可知, smdk6410_map_io()最终 调用create_mapping()建立映射表。其参数为:
struct map_desc {
unsigned long virtual;
unsigned long pfn;
unsigned long length;
unsigned int type;
};
?
create_mapping()就是根据 map_desc提供的信息创建线性映射表的。
。。。。。。。
来看下,S3C6410是怎么创建map_desc结构体的。
/* Initial IO mappings */
static struct map_desc s3c6410_iodesc[] __initdata = {
IODESC_ENT(LCD),IODESC_ENT(SROMC),IODESC_ENT(HOSTIFB),IODESC_ENT(OTG),IODESC_ENT(OTGSFR),};
?
#define IODESC_ENT(x) { (unsigned long)S3C64XX_VA_##x,__phys_to_pfn(S3C64XX_PA_##x),S3C64XX_SZ_##x,MT_DEVICE }
展开后,等价于:
static struct map_desc s3c6410_iodesc[] __initdata = {
.virtual S3C64XX_VA_GPIO),.pfn __phys_to_pfn(S3C64XX_PA_GPIO),.length S3C64XX_SZ_GPIO,.type MTD_DEVICE,
......................
};
?
至此,我们已经清楚看到了GPIO被静态映射的过程,由于我么在前面的静态映射中,已经做好了相关的映射,所以 在写GPIO相关的驱动的时候。可以直接配置引脚的原因。
s3c_gpio_cfgpin(S3C64XX_GPN(0),S3C_GPIO_SFN(1));
?
2、最终还会调用s3c_init_cpu()->cpu->map_io( );
static struct cpu_table *cpu;
?
static struct cpu_table *cpu;
?
?
/* per-cpu initialisation function table. */
struct cpu_table {
unsigned long idcode;
unsigned long idmask;
void (*map_io)(void);
void (*init_uarts)(struct s3c2410_uartcfg *cfg,int no);
void (*init_clocks)(int xtal);
int (*init)(void);
const char *name;
};
在cpu.c文件
?
/* table of supported CPUs */
static const char name_s3c6400[] = "S3C6400";
static const char name_s3c6410[] = "S3C6410";
?
static struct cpu_table cpu_ids[] __initdata = {
{
.idcode = 0x36400000,.idmask = 0xfffff000,.map_io = s3c6400_map_io,.init_clocks = s3c6400_init_clocks,.init_uarts = s3c6400_init_uarts,.init = s3c6400_init,.name = name_s3c6400,{
.idcode = 0x36410100,.idmask = 0xffffff00,.map_io = s3c6410_map_io,.init_clocks = s3c6410_init_clocks,.init_uarts = s3c6410_init_uarts,.init = s3c6410_init,.name = name_s3c6410,};
?
/* s3c6410_map_io
*
* register the standard cpu IO areas
*/
void __init s3c6410_map_io(void)
{
iotable_init(s3c6410_iodesc,ARRAY_SIZE(s3c6410_iodesc));
/* initialise device information early */
s3c6410_default_sdhci0();
s3c6410_default_sdhci1();
/* the i2c devices are directly compatible with s3c2440 */
s3c_i2c0_setname("s3c2440-i2c");
s3c_i2c1_setname("s3c2440-i2c");
/* set our idle function */
s3c64xx_idle = s3c6410_idle;
}
?
?
/* Initial IO mappings */
static struct map_desc s3c6410_iodesc[] __initdata = {
IODESC_ENT(LCD),};
?
和前面分析是一样的情况:
/* Initial IO mappings */
static struct map_desc s3c6410_iodesc[] __initdata = {
LCDSROMC HOSTIFB OTG OTGSFR.等的静态映射都看 的清楚明白了了。