uart默认波特率修改

  • Post author:
  • Post category:其他


又是一年没更新了,这一年多真够忙碌的。刚开始这个项目人数不够,各种新 feature 移植,各个模块问题都得解决,各种救火,自己都没有好好沉下来深入的看代码,而结果却是吃力并不讨好。现在人数招聘够了,自己终于可以专心下来安安静静的做一个小程序员,写代码,解bug, 了解新技术,及时充电,及时记录。

开始记录,平台 :mdm9x07

刚开始以为 UART 的波特率会在dts里面配置,结果没找到,虽然在终端可以通过 stty去配置,但stty -a 里面默认的波特率从哪来的呢?

于是去看了看源码。


   
   
  1. int uart_register_driver(struct uart_driver *drv)
  2. normal = alloc_tty_driver(drv->nr);
  3. drv->tty_driver = normal; --- 分配一个 tty_driver ,并将drv->tty_driver 指向它
  4. normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; ---B0 设置 初始波特率 等,B9600 即0x0000000d
  5. tty_set_operations(normal, &uart_ops); --- driver->ops = op; 将 tty 的 ops 定位到 uart 的 ops
  6. tty_port_init(port);
  7. port->ops = &uart_port_ops;
  8. tty_register_driver(normal);

这个函数里面 normal->init_termios.c_cflag 这里定义了波特率 9600,即是默认波特率。那么它是怎么传递的并最终配置UART 寄存器生效的呢?

继续 看 tty_register_driver


   
   
  1. int tty_register_driver(struct tty_driver *driver)
  2. register_chrdev_region(dev, driver->num, driver->name); --- 注册字符设备
  3. list_add(&driver->tty_drivers, &tty_drivers); --- 将 driver->tty_drivers 加入到全局变量 tty_drivers

然后呢?会发现在 uart_change_speed 函数中调用了 uport->ops->set_termios去设置,但是还想了解在 uart_change_speed之前的过程是如何传递的。

用 dump_stack() 打出 uart_open 的调用栈:


   
   
  1. uart_open
  2. tty_open
  3. chrdev_open
  4. do_dentry_open
  5. do_last.isra.32
  6. path_openat
  7. do_filp_open
  8. do_sys_open

那么从 tty_open 开始看起:


   
   
  1. static int tty_open(struct inode *inode, struct file *filp)
  2. tty_lookup_driver(device, filp, &noctty, &index); --> get_tty_driver --> list_for_each_entry(p, &tty_drivers, tty_drivers)
  3. tty_init_dev(driver, index); --- 与 B1 相关
  4. tty->ops->open(tty, filp); --- 即 uart_open

tty_register_driver 把 driver->tty_drivers 放入链表 tty_drivers,这里又把它取出来了。

tty_init_dev 这个函数就把 uart_register_driver函数中定义的init_termios传递到 tty->termios 这里来了:


   
   
  1. tty_init_termios <-- tty_standard_install <-- tty_driver_install_tty <-- tty_init_dev
  2. tty_reset_termios < -- tty_ldisc_hangup < -- __tty_hangup < -- do_tty_hangup < -- alloc_tty_struct < -- tty_init_dev
  3. tty->termios = tty->driver->init_termios;

为什么tty->ops->open 对应的是 uart_open?

上面的uart_register_driver里面有tty_set_operations,即 driver->ops = op; 将 tty 的 ops 定位到 uart 的 ops,这样 tty->ops->open 就对应的是uart_open。


   
   
  1. static const struct tty_operations uart_ops = {
  2. .open = uart_open,
  3. ...
  4. };

现在开始看 uart_open。


   
   
  1. static int uart_open(struct tty_struct *tty, struct file *filp)
  2. uart_startup(tty, state, 0);
  3. uart_port_startup(tty, state, init_hw);

来到 uart_port_startup:


   
   
  1. static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,int init_hw)
  2. uport->ops->startup(uport); --- 已知 uport->ops = &msm_hs_ops;即 msm_hs_startup(uport)
  3. uart_change_speed(tty, state, NULL);

其中 uport->ops->startup 指向 msm_hs_startup,负责一些初始化工作,比如使能 uart clk,irq, uart pin,写各种寄存器。

为什么 uport->ops->startup 指向 msm_hs_startup? 因为在 msm_hs_probe 有 uport->ops = &msm_hs_ops;uart_add_one_port(&msm_hs_driver, uport);其中 msm_hs_ops 结构体中有.startup = msm_hs_startup。

继续看uart_change_speed,根据termios = &tty->termios;即可联系前面的B0,B1语句,知道波特率信息已经传入 termios 中


   
   
  1. static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,struct ktermios *old_termios)
  2. termios = &tty->termios; --- 与 B0, B1 相关,得到 termios 信息
  3. uport->ops->set_termios(uport, termios, old_termios); --- 即 msm_hs_set_termios

接着 uport->ops->set_termios 指向 msm_hs_set_termios:


   
   
  1. static void msm_hs_set_termios(struct uart_port *uport,struct ktermios *termios,struct ktermios *oldtermios)
  2. bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000); --- C1.1
  3. msm_hs_set_bps_locked(uport, bps); --- C1.2

在这个函数中,uart_get_baud_rate 根据 termios->c_cflag 的信息从baud_table取出对应波特率 9600.


   
   
  1. uart_get_baud_rate
  2. baud = tty_termios_baud_rate(termios);
  3. speed_t tty_termios_baud_rate(struct ktermios *termios)
  4. cbaud = termios->c_cflag & CBAUD; --- CBAUD 为 0x0000100f, 即保留 c_cflag 的0~3,12 几个bit位
  5. if (cbaud < 1 || cbaud + 15 > n_baud_table)
  6. termios->c_cflag &= ~CBAUDEX; --- CBAUDEX 为 0x00001000, 即把 bit12置零,最终保留 c_cflag 的0~3bit位
  7. else
  8. cbaud += 15;
  9. return baud_table[cbaud];
  10. static const speed_t baud_table[] = {
  11. 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
  12. 9600, 19200, 38400, 57600, 115200, 230400, 460800, ...}

baud_table[13] = 96000,而 msm_hs_set_termios 中msm_hs_set_bps_locked 则是根据波特率配置 UART 的寄存器:


   
   
  1. static void msm_hs_set_bps_locked(struct uart_port *uport,unsigned int bps)
  2. case 9600:
  3. msm_hs_write(uport, UART_DM_CSR, 0x55);
  4. if (bps > 460800) {
  5. uport->uartclk = bps * 16;
  6. if (bps == 4000000)
  7. uport->uartclk = BLSP_UART_CLK_FMAX;
  8. } else {
  9. uport->uartclk = 7372800;
  10. }

uport->uartclk 默认值 是7372800,根据datasheet上UART_DM_CSR寄存器的分频规则如下:


   
   
  1. 0xF = 16
  2. 0xE = 32
  3. 0xD = 48
  4. 0xC = 64
  5. 0xB = 96
  6. 0xA = 128
  7. 0x9 = 192
  8. 0x8 = 256
  9. 0x7 = 384
  10. 0x6 = 512
  11. 0x5 = 768
  12. 0x4 = 1536
  13. 0x3 = 3072
  14. 0x2 = 6144
  15. 0x1 = 12288
  16. 0x0 = 24576

那么msm_hs_write(uport, UART_DM_CSR, 0x55);,即按照 768 分频,7372800/768=9600.

当然如果想把UART 默认波特率改成115200呢?

很简单,在uart_register_driver中把 normal->init_termios.c_cflag改成:

normal->init_termios.c_cflag = B1152000 | CS8 | CREAD | HUPCL | CLOCAL;

而 B115200为0x00001002,即.c_cflag 0~3bit位十进制值为2,那么在tty_termios_baud_rate中会cbaud += 15后  cbaud为17,结果baud_table[17] = 115200;在msm_hs_set_bps_locked中有case 115200: msm_hs_write(uport, UART_DM_CSR, 0xcc); 而uport->uartclk = 7372800;0xc的分频系数为 64,那么7372800/64=115200.

推荐两个博客,里面有关于uart的分析,还有清晰的图片:

http://blog.csdn.net/lizuobin2/article/details/51773305

http://www.cnblogs.com/aaronLinux/p/5616153.html