zoukankan      html  css  js  c++  java
  • 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
    ");
            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)
    ", 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
    ", dev, info, dev->id);
    
        if (dev->id >= ARRAY_SIZE(s3c24xx_serial_ports)) {
            dev_err(&dev->dev, "unsupported device id %d
    ", 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...
    ", __func__, ourport);
    
        ret = s3c24xx_serial_init_port(ourport, info, dev);//初始化UART端口 ------->
        if (ret < 0)
            goto probe_err;
    
        dbg("%s: adding port
    ", __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.
    ", __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 "); 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
    ", 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
    ", __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)...
    ", port, cfg->hwport);
    
        port->uartclk = 1;
    
        if (cfg->uart_flags & UPF_CONS_FLOW) {
            dbg("s3c24xx_serial_init_port: enabling flow control
    ");
            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
    ");
            return -EINVAL;
        }
    
        dbg("resource %p (%lx..%lx)
    ", 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
    ",
            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

    最底层的硬件操作。

  • 相关阅读:
    能帮你找到网页设计灵感的16个网站
    Alpha和索引色透明
    CSS2.0中最常用的18条技巧
    汇编指令CPUID
    ewebeditor漏洞解決方法
    关于SQL SERVER建立索引需要注意的问题
    Apple QuickTime
    免杀修改特征码需要掌握的汇编知识
    【我翻译的文章】理解和应用F#中的“use”语法
    【我翻译的文章】Promesh.NET:一个.NET的MVC Web框架
  • 原文地址:https://www.cnblogs.com/hello2mhb/p/3344159.html
Copyright © 2011-2022 走看看