天天看點

UART的預設波特率

又是一年沒更新了,這一年多真夠忙碌的。剛開始這個項目人數不夠,各種新 feature 移植,各個子產品問題都得解決,各種救火,自己都沒有好好沉下來深入的看代碼,而結果卻是吃力并不讨好。現在人數招聘夠了,自己終于可以專心下來安安靜靜的做一個小程式員,寫代碼,解bug, 了解新技術,及時充電,及時記錄。

開始記錄,平台 :mdm9x07

剛開始以為 UART 的波特率會在dts裡面配置,結果沒找到,雖然在終端可以通過 stty去配置,但stty -a 裡面預設的波特率從哪來的呢?

于是去看了看源碼。

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

這個函數裡面 normal->init_termios.c_cflag 這裡定義了波特率 9600,即是預設波特率。那麼它是怎麼傳遞的并最終配置UART 寄存器生效的呢?

繼續 看 tty_register_driver

int tty_register_driver(struct tty_driver *driver)
	register_chrdev_region(dev, driver->num, driver->name);	--- 注冊字元裝置
	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 的調用棧:

uart_open
tty_open
chrdev_open 
do_dentry_open
do_last.isra.32
path_openat 
do_filp_open
do_sys_open 
           

那麼從 tty_open 開始看起:

static int tty_open(struct inode *inode, struct file *filp)
	tty_lookup_driver(device, filp, &noctty, &index);	--> get_tty_driver --> list_for_each_entry(p, &tty_drivers, tty_drivers) 
	tty_init_dev(driver, index);				--- 與 B1 相關
	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 這裡來了:

tty_init_termios	<-- tty_standard_install  <-- tty_driver_install_tty <-- tty_init_dev
tty_reset_termios	<-- tty_ldisc_hangup      <-- __tty_hangup         <-- do_tty_hangup  <-- alloc_tty_struct  <-- tty_init_dev
	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。

static const struct tty_operations uart_ops = {
	.open		= uart_open,
...
};
           

現在開始看 uart_open。

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

來到 uart_port_startup:

static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,int init_hw)
	uport->ops->startup(uport);			--- 已知 uport->ops = &msm_hs_ops;即 msm_hs_startup(uport)
	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 中

static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,struct ktermios *old_termios)	
	termios = &tty->termios;				--- 與 B0, B1 相關,得到 termios 資訊
	uport->ops->set_termios(uport, termios, old_termios);	--- 即 msm_hs_set_termios
           

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

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

在這個函數中,uart_get_baud_rate 根據 termios->c_cflag 的資訊從baud_table取出對應波特率 9600.

uart_get_baud_rate
	baud = tty_termios_baud_rate(termios);

speed_t tty_termios_baud_rate(struct ktermios *termios)
	cbaud = termios->c_cflag & CBAUD;		--- CBAUD 為 0x0000100f, 即保留 c_cflag 的0~3,12 幾個bit位
	if (cbaud < 1 || cbaud + 15 > n_baud_table)
		termios->c_cflag &= ~CBAUDEX;		--- CBAUDEX 為 0x00001000, 即把 bit12置零,最終保留 c_cflag 的0~3bit位
	else
		cbaud += 15;	
	return baud_table[cbaud];
	
static const speed_t baud_table[] = {
	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 230400, 460800, ...}
           

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

static void msm_hs_set_bps_locked(struct uart_port *uport,unsigned int bps)
	case 9600:
	msm_hs_write(uport, UART_DM_CSR, 0x55);

     if (bps > 460800) {
        uport->uartclk = bps * 16;
        if (bps == 4000000)
            uport->uartclk = BLSP_UART_CLK_FMAX;
     } else {
        uport->uartclk = 7372800;
     }
           

uport->uartclk 預設值 是7372800,根據datasheet上UART_DM_CSR寄存器的分頻規則如下:

0xF = 16
0xE = 32
0xD = 48
0xC = 64
0xB = 96
0xA = 128
0x9 = 192
0x8 = 256
0x7 = 384
0x6 = 512
0x5 = 768
0x4 = 1536
0x3 = 3072
0x2 = 6144
0x1 = 12288
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

繼續閱讀