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!!\n");
    }
    if (pdev->id >= UART_NR) {
        pr_err("DTS cell ID %d > UART nuber %d\n", 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\n", 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\n", 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\n", 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\n", 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!\n");
#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\n", 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\n", 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!\n");
#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.\n");
            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\n", 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\n",
               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设备。

 

转载于:https://www.cnblogs.com/jiangjh/p/10147590.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值