对于S5PV210 UART驱动来说,主要关心的就是drivers/serial下的samsung.c和s5pv210.c连个文件。
由drivers/serial/Kconfig:
config SERIAL_SAMSUNG
depends on ARM && PLAT_SAMSUNG
config SERIAL_S5PV210
depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442) && SERIAL_SAMSUNG_CONSOLE
可以看出模块的依赖关系,先加载samsung.ko,然后再加载s5pv210.ko。
所以串口的初始化的简要过程如下:
samsung.c中模块加载函数s3c24xx_serial_modinit调用uart_register_driver(&s3c24xx_uart_drv),注册了s3c24xx_uart_drv这个uart_driver;
s5pv210.c中模块加载函数s5p_serial_init ----> s3c24xx_serial_init(&s5p_serial_driver, *s5p_uart_inf) ----> platform_driver_register(drv) 注册了s5p_serial_driver这个平台驱动,
有了平台驱动后,当平台设备与平台驱动match之后,调用s5p_serial_probe ----> s3c24xx_serial_probe(pdev, s5p_uart_inf[pdev->id]) ----> s3c24xx_serial_init_port初始化UART端口
> uart_add_one_port添加端口
《《《《====接下来具体分析====》》》》
一、注册uart_driver
/* 模块加载函数*/
static int __init s3c24xx_serial_modinit(void)
{
int ret; ret = uart_register_driver(&s3c24xx_uart_drv);/*注册uart_driver*/
if (ret < ) {
printk(KERN_ERR "failed to register UART driver\n");
return -;
} return ;
}
在模块加载函数中调用serial_core.c中的uart_register_driver()注册s3c24xx_uart_drv这个uart_driver,实际上在uart_register_driver()中包含了tty_register_driver(),代码如下:
/**
* uart_register_driver - register a driver with the uart core layer
* @drv: low level driver structure
*
* Register a uart driver with the core driver. We in turn register
* with the tty layer, and initialise the core driver per-port state.
*
* We have a proc file in /proc/tty/driver which is named after the
* normal driver.
*
* drv->port should be NULL, and the per-port structures should be
* registered using uart_add_one_port after this call has succeeded.
*/
/*实际上是填充uart_driver结构体*/
int uart_register_driver(struct uart_driver *drv)
{
/*声明一个tty_driver,接下来填充其中的成员,并使uart_driver中的tty_driver指向这个结构*/
struct tty_driver *normal;
int i, retval; BUG_ON(drv->state); /*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle.
*/
/*分配设备私有信息结构体的内存空间,并初始化为零*/
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if (!drv->state)
goto out; normal = alloc_tty_driver(drv->nr);/*分配tty驱动*/
if (!normal)
goto out_kfree; drv->tty_driver = normal;/*填充uart_driver中封装的tty_driver,使其指向分配好的tty驱动*/ /*初始化tty_driver结构体*/
normal->owner = drv->owner;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;//tty驱动的类型
normal->subtype = SERIAL_TYPE_NORMAL;//tty驱动的子类
normal->init_termios = tty_std_termios;//初始的termios,即初始的线路设置,用来提供一个线路设置集合
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;//控制模式
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = ;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;//~~~~~~
tty_set_operations(normal, &uart_ops);//设置tty驱动操作,normal->ops=&uart_ops /*
* Initialise the UART state(s).初始化UART状态
*/
for (i = ; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port; tty_port_init(port);//tty端口初始化
port->close_delay = ; /* .5 seconds */
port->closing_wait = ; /* 30 seconds */
tasklet_init(&state->tlet, uart_tasklet_action,
(unsigned long)state);//初始化tasklet,即中断的底半部机制
} retval = tty_register_driver(normal);//注册tty设备
if (retval >= )
return retval; put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
}
二、注册平台驱动
s5p_serial_init ----> s3c24xx_serial_init(&s5p_serial_driver, *s5p_uart_inf) ----> platform_driver_register(drv)
int s3c24xx_serial_init(struct platform_driver *drv,
struct s3c24xx_uart_info *info)
{
dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); #ifdef CONFIG_PM
drv->suspend = s3c24xx_serial_suspend;
drv->resume = s3c24xx_serial_resume;
#endif return platform_driver_register(drv);
}
直接调用platform_driver_register,注册了 s5p_serial_driver这个平台驱动。
三、平台驱动的探测函数probe()
因为把uart驱动注册为platform驱动,当平台驱动与平台设备进行匹配的时候会调用平台总线的match函数,匹配成功后就会调用平台驱动的xxx_probe()函数来进行一系列的初始化工作。
UART驱动的probe()调用过程如下:
s5p_serial_probe ----> s3c24xx_serial_probe(pdev, s5p_uart_inf[pdev->id])
最终调用的是s3c24xx_serial_probe();
详细的代码分析如下:
/* Device driver serial port probe */
int s3c24xx_serial_probe(struct platform_device *dev,
struct s3c24xx_uart_info *info)
{
struct s3c24xx_uart_port *ourport;//s3c24xx_uart_port封装了uart_port
int ret; dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, dev->id); if (dev->id >= ARRAY_SIZE(s3c24xx_serial_ports)) {
dev_err(&dev->dev, "unsupported device id %d\n", dev->id);
return -ENODEV;
} ourport = &s3c24xx_serial_ports[dev->id];//s3c24xx_serial_ports是s3c24xx_uart_port结构体类型的
ourport->channelnum= dev->id; dbg("%s: initialising port %p...\n", __func__, ourport); ret = s3c24xx_serial_init_port(ourport, info, dev);//初始化UART端口 ------->
if (ret < )
goto probe_err; dbg("%s: adding port\n", __func__);
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);//添加端口,配置端口,构造与本端口对应的设备节点
platform_set_drvdata(dev, &ourport->port);//将ourport->port保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了 ret = device_create_file(&dev->dev, &dev_attr_clock_source);//添加设备属性
if (ret < )
printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
/*
注册通知链,当CPU频率改变时调用函数s3c24xx_serial_cpufreq_transition,最终调用函数
s3c24xx_serial_set_termios设置波特率等
*/
ret = s3c24xx_serial_cpufreq_register(ourport);//动态频率调节初始化
if (ret < )
dev_err(&dev->dev, "failed to add cpufreq notifier\n"); return ; probe_err:
return ret;
}
其中s3c24xx_serial_init_port函数的分析如下:
/* s3c24xx_serial_init_port
*
* initialise a single serial port from the platform device given
*/
/*初始化UART端口,建立各结构体的联系,申请中断,IO资源。复位端口*/
static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
struct s3c24xx_uart_info *info,
struct platform_device *platdev)
{
struct uart_port *port = &ourport->port;
struct s3c2410_uartcfg *cfg;
struct resource *res;
int ret; dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev); if (platdev == NULL)
return -ENODEV; //s3c24xx_init_uarts --> s5pv210_init_uarts --> s5pv210_common_init_uarts --> s3c24xx_init_uartdevs -->>platdev->dev.platform_data = cfgptr
//在该函数中将cfg挂到platdev->dev.platform_data上。
cfg = s3c24xx_dev_to_cfg(&platdev->dev);//获取cfg if (port->mapbase != )
return ; if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) {
printk(KERN_ERR "%s: port %d bigger than %d\n", __func__,
cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS);
return -ERANGE;
} /* setup info for port */
port->dev = &platdev->dev;//让端口uart_port的成员dev指向平台设备
//ourport的结构体类型为struct s3c24xx_uart_port不是uart_port。
//此处的info的结构体类型为s3c24xx_uart_info在文件samsung.h 中定义,s5pv210.c中初始化。不是uart_info。
ourport->info = info; /* copy the info in from provided structure */
ourport->port.fifosize = info->fifosize; dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport); port->uartclk = ; if (cfg->uart_flags & UPF_CONS_FLOW) {
dbg("s3c24xx_serial_init_port: enabling flow control\n");
port->flags |= UPF_CONS_FLOW;
} /* sort our the physical and virtual addresses for each UART */
//获取IO内存
res = platform_get_resource(platdev, IORESOURCE_MEM, );
if (res == NULL) {
printk(KERN_ERR "failed to find memory resource for uart\n");
return -EINVAL;
} dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); port->mapbase = res->start;
port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000);
ret = platform_get_irq(platdev, );
if (ret < )
port->irq = ;
else {
port->irq = ret;
ourport->rx_irq = ret;
ourport->tx_irq = ret + ;
} ret = platform_get_irq(platdev, );
if (ret > )
ourport->tx_irq = ret; ourport->clk = clk_get(&platdev->dev, "uart");//获取名为"uart"的clk dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
port->mapbase, port->membase, port->irq,
ourport->rx_irq, ourport->tx_irq, port->uartclk); /* reset the fifos (and setup the uart) */
s3c24xx_serial_resetport(port, cfg); //调用函数info->reset_port复位串口 s3c_setup_uart_cfg_gpio(cfg->hwport); return ;
}
《《======总结=======》》
到这一步所有的tty和uart初始化的部分算是完成了。也就是完成了下图的构建:
但是目前还不能进行read、write操作。
因为还有一个重要的函数tty_open没有分析,作为tty设备的uart,这个tty_open函数也是非常重要的,下篇分析。
初始化的工作主要是
1.初始化uart_driver结构体,包括初始化uart_driver结构体中的tty_driver uart_state。
2.uart_state部分的初始化主要是初始化其中的uart_port,这部分的初始化在probe函数总完成
修改驱动需要设计的数据结构
1.uart_driver
uart_driver中的数据用于初始化tty_driver
2.s3c24xx_uart_info
用于初始化uart_port
3.s3c24xx_uart_port或者说uart_port。
uart_port的初始化,在s3c24xx的uart驱动中uart_port是内嵌在s3c24xx_uart_port中的
4.uart_ops
最底层的硬件操作。