全部博文(685)
分类: 嵌入式
2014-12-19 12:52:23
分析串口的平台设备注册
这里直接从这里开始分析//arch/arm/mach-s3c2440/mach-mini2440.c
static void __init mini2440_map_io(void)//mini2440_map_io 的定义以及在系统启动过程中是怎么被调用的可以查看 内核时钟部分的分析参数定义
static struct s3c2410_uartcfg mini2440_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, .ucon = 0x3c5, .ulcon = 0x03,//一般串口 .ufcon = 0x51, }, [1] = { .hwport = 1, .flags = 0, .ucon = 0x3c5, .ulcon = 0x03, .ufcon = 0x51, }, /* IR port */ [2] = { .hwport = 2, .flags = 0, .ucon = 0x3c5, .ulcon = 0x43,//红外方式 .ufcon = 0x51, } };
接着分析s3c24xx_init_uarts函数的具体执行
void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) { if (cpu == NULL) return;
if (cpu->init_uarts == NULL) {
printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n");
} else
(cpu->init_uarts)(cfg, no);//这里就是调用cpu_table中init_uarts函数指针
}
cpu_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->init_uarts 函数指针具体是调用的哪个函数,这就要去看cpu_table这个全局结构体的定义了:
static struct cpu_table cpu_ids[] __initdata = {
{
.idcode = 0x32410000,
.idmask = 0xffffffff,
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
.init = s3c2410_init,
.name = name_s3c2410
},
{
.idcode = 0x32410002,
.idmask = 0xffffffff,
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
.init = s3c2410a_init,
.name = name_s3c2410a
},
{
.idcode = 0x32440000,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts, //好在这里我们终于找到了具体函数的形式,接着我们对其进行剖析 .init = s3c2440_init,
.name = name_s3c2440
},
{
.idcode = 0x32440001,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2440_init,
.name = name_s3c2440a
},
{
.idcode = 0x32440aaa,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2442_init,
.name = name_s3c2442
},
{
.idcode = 0x32440aab,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2442_init,
.name = name_s3c2442b
},
{
.idcode = 0x32412001,
.idmask = 0xffffffff,
.map_io = s3c2412_map_io,
.init_clocks = s3c2412_init_clocks,
.init_uarts = s3c2412_init_uarts,
.init = s3c2412_init,
.name = name_s3c2412,
},
{ /* a newer version of the s3c2412 */
.idcode = 0x32412003,
.idmask = 0xffffffff,
.map_io = s3c2412_map_io,
.init_clocks = s3c2412_init_clocks,
.init_uarts = s3c2412_init_uarts,
.init = s3c2412_init,
.name = name_s3c2412,
},
{
.idcode = 0x32443001,
.idmask = 0xffffffff,
.map_io = s3c2443_map_io,
.init_clocks = s3c2443_init_clocks,
.init_uarts = s3c2443_init_uarts,
.init = s3c2443_init,
.name = name_s3c2443,
},
{
.idcode = 0x0, /* S3C2400 doesn't have an idcode */
.idmask = 0xffffffff,
.map_io = s3c2400_map_io,
.init_clocks = s3c2400_init_clocks,
.init_uarts = s3c2400_init_uarts,
.init = s3c2400_init,
.name = name_s3c2400
},
};
void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no) //arch/arm/plat-s3c24xx/s3c244x.c第一传入platform_device名字则不用说;第二个s3c2410_uart_resources参数,是怎么定义的呢?/* Serial port registrations */
static struct resource s3c2410_uart0_resource[] = { [0] = { .start = S3C2410_PA_UART0, .end = S3C2410_PA_UART0 + 0x3fff, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_S3CUART_RX0, .end = IRQ_S3CUART_ERR0, .flags = IORESOURCE_IRQ, } };
static struct resource s3c2410_uart1_resource[] = {
[0] = {
.start = S3C2410_PA_UART1,
.end = S3C2410_PA_UART1 + 0x3fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_S3CUART_RX1,
.end = IRQ_S3CUART_ERR1,
.flags = IORESOURCE_IRQ,
}
};
static struct resource s3c2410_uart2_resource[] = {
[0] = {
.start = S3C2410_PA_UART2,
.end = S3C2410_PA_UART2 + 0x3fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_S3CUART_RX2,
.end = IRQ_S3CUART_ERR2,
.flags = IORESOURCE_IRQ,
}
};
static struct resource s3c2410_uart3_resource[] = {
[0] = {
.start = S3C2443_PA_UART3,
.end = S3C2443_PA_UART3 + 0x3fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_S3CUART_RX3,
.end = IRQ_S3CUART_ERR3,
.flags = IORESOURCE_IRQ,
},
};
struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = { //arch/arm/plat-s3c24xx/devs.c
[0] = {
.resources = s3c2410_uart0_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart0_resource),
},
[1] = {
.resources = s3c2410_uart1_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart1_resource),
},
[2] = {
.resources = s3c2410_uart2_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart2_resource),
},
[3] = {
.resources = s3c2410_uart3_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart3_resource),
},
};
/* yart devices */
static struct platform_device s3c24xx_uart_device0 = {
.id = 0,
};
static struct platform_device s3c24xx_uart_device1 = {
.id = 1,
};
static struct platform_device s3c24xx_uart_device2 = {
.id = 2,
};
static struct platform_device s3c24xx_uart_device3 = {
.id = 3,
};
struct platform_device *s3c24xx_uart_src[4] = { //平台设备指针数组
&s3c24xx_uart_device0, //
&s3c24xx_uart_device1,
&s3c24xx_uart_device2,
&s3c24xx_uart_device3,
};
struct platform_device *s3c24xx_uart_devs[4] = {
};
第三个参数cfg,可见最上面的分析,即为struct s3c2410_uartcfg mini2440_uartcfgs
接着分析s3c24xx_init_uartdevs平台设备的初始化过程
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; /*将arch/arm/mach-s3c2440/mach-mini2440.c下的mini2440_uartcfgs
拷入cfgptr指针,亦即装入uart_cfgs全局变量 */ memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
platdev = s3c24xx_uart_src[cfgptr->hwport];//取出已定义的platform_device指针
resp = res + cfgptr->hwport; //取出各设备相关的设备资源数组
s3c24xx_uart_devs[uart] = platdev; //最后再存入已定义好的设备指针数组
platdev->name = name;
platdev->resource = resp->resources;
platdev->num_resources = resp->nr_resources;
platdev->dev.platform_data = cfgptr; //将配置信息存入平台结构中,驱动的probe函数中要用到
}
nr_uarts = no;
}
以上部分为平台设备的定义及初始化的坎坷过程
下面接着分析,平台设备的真正注册流程:
static int __init s3c_arch_init(void) { int ret;// do the correct init for cpuif (cpu == NULL) panic("s3c_arch_init: NULL cpu\n");ret = (cpu->init)(); if (ret != 0) return ret;ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); //在此处进行注册 return ret; }
从这里开始我们再分析s3c_arch_init函数又是如何被调用的呢,搜遍Source Insight只找到arch_initcall(s3c_arch_init); 这里的arch_initcall是一个宏定义
该宏定义位于..\include\linux\init.h,原型为: #define arch_initcall(fn) __define_initcall("3",fn,3)
而宏定义__define_initcall也位于该文件中,原型为: #define __define_initcall(level,fn,id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn |
static void __init do_initcalls(void) //init/main.c { initcall_t *call;
for (call = __early_initcall_end; call < __initcall_end; call++)
do_one_initcall(*call);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
执行顺序是什么呢?原来是按Systems.map中映射顺序执行。