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

最底层的硬件操作。

 

 

 

posted on 2013-10-09 15:59  熊猫酒仙是也  阅读(2369)  评论(0编辑  收藏  举报

导航