开发板:tiny4412ADK+S700 4GB Flash
主机:Wind7 64位
虚拟机:Vmware+Ubuntu12_04
u-boot:U-Boot 2010.12
Linux内核版本:linux-3.0.31
Android版本:android-4.1.2
在arch/arm/mach-exynos/mach-tiny4412.c中:
MACHINE_START(TINY4412, "TINY4412")
.boot_params = S5P_PA_SDRAM + 0x100,
.init_irq = exynos4_init_irq,
.map_io = smdk4x12_map_io,
.init_machine = smdk4x12_machine_init,
.timer = &exynos4_timer,
.reserve = &exynos4_reserve,
MACHINE_END
在文件arch/arm/kernel/setup.c中:
static int __init customize_machine(void)
{
if (machine_desc->init_machine)
machine_desc->init_machine();
return 0;
}
arch_initcall(customize_machine);
在文件arch/arm/plat-samsung/init.c中:
static int __init s3c_arch_init(void)
{
int ret;
…
ret = (cpu->init)();
…
ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
return ret;
}
arch_initcall(s3c_arch_init);
这几个函数跟uart有关,我们先看一下内核启动的时候是如何调用这个函数:
start_kernel (init/main.c)
--- setup_arch (arch/arm/kernel/setup.c)
--- paging_init (arch/arm/mm/mmu-cma.c)
--- devicemaps_init (arch/arm/mm/mmu-cma.c)
--- mdesc->map_io() ----- smdk4x12_map_io
--- rest_init
---kernel_init
--- do_pre_smp_initcalls (init/main.c)
--- do_basic_setup
--- do_initcalls
static void __init do_pre_smp_initcalls(void)
{
initcall_t *fn;
for (fn = __initcall_start; fn < __early_initcall_end; fn++)
do_one_initcall(*fn);
}
static void __init do_initcalls(void)
{
initcall_t *fn;
for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
// 在这里会调用customize_machine和s3c_arch_init
}
在文件include/linux/init.h中:
#define __define_initcall(level,fn,id)
static initcall_t __initcall_##fn##id __used
__attribute__((__section__(".initcall" level ".init"))) = fn
#define arch_initcall(fn) __define_initcall("3",fn,3)
对于arch_initcall(customize_machine)展开后就是:
static initcall_t __initcall_ customize_machine3 __used
__attribute__((__section__(".initcall3.init"))) = customize_machine
在arch/arm/kernel/vmlinux.lds中:
__initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcallbresume.init) *(.initcallresume.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
通过上面的分析,大致知道这几个函数的调用过程了。
下面继续分析:
static void __init smdk4x12_map_io(void)
{
clk_xusbxti.rate = 24000000;
// 宏S5P_VA_CHIPID的值是S3C_ADDR(0x02000000)
s5p_init_io(NULL, 0, S5P_VA_CHIPID); // 获取cpu id
s3c24xx_init_clocks(24000000); // 初始化时钟资源
s3c24xx_init_uarts(smdk4x12_uartcfgs, ARRAY_SIZE(smdk4x12_uartcfgs));
/*
初始化uart,此时并没有进行注册,只是填充了一些结构体,提供给注册时用,数组smdk4x12_uartcfgs中是关于每一个uart控制器的寄存器默认参数,如:
static struct s3c2410_uartcfg smdk4x12_uartcfgs[] __initdata = {
[0] = {
.hwport = 0,
.flags = 0,
.ucon = SMDK4X12_UCON_DEFAULT,
.ulcon = SMDK4X12_ULCON_DEFAULT,
.ufcon = SMDK4X12_UFCON_DEFAULT,
},
...
}
*/
exynos4_reserve_mem(); // 预留内存
}
下面我们一一分析上面的每个函数
s5p_init_io 函数
void __init s5p_init_io(struct map_desc *mach_desc,
int size, void __iomem *cpuid_addr)
{
/* initialize the io descriptors we need for initialization */
/*
数组s5p_iodesc中记录了一些物理地址和虚拟地址的对应关系,如:
static struct map_desc s5p_iodesc[] __initdata = {
{
.virtual = (unsigned long)S5P_VA_CHIPID,
.pfn = __phys_to_pfn(S5P_PA_CHIPID),
.length = SZ_4K,
.type = MT_DEVICE,
},
……
}
*/
iotable_init(s5p_iodesc, ARRAY_SIZE(s5p_iodesc)); // 静态映射内存资源
if (mach_desc)
iotable_init(mach_desc, size);
/* detect cpu id and rev.
这里cpuid_addr的值是S5P_VA_CHIPID,从上面的代码可以知道S5P_VA_CHIPID对应的物理地址是S5P_PA_CHIPID,即0x10000000,这个是exynos4412的PRO_ID寄存器,通过在u-boot读取到寄存器0x10000000的值,注意:由于在u-boot中开启了mmu,需要判断这个物理地址0x10000000对应的虚拟机地址是多少,还好,我们的u-boot中将物理地址0x0000_0000 -- 0x1FFF_FFFF 映射到了0x0000_0000 -- 0x1FFF_FFFF,所以我们直接使用命令 md 0x10000000 即可,我试了一下,结果如下:
TINY4412 # md 0x10000000 0x4
10000000: e4412011 08051008 00000009 00000010 . A............
*/
s5p_init_cpu(cpuid_addr); // 读取cpu_id
/*
下面是函数s5p_init_cpu的实现
void __init s5p_init_cpu(void __iomem *cpuid_addr)
{
samsung_cpu_id = __raw_readl(cpuid_addr);
samsung_cpu_rev = samsung_cpu_id & 0xFF;
}
所以samsung_cpu_id的值是 0xe4412011,samsung_cpu_id的值是0x11
*/
s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids));
/*
数组cpu_ids中列出了一些类samsung的soc芯片的信息,关于exynos4412的信息如下:
static struct cpu_table cpu_ids[] __initdata = {
...
{
.idcode = EXYNOS4412_CPU_ID, // 0xE4412200
.idmask = EXYNOS_CPU_MASK, // 0xFFFE0000
.map_io = exynos4_map_io,
.init_clocks = exynos4_init_clocks,
.init_uarts = exynos4_init_uarts,
.init = exynos4_init,
.name = name_exynos4412, // "EXYNOS4412"
},
...
}
*/
}
下面的函数的目的是从cpu_ids中找到与samsung_cpu_id匹配的数组元素
void __init s3c_init_cpu(unsigned long idcode,
struct cpu_table *cputab, unsigned int cputab_size)
{
cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);
/* 下面是函数s3c_lookup_cpu的实现,目的就是从上面的cpu_ids中找到了跟刚才读到的cpu id相等的数组元素
static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode,
struct cpu_table *tab,
unsigned int count)
{
for (; count != 0; count--, tab++) {
if ((idcode & tab->idmask) == (tab->idcode & tab->idmask))
return tab;
}
return NULL;
}
通过上面的循环就可以找到exynos4412对应的cpu_ids
*/
…
cpu->map_io(); // 调用的是exynos4_map_io
}
s3c24xx_init_clocks 函数
void __init s3c24xx_init_clocks(int xtal)
{
…
(cpu->init_clocks)(xtal); // 调用的是exynos4_init_clocks,初始化系统时钟资源
}
s3c24xx_init_uarts
cfg指向了一个数组,no是数组的元素个数,cfg数组中每一项对应了一个uart控制器的寄存器默认配置参数
void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
…
(cpu->init_uarts)(cfg, no); // 调用函数exynos4_init_uarts
}
在文件arch/arm/plat-s5p/include/plat/exynos4.h中:
#define exynos4_init_uarts exynos_common_init_uarts
void __init exynos_common_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
struct s3c2410_uartcfg *tcfg = cfg;
u32 ucnt;
// 下面这个循环的目的是为每一个uart指定之中源
for (ucnt = 0; ucnt < no; ucnt++, tcfg++) {
if (!tcfg->clocks) {
tcfg->has_fracval = 1;
tcfg->clocks = exynos_serial_clocks;
tcfg->clocks_size = ARRAY_SIZE(exynos_serial_clocks);
}
tcfg->flags |= NO_NEED_CHECK_CLKSRC;
}
// s5p_uart_resources数组中存放的是每一个uart控制器的使用的寄存器资源、中断资源
s3c24xx_init_uartdevs("s5pv210-uart", s5p_uart_resources, cfg, no);
}
下面这个函数的目的是填充每一个uart控制器对应的platform_device
void __init s3c24xx_init_uartdevs(char *name,
struct s3c24xx_uart_resources *res,
struct s3c2410_uartcfg *cfg, int no)
{
struct platform_device *platdev;
struct s3c2410_uartcfg *cfgptr = uart_cfgs;
struct s3c24xx_uart_resources *resp;
int uart;
memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
platdev = s3c24xx_uart_src[cfgptr->hwport]; // 每一个uart对应一个platform_device
resp = res + cfgptr->hwport; // 找到编号为cfgptr->hwport的uart对应的res资源地址
s3c24xx_uart_devs[uart] = platdev; // 将来会注册其中的每一个platform_device