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 < 0) { printk(KERN_ERR "failed to register UART driver\n"); return -1; } return 0; }
在模块加载函数中调用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 = 9600; 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 = 0; 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 = 500; /* .5 seconds */ port->closing_wait = 30000; /* 30 seconds */ tasklet_init(&state->tlet, uart_tasklet_action, (unsigned long)state);//初始化tasklet,即中断的底半部机制 } retval = tty_register_driver(normal);//注册tty设备 if (retval >= 0) 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 < 0) 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 < 0) 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 < 0) dev_err(&dev->dev, "failed to add cpufreq notifier\n"); return 0; 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 != 0) return 0; 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 = 1; 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, 0); 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, 0); if (ret < 0) port->irq = 0; else { port->irq = ret; ourport->rx_irq = ret; ourport->tx_irq = ret + 1; } ret = platform_get_irq(platdev, 1); if (ret > 0) 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 0; }
《《======总结=======》》
到这一步所有的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
最底层的硬件操作。