zoukankan      html  css  js  c++  java
  • MTK UART串口调试

    一、UART初始化

    1. kernel-3.18/drivers/misc/mediatek/uart/uart.c

     1 static int __init mtk_uart_init(void)
     2 {
     3     int ret = 0;
     4 
     5     tx_history.buffer = kzalloc(UART_HISTORY_DATA_SIZE, GFP_KERNEL);
     6     rx_history.buffer = kzalloc(UART_HISTORY_DATA_SIZE, GFP_KERNEL);
     7     tx_history.index = -1;
     8     rx_history.index = -1;
     9 
    10     if (!tx_history.buffer || !rx_history.buffer)
    11         return -ENOMEM;
    12 
    13 #ifndef CONFIG_MTK_SERIAL_CONSOLE
    14     mtk_uart_init_ports();
    15 #endif
    16 
    17 #if defined(ENABLE_SYSFS)
    18     mtk_uart_sysfs();
    19 #endif
    20 
    21     ret = uart_register_driver(&mtk_uart_drv);
    22 
    23     if (ret)
    24         return ret;
    25 
    26     ret = platform_driver_register(&mtk_uart_dev_drv);
    27 
    28     if (ret) {
    29         uart_unregister_driver(&mtk_uart_drv);
    30         return ret;
    31     }
    32 #ifdef CONFIG_PM
    33     mtk_uart_init_ops();
    34 #endif
    35 
    36 #ifdef ENABLE_RAW_DATA_DUMP
    37     mtk_uart_init_debug_spinlock();
    38 #endif
    39     spin_lock_init(&mtk_uart_bt_lock);
    40     return ret;
    41 }

     第 21 行调用 uart_register_driver 函数注册 mtk_uart_drv。

    mtk_uart_drv 结构如下:

     1 static struct uart_driver mtk_uart_drv = {
     2     .owner = THIS_MODULE,
     3     .driver_name = DRV_NAME,
     4     .dev_name = "ttyMT",
     5     .major = UART_MAJOR,
     6     .minor = UART_MINOR,
     7     .nr = UART_NR,
     8 #if defined(CONFIG_MTK_SERIAL_CONSOLE) && !defined(CONFIG_MTK_SERIAL_MODEM_TEST)
     9     .cons = &mtk_uart_console,
    10 #endif
    11 };
     1 static struct console mtk_uart_console = {
     2     .name = "ttyMT",
     3 #if !defined(CONFIG_MTK_SERIAL_MODEM_TEST)
     4     /*don't configure UART4 as console */
     5     .write = mtk_uart_console_write,
     6     .setup = mtk_uart_console_setup,
     7 #endif
     8     .device = uart_console_device,
     9     .flags = CON_PRINTBUFFER,
    10     .index = -1,
    11     .data = &mtk_uart_drv,
    12 };

    uart_register_driver 函数定义在 kernel-3.18/drivers/tty/serial/serial_core.c 文件中,

     1 /**
     2  *    uart_register_driver - register a driver with the uart core layer
     3  *    @drv: low level driver structure
     4  *
     5  *    Register a uart driver with the core driver.  We in turn register
     6  *    with the tty layer, and initialise the core driver per-port state.
     7  *
     8  *    We have a proc file in /proc/tty/driver which is named after the
     9  *    normal driver.
    10  *
    11  *    drv->port should be NULL, and the per-port structures should be
    12  *    registered using uart_add_one_port after this call has succeeded.
    13  */
    14 int uart_register_driver(struct uart_driver *drv)
    15 {
    16     struct tty_driver *normal;
    17     int i, retval;
    18 
    19     BUG_ON(drv->state);
    20 
    21     /*
    22      * Maybe we should be using a slab cache for this, especially if
    23      * we have a large number of ports to handle.
    24      */
    25     drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
    26     if (!drv->state)
    27         goto out;
    28 
    29     normal = alloc_tty_driver(drv->nr);
    30     if (!normal)
    31         goto out_kfree;
    32 
    33     drv->tty_driver = normal;
    34 
    35     normal->driver_name    = drv->driver_name;
    36     normal->name        = drv->dev_name;
    37     normal->major        = drv->major;
    38     normal->minor_start    = drv->minor;
    39     normal->type        = TTY_DRIVER_TYPE_SERIAL;
    40     normal->subtype        = SERIAL_TYPE_NORMAL;
    41     normal->init_termios    = tty_std_termios;
    42     normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
    43     normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
    44     normal->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
    45     normal->driver_state    = drv;
    46     tty_set_operations(normal, &uart_ops);
    47 
    48     /*
    49      * Initialise the UART state(s).
    50      */
    51     for (i = 0; i < drv->nr; i++) {
    52         struct uart_state *state = drv->state + i;
    53         struct tty_port *port = &state->port;
    54 
    55         tty_port_init(port);
    56         port->ops = &uart_port_ops;
    57         port->close_delay     = HZ / 2;    /* .5 seconds */
    58         port->closing_wait    = 30 * HZ;/* 30 seconds */
    59     }
    60 
    61     retval = tty_register_driver(normal);
    62     if (retval >= 0)
    63         return retval;
    64 
    65     for (i = 0; i < drv->nr; i++)
    66         tty_port_destroy(&drv->state[i].port);
    67     put_tty_driver(normal);
    68 out_kfree:
    69     kfree(drv->state);
    70 out:
    71     return -ENOMEM;
    72 }

    第 33行,uart驱动与tty_driver关联起来。

    第 43 行,设置 uart 的初始波特率。

    第 46 行,将 tty_driver 的操作集统一设为了 uart_ops.这样就使得从用户空间下来的操作可以找到正确的 serial_core 的操作函数。Uart_ops 如下:

    static const struct tty_operations uart_ops = {
        .open        = uart_open,
        .close        = uart_close,
        .write        = uart_write,
        .put_char    = uart_put_char,
        .flush_chars    = uart_flush_chars,
        .write_room    = uart_write_room,
        .chars_in_buffer= uart_chars_in_buffer,
        .flush_buffer    = uart_flush_buffer,
        .ioctl        = uart_ioctl,
        .throttle    = uart_throttle,
        .unthrottle    = uart_unthrottle,
        .send_xchar    = uart_send_xchar,
        .set_termios    = uart_set_termios,
        .set_ldisc    = uart_set_ldisc,
        .stop        = uart_stop,
        .start        = uart_start,
        .hangup        = uart_hangup,
        .break_ctl    = uart_break_ctl,
        .wait_until_sent= uart_wait_until_sent,
    #ifdef CONFIG_PROC_FS
        .proc_fops    = &uart_proc_fops,
    #endif
        .tiocmget    = uart_tiocmget,
        .tiocmset    = uart_tiocmset,
        .get_icount    = uart_get_icount,
    #ifdef CONFIG_CONSOLE_POLL
        .poll_init    = uart_poll_init,
        .poll_get_char    = uart_poll_get_char,
        .poll_put_char    = uart_poll_put_char,
    #endif
    };

    第 56 行,uart_port_ops 结构体如下:

    static const struct tty_port_operations uart_port_ops = {
        .activate    = uart_port_activate,
        .shutdown    = uart_port_shutdown,
        .carrier_raised = uart_carrier_raised,
        .dtr_rts    = uart_dtr_rts,
    };

    第 61 行,调用 tty_register_driver 函数,其所在文件为 kernel-3.18/drivers/tty/tty_io.c。

     1 /*
     2  * Called by a tty driver to register itself.
     3  */
     4 int tty_register_driver(struct tty_driver *driver)
     5 {
     6     int error;
     7     int i;
     8     dev_t dev;
     9     struct device *d;
    10 
    11     if (!driver->major) {
    12         error = alloc_chrdev_region(&dev, driver->minor_start,
    13                         driver->num, driver->name);
    14         if (!error) {
    15             driver->major = MAJOR(dev);
    16             driver->minor_start = MINOR(dev);
    17         }
    18     } else {
    19         dev = MKDEV(driver->major, driver->minor_start);
    20         error = register_chrdev_region(dev, driver->num, driver->name);
    21     }
    22     if (error < 0)
    23         goto err;
    24 
    25     if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
    26         error = tty_cdev_add(driver, dev, 0, driver->num);
    27         if (error)
    28             goto err_unreg_char;
    29     }
    30 
    31     mutex_lock(&tty_mutex);
    32     list_add(&driver->tty_drivers, &tty_drivers);
    33     mutex_unlock(&tty_mutex);
    34 
    35     if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
    36         for (i = 0; i < driver->num; i++) {
    37             d = tty_register_device(driver, i, NULL);
    38             if (IS_ERR(d)) {
    39                 error = PTR_ERR(d);
    40                 goto err_unreg_devs;
    41             }
    42         }
    43     }
    44     proc_tty_register_driver(driver);
    45     driver->flags |= TTY_DRIVER_INSTALLED;
    46     return 0;
    47 
    48 err_unreg_devs:
    49     for (i--; i >= 0; i--)
    50         tty_unregister_device(driver, i);
    51 
    52     mutex_lock(&tty_mutex);
    53     list_del(&driver->tty_drivers);
    54     mutex_unlock(&tty_mutex);
    55 
    56 err_unreg_char:
    57     unregister_chrdev_region(dev, driver->num);
    58 err:
    59     return error;
    60 }
    61 EXPORT_SYMBOL(tty_register_driver);

    MKDEV 宏定义在 include/linux/kdev_t.h 头文件,#define MKDEV(ma,mi) (((ma)<< MINORBITS) | (mi)) 作用是将主设备号和次设备号转换成 dev_t 类型。

    register_chrdev_region 为一个字符驱动获取一个或多个设备编号。

    调用 cdev_add() 函数,将cdev添加到系统中去。

    因normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 故没有调用tty_register_device来创建ttyMT0~ttyMT3设备。

    调用proc_tty_register_driver创建/proc/tty/driver/mtk-uart文件。其driver->ops->proc_fops 为uart_ops结构体中的uart_proc_fops函数。

     回到 uart_register_driver函数,调用 put_tty_driver 函数,当注册失败时,判断引用计数注销驱动。

    二、回到 mtk_uart_init 函数,调用 platform_driver_register 函数注册 mtk_uart_dev_drv。

    static struct platform_driver mtk_uart_dev_drv = {
        .probe = mtk_uart_probe,
        .remove = mtk_uart_remove,
    #ifdef CONFIG_PM
        .suspend = mtk_uart_suspend,
        .resume = mtk_uart_resume,
    #endif
        .driver = {
               .name = DRV_NAME,
               .owner = THIS_MODULE,
    #ifdef CONFIG_OF
               .of_match_table = apuart_of_ids,
    #endif
    #ifdef CONFIG_PM
               .pm = &mtk_uart_pm_ops,
    #endif
    
               }
    };

    mtk_uart_probe函数如下:

    static int mtk_uart_probe(struct platform_device *pdev)
    {
        struct mtk_uart *uart;
        int err;
    #if !defined(CONFIG_FPGA_EARLY_PORTING)
    #if !defined(CONFIG_MTK_CLKMGR)
        static const char * const clk_uart_name[] = {
            "uart0-main",
            "uart1-main",
            "uart2-main",
            "uart3-main",
            "uart4-main",
        };
        struct mtk_uart_setting *uart_setting = NULL;
    #endif
    #if !defined(CONFIG_MTK_LEGACY)
        /* for GPIO pinctrl */
        struct pinctrl *ppinctrl = NULL;
    #endif
    #endif /* !defined(CONFIG_FPGA_EARLY_PORTING) */
    
    #ifdef CONFIG_OF
        if (pdev->dev.of_node) {
            struct device_node *node = pdev->dev.of_node;
    
            err = of_property_read_u32(node, "cell-index", &pdev->id);
            if (err)
                pr_err("[DTS] get uart platform_device id fail!!
    ");
        }
        if (pdev->id >= UART_NR) {
            pr_err("DTS cell ID %d > UART nuber %d
    ", pdev->id, UART_NR);
            return -ENODEV;
        }
    #endif
        uart = &mtk_uarts[pdev->id];
        MSG_FUNC_ENTRY();
    
    /* For clock setting */
    #if !defined(CONFIG_MTK_CLKMGR) && !defined(CONFIG_FPGA_EARLY_PORTING)
        uart_setting = get_uart_default_settings(pdev->id);
        uart_setting->clk_uart_main = devm_clk_get(&pdev->dev, clk_uart_name[pdev->id]);
        if (IS_ERR(uart_setting->clk_uart_main)) {
            pr_err("[UART%d][CCF]cannot get %s clock. ptr_err:%ld
    ", pdev->id, clk_uart_name[pdev->id]
                   , PTR_ERR(uart_setting->clk_uart_main));
            return PTR_ERR(uart_setting->clk_uart_main);
        }
        pr_debug("[UART%d][CCF]clk_uart%d_main:%p
    ", pdev->id, pdev->id, uart_setting->clk_uart_main);
    
        if (pdev->id == 0) {
            struct clk *clk_uart0_dma = devm_clk_get(&pdev->dev, "uart-apdma");
    
            if (IS_ERR(clk_uart0_dma)) {
                pr_err("[UART][CCF]cannot get clk_uart0_dma clock. ptr_err:%ld
    ", PTR_ERR(clk_uart0_dma));
                return PTR_ERR(clk_uart0_dma);
            }
            set_uart_dma_clk(pdev->id, clk_uart0_dma);
            pr_debug("[UART][CCF]clk_uart0_dma:%p
    ", clk_uart0_dma);
        }
    #else /* !defined(CONFIG_MTK_CLKMGR) && !defined(CONFIG_FPGA_EARLY_PORTING) */
        pr_debug("[UART][CCF]mtk_uart_probe CONFIG_MTK_CLKMGR or CONFIG_FPGA_EARLY_PORTING is defined!
    ");
    #endif /*!defined(CONFIG_MTK_CLKMGR) && !defined(CONFIG_FPGA_EARLY_PORTING) */
    
    /* For GPIO setting */
    #if !defined(CONFIG_MTK_LEGACY) && !defined(CONFIG_FPGA_EARLY_PORTING)
        ppinctrl = devm_pinctrl_get(&pdev->dev);
        if (IS_ERR(ppinctrl)) {
            err = PTR_ERR(ppinctrl);
            pr_err("[UART%d][PinC]cannot find pinctrl. ptr_err:%ld
    ", pdev->id, PTR_ERR(ppinctrl));
            set_uart_pinctrl(pdev->id, NULL);
        } else {
            set_uart_pinctrl(pdev->id, ppinctrl);
        }
        pr_debug("[UART%d][PinC]set idx:%d, ppinctrl:%p
    ", pdev->id, pdev->id, ppinctrl);
    #else /* !defined(CONFIG_MTK_LEGACY) && !defined(CONFIG_FPGA_EARLY_PORTING) */
        pr_debug("[UART][PinC]mtk_uart_probe CONFIG_MTK_LEGACY or CONFIG_FPGA_EARLY_PORTING is defined!
    ");
    #endif /* !defined(CONFIG_MTK_LEGACY) && !defined(CONFIG_FPGA_EARLY_PORTING) */
    
        if (uart->setting->support_33bits) {
            pdev->dev.coherent_dma_mask = DMA_BIT_MASK(33);
            if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(33))) {
                dev_err(&pdev->dev, "dma_set_mask return error.
    ");
                return -EINVAL;
            }
        } else {
            pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
        }
        uart->port.dev = &pdev->dev;
        err = uart_add_one_port(&mtk_uart_drv, &uart->port);
        if (!err)
            platform_set_drvdata(pdev, uart);
    
    #if defined(ENABLE_VFIFO)
        err = mtk_uart_vfifo_create(uart);
        if (err) {
            mtk_uart_vfifo_delete(uart);
            DEV_ERR("create vff buffer fail:%d
    ", err);
        }
    #endif
        return err;
    }

    调用uart_add_one_port向串口驱动添加一个端口。其具体实现在Kernel-3.18/drivers/tty/serial/Serial_core.c

    /**
     *    uart_add_one_port - attach a driver-defined port structure
     *    @drv: pointer to the uart low level driver structure for this port
     *    @uport: uart port structure to use for this port.
     *
     *    This allows the driver to register its own uart_port structure
     *    with the core driver.  The main purpose is to allow the low
     *    level uart drivers to expand uart_port, rather than having yet
     *    more levels of structures.
     */
    int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
    {
        struct uart_state *state;
        struct tty_port *port;
        int ret = 0;
        struct device *tty_dev;
        int num_groups;
    
        BUG_ON(in_interrupt());
    
        if (uport->line >= drv->nr)
            return -EINVAL;
    
        state = drv->state + uport->line;
        port = &state->port;
    
        mutex_lock(&port_mutex);
        mutex_lock(&port->mutex);
        if (state->uart_port) {
            ret = -EINVAL;
            goto out;
        }
    
        state->uart_port = uport;
        state->pm_state = UART_PM_STATE_UNDEFINED;
    
        uport->cons = drv->cons;
        uport->state = state;
    
        /*
         * If this port is a console, then the spinlock is already
         * initialised.
         */
        if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
            spin_lock_init(&uport->lock);
            lockdep_set_class(&uport->lock, &port_lock_key);
        }
        if (uport->cons && uport->dev)
            of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
    
        uart_configure_port(drv, state, uport);
    
        num_groups = 2;
        if (uport->attr_group)
            num_groups++;
    
        uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
                        GFP_KERNEL);
        if (!uport->tty_groups) {
            ret = -ENOMEM;
            goto out;
        }
        uport->tty_groups[0] = &tty_dev_attr_group;
        if (uport->attr_group)
            uport->tty_groups[1] = uport->attr_group;
    
        /*
         * Register the port whether it's detected or not.  This allows
         * setserial to be used to alter this port's parameters.
         */
        tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
                uport->line, uport->dev, port, uport->tty_groups);
        if (likely(!IS_ERR(tty_dev))) {
            device_set_wakeup_capable(tty_dev, 1);
        } else {
            dev_err(uport->dev, "Cannot register tty device on line %d
    ",
                   uport->line);
        }
    
        /*
         * Ensure UPF_DEAD is not set.
         */
        uport->flags &= ~UPF_DEAD;
    
     out:
        mutex_unlock(&port->mutex);
        mutex_unlock(&port_mutex);
    
        return ret;
    }

    调用uart_configure_port函数进行端口配置。

    调用tty_port_register_device_attr创建ttyMT0~ttyMT3设备。

  • 相关阅读:
    推荐一个JavaScript触发器插件,可通过指定频次、指定时间内触发指定的处理函数
    TortoiseGit for windows安装与配置
    Postgresql 迁移随笔一
    三边定位 c#
    unset变量释放内存不起作用
    局域网下 连接别人的数据库授权
    iconv 参数详解
    urlencode()和rawurlencode()区别
    php数组函数
    php://input和parse_str()使用
  • 原文地址:https://www.cnblogs.com/jiangjh/p/10147590.html
Copyright © 2011-2022 走看看