ARM-Linux S5PV210 UART驱动(4)----串口驱动初始化过程
对于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
最底层的硬件操作。
ARM-Linux S5PV210 UART驱动(4)----串口驱动初始化过程的更多相关文章
- 【驱动】USB驱动实例·串口驱动·键盘驱动
Preface USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_driver结构描述. 对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动 ...
- 【驱动】USB驱动实例·串口驱动·键盘驱动【转】
转自:http://www.cnblogs.com/lcw/p/3159370.html Preface USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_drive ...
- ARM linux解析之压缩内核zImage的启动过程
ARM linux解析之压缩内核zImage的启动过程 semilog@163.com 首先,我们要知道在zImage的生成过程中,是把arch/arm/boot/compressed/head.s ...
- ARM-Linux S5PV210 UART驱动(3)----串口核心层、关键结构体、接口关系
尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实 ...
- Linux驱动之串口(UART)
<uart驱动程序概述> 在嵌入式Linux系统中,串口被看成终端设备,终端设备(tty)的驱动程序分为3部分: tty_core tty_disicipline tty_driver ...
- linux UART串口驱动开发文档
转:http://www.360doc.com/content/10/0417/18/829197_23519037.shtml linux UART串口驱动开发文档时间:2010-01-09 14: ...
- ARM-Linux S5PV210 UART驱动(5)----串口的open操作(tty_open、uart_open)
串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了. 那接下来uart的操作是如何进行的呢? 操作硬件之前都是要先open设备,先来分析下这里的open函数具体做了 ...
- Smart210学习记录------linux串口驱动
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...
- linux串口驱动分析
linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...
随机推荐
- 探讨PHP页面跳转几种实现技巧
PHP被许多程序员用来开发WEB的首选语言.在实际开发中,网站的各项功能都可以通过PHP语言的编写来满足,比如PHP页面跳转这一方法. 探讨PHP变量解析顺序如何获取提交数据 深入解读PHP运行机制 ...
- MySQL服务器的SQL模式 (转)
转自: http://blog.csdn.net/kumu_linux/article/details/8185912 sql_mode的系统变量可以调控MySQL的SQL模式 任何一个客户端可以在不 ...
- ORA-01034: ORACLE not available
ora-01034:oracle not available ora-27101:shared mermory realm does not exist 解决办法: ...
- [BigData]关于HDFS的伪分布式安装和虚拟机网络的配置
[BigData]关于Hadoop学习笔记第一天(段海涛老师)(三) 视频2: hadoop的应用在电商,"浏览了该商品的人还看了","浏览了该商品的人最终购买的&quo ...
- Netbackup磁带过期处理
bpexpdate -m <mediaid> -d 0 如果不通过,看是否被其他media server写入数据.使用 nbemmcmd -listhost 查看所有media serve ...
- response小结(五)—通过response实现请求重定向
请求重定向指的是一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向.302状态码和location头即可实现重定向. 请求重定向最常见的应用场景就是用户登录. 下面 ...
- ios开发入门篇(一):创建工程
突然心血来潮,想写点技术方面的东西,做了ios也有好几年了,就简单的写个ios开发的技术博客,希望有人能用得到. 今天就先从创建一个Hellow World工程开始 一:首先打开xcode然后单击Cr ...
- AjaxFileUpload 在C#中应用
一.前台页面 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head ru ...
- display的小故事
实在是想不出来到底整个什么题目好..姑且先整这个吧.. 本文不是讲解display这个牛逼css属性的(讲不好才是真的!),主要是分享一下一些小Tips. display:table-cell wid ...
- 【转】揭开Socket编程的面纱
对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问: 1. 什么是TCP/IP.UDP?2. Sock ...