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设备。