S3C2410 UART的驅動
14.7.1 S3C2410串口硬件描述
S3C2410 內部具有3個獨立的UART控制器,每個控制器都可以工作在Interrupt(中斷)模式或DMA(直接內存訪問)模式,也就是說UART控制器可以在 CPU與UART控制器傳送資料的時候產生中斷或DMA請求。S3C2410集成的每個UART均具有16字節的FIFO,支持的最高波特率可達到 230.4Kbps。
ULCONn(UART Line Control Register)寄存器用於S3C2410 UART的線路控制,用於設置模式、每幀的數據位數、停止位數及奇偶校驗,如表14.1。
表14.1 S3C2410 UART的ULCONn寄存器
ULCONn 位 描述
保留 [7]
紅外模式 [6] 0:正常模式 1:紅外模式
奇偶校驗 [5:3] 0xx:無校驗 100:奇校驗 101:偶校驗 ...
停止位 [2] 0:1個停止位 1:2個停止位
字長 [1:0] 00:5位 01:6位 10:7位 11:8位
UCONn(UART Control Register)寄存器用於從整體上控制S3C2410 UART的中斷模式及工作模式(DMA、中斷、輪詢)等,如表14.2。
表14.2 S3C2410 UART的UCONn寄存器
UCONn 位 描述
時鐘選擇 [10] 爲UART的波特率產生選擇PCLK或UCLK時鐘
Tx中斷 [9] 0:脈衝 1:電平
Rx中斷 [8] 0:脈衝 1:電平
Rx超時使能 [7] 當UART被使能,使能/禁止Rx超時中斷 0:禁止 1:使能
Rx錯誤狀態中斷使能 [6] 使能接收異常中斷(如break、幀錯誤、校驗錯、溢出等)
loopback [5] 0:正常模式 1:迴環
發送break [4] 設置該位將造成UART在1幀的時間內發送break,當發送完break後,該位將自動被清除
發送模式 [3:2] 發送數據到UART的模式,00:禁止 01:中斷或輪詢 10:DMA0(僅針對UART0)、DMA3(僅針對UART3) 11:DMA1(僅針對UART1)
接收模式 [1:0] 從UART接收數據的模式,00:禁止 01:中斷或輪詢 10:DMA0(僅針對UART0)
UFCONn(UART FIFO Conrtol Register)寄存器用於S3C2410 UART的FIFO控制,用於控制FIFO中斷的觸發級別以及復位時是否清空FIFO中的內容,如表14.3。
表14.3 S3C2410 UART的UFCONn寄存器
UFCONn 位 描述
Tx FIFO觸發級別 [7:6] 決定發送FIFO的觸發級別: 00:空 01:4字節 10:8字節 11:12字節
Rx FIFO觸發級別 [5:4] 決定接收FIFO的觸發級別: 00:4字節 01:8字節 10:12字節 11:16字節
Tx FIFO復位 [2] 復位FIFO後自動清除FIFO 0:正常 1:Tx FIFO復位
Rx FIFO復位 [1] 復位FIFO後自動清除FIFO 0:正常 1:Tx FIFO復位
FIFO使能 [0] 0:禁止 1:使能
代碼清單14.19給出了UFCONn寄存器的位掩碼和默認設置(使能FIFO、Tx FIFO爲空時觸發中斷、Rx FIFO中包含8個字節時觸發中斷)。
代碼清單14.19 S3C2410 UART UFCONn寄存器的位掩碼和默認設置
1 #define S3C2410_UFCON_FIFOMODE (1<<0)
2 #define S3C2410_UFCON_TXTRIG0 (0<<6)
3 #define S3C2410_UFCON_RXTRIG8 (1<<4)
4 #define S3C2410_UFCON_RXTRIG12 (2<<4)
5
6 #define S3C2410_UFCON_RESETBOTH (3<<1)
7 #define S3C2410_UFCON_RESETTX (1<<2)
8 #define S3C2410_UFCON_RESETRX (1<<1)
9
10 #define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | /
11 S3C2410_UFCON_TXTRIG0 | /
12 S3C2410_UFCON_RXTRIG8 )
UFSTATn(UART FIFO Status Register)寄存器用於表徵UART FIFO的狀態,如表14.4。
表14.4 S3C2410 UART的UFSTATn寄存器
UFSTATn 位 描述
保留 [15:10]
Tx FIFO滿 [9] 當Tx FIFO滿後,將自動被設置爲1 0:0字節 ≤ Tx FIFO數據數 ≤ 15 1:Tx FIFO數據數 = 15
Rx FIFO滿 [8] 當Rx FIFO滿後,將自動被設置爲1 0:0字節 ≤ Rx FIFO數據數 ≤ 15 1:Tx FIFO數據數 = 15
Tx FIFO數據數 [7:4]
Rx FIFO數據數 [3:0]
由於UFSTATn寄存器中的Tx FIFO數據數和Rx FIFO數據數分別佔據[7:4]和[3:0]位,因此定義S3C2410_UFSTAT_TXSHIFT和 S3C2410_UFSTAT_RXSHIFT分別爲4和0,代碼清單14.20給出了UFSTATn寄存器的位掩碼等信息。
代碼清單14.20 S3C2410 UART UFSTATn寄存器的位掩碼
1 #define S3C2410_UFSTAT_TXFULL (1<<9)
2 #define S3C2410_UFSTAT_RXFULL (1<<8)
3 #define S3C2410_UFSTAT_TXMASK (15<<4)
4 #define S3C2410_UFSTAT_TXSHIFT (4)
5 #define S3C2410_UFSTAT_RXMASK (15<<0)
6 #define S3C2410_UFSTAT_RXSHIFT (0)
UTXHn(UART Transmit Buffer Register)和 URXHn(UART Receive Buffer Register)分別是UART發送和接收數據寄存器,這2個寄存器存放着發送和接收的數據。
UTRSTATn(UART TX/RX Status Register)寄存器反映了發送和接收的狀態,通過這個寄存器,驅動程序可以判斷URXHn中是否有數據接收到或UTXHn是否爲空,這個寄存器主要在非FIFO模式時使用。
UMCONn(UART Modem Control Register)用於S3C2410 UART的modem控制,設置是否使用RTS流控,若採用流控,可選擇自動流控(Auto Flow Control,AFC)或由軟件控制RTS信號的“高”或“低”電平。
14.7.2 S3C2410串口驅動數據結構
S3C2410串口驅動中uart_driver結構體實例的定義如代碼清單14.21,設備名爲“s3c2410_serial”,驅動名爲“ttySAC”。
代碼清單14.21 S3C2410串口驅動uart_driver結構體
1 #define S3C24XX_SERIAL_NAME "ttySAC"
2 #define S3C24XX_SERIAL_DEVFS "tts/"
3 #define S3C24XX_SERIAL_MAJOR 204
4 #define S3C24XX_SERIAL_MINOR 64
5
6 static struct uart_driver s3c24xx_uart_drv =
7 {
8 .owner = THIS_MODULE,
9 .dev_name = "s3c2410_serial",
10 .nr = 3,
11 .cons = S3C24XX_SERIAL_CONSOLE,
12 .driver_name = S3C24XX_SERIAL_NAME,
13 .devfs_name = S3C24XX_SERIAL_DEVFS,
14 .major = S3C24XX_SERIAL_MAJOR,
15 .minor = S3C24XX_SERIAL_MINOR,
16 };
S3C2410 串口驅動中定義了結構體s3c24xx_uart_port,該結構體中封裝了uart_port結構體及一些針對S3C2410 UART的附加信息,代碼清單14.22給出了s3c24xx_uart_port結構體及其實例s3c24xx_serial_ports[]數組。
代碼清單14.22 S3C2410串口驅動s3c24xx_uart_port結構體
1 struct s3c24xx_uart_port
2 {
3 unsigned char rx_claimed;
4 unsigned char tx_claimed;
5
6 struct s3c24xx_uart_info *info;
7 struct s3c24xx_uart_clksrc *clksrc;
8 struct clk *clk;
9 struct clk *baudclk;
10 struct uart_port port;
11 };
12
13 static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
14 [0] = {
15 .port = {
16 .lock = SPIN_LOCK_UNLOCKED,
17 .iotype = UPIO_MEM,
18 .irq = IRQ_S3CUART_RX0,
19 .uartclk = 0,
20 .fifosize = 16,
21 .ops = &s3c24xx_serial_ops,
22 .flags = UPF_BOOT_AUTOCONF,
23 .line = 0,//端口索引:0
24 }
25 },
26 [1] = {
27 .port = {
28 .lock = SPIN_LOCK_UNLOCKED,
29 .iotype = UPIO_MEM,
30 .irq = IRQ_S3CUART_RX1,
31 .uartclk = 0,
32 .fifosize = 16,
33 .ops = &s3c24xx_serial_ops,
34 .flags = UPF_BOOT_AUTOCONF,
35 .line = 1, //端口索引:1
36 }
37 },
38 #if NR_PORTS > 2
39
40 [2] = {
41 .port = {
42 .lock = SPIN_LOCK_UNLOCKED,
43 .iotype = UPIO_MEM,
44 .irq = IRQ_S3CUART_RX2,
45 .uartclk = 0,
46 .fifosize = 16,
47 .ops = &s3c24xx_serial_ops,
48 .flags = UPF_BOOT_AUTOCONF,
49 .line = 2, //端口索引:2
50 }
51 }
52 #endif
53 };
S3C2410串口驅動中uart_ops結構體實例的定義如代碼清單14.23,將一系列s3c24xx_serial_函數賦值給了uart_ops結構體的成員。
代碼清單14.23 S3C2410串口驅動uart_ops結構體
1 static struct uart_ops s3c24xx_serial_ops =
2 {
3 .pm = s3c24xx_serial_pm,
4 .tx_empty = s3c24xx_serial_tx_empty,//發送緩衝區空
5 .get_mctrl = s3c24xx_serial_get_mctrl,//得到modem控制設置
6 .set_mctrl = s3c24xx_serial_set_mctrl, //設置modem控制(MCR)
7 .stop_tx = s3c24xx_serial_stop_tx, //停止接收字符
8 .start_tx = s3c24xx_serial_start_tx,//開始傳輸字符
9 .stop_rx = s3c24xx_serial_stop_rx, //停止接收字符
10 .enable_ms = s3c24xx_serial_enable_ms,// modem狀態中斷使能
11 .break_ctl = s3c24xx_serial_break_ctl,// 控制break信號的傳輸
12 .startup = s3c24xx_serial_startup,//啓動端口
13 .shutdown = s3c24xx_serial_shutdown,// 禁用端口
14 .set_termios = s3c24xx_serial_set_termios,//改變端口參數
15 .type = s3c24xx_serial_type,//返回描述特定端口的常量字符串指針
16 .release_port = s3c24xx_serial_release_port,//釋放端口占用的內存及IO資源
17 .request_port = s3c24xx_serial_request_port,//申請端口所需的內存和IO資源
18 .config_port = s3c24xx_serial_config_port,//執行端口所需的自動配置步驟
19 .verify_port = s3c24xx_serial_verify_port,//驗證新的串行端口信息
20 };
set_mctrl()函數的原型爲:
void (*set_mctrl)(struct uart_port *port, u_int mctrl);
它將參數port所對應的調制解調器控制線的值設爲參數mctrl的值。
get_mctrl()函數的原型爲:
unsigned int (*get_mctrl)(struct uart_port *port);
該函數返回調制解調器控制輸入的現有狀態,這些狀態信息包括:TIOCM_CD(CD 信號狀態)、TIOCM_CTS(CTS信號狀態)、TIOCM_DSR(DSR信號狀態)、TIOCM_RI(RI信號狀態)等。如果信號被置爲有效,則對應位將被置位。
端口啓動函數startup()的原型爲:
int (*startup)(struct uart_port *port, struct uart_info *info);
該函數申請所有中斷資源,初始化底層驅動狀態,並開啓端口爲可接收數據的狀態。
shutdown()函數完成與startup()函數的作用相反,其原型:
void (*shutdown)(struct uart_port *port, struct uart_info *info);
這個函數禁用端口,釋放所有的中斷資源。
回過頭來看s3c24xx_uart_port結構體,其中的s3c24xx_uart_info成員(代碼清單14.22第6行)是一些針對S3C2410 UART的信息,其定義如代碼清單14.24。
代碼清單14.24 S3C2410串口驅動s3c24xx_uart_info結構體
1 static struct s3c24xx_uart_info s3c2410_uart_inf =
2 {
3 .name = "Samsung S3C2410 UART",
4 .type = PORT_S3C2410,
5 .fifosize = 16,
6 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
7 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
8 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
9 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
10 .tx_fifomask = S3C2410_UFSTAT_TXMASK,
11 .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
12 .get_clksrc = s3c2410_serial_getsource,
13 .set_clksrc = s3c2410_serial_setsource,
14 .reset_port = s3c2410_serial_resetport,
15 };
在S3C2410串口驅動中,針對UART的設置(UCONn、ULCONn、UFCONn寄存器等)被封裝到s3c2410_uartcfg結構體中,其定義如代碼清單14.25。
代碼清單14.25 S3C2410串口驅動s3c2410_uartcfg結構體
1 struct s3c2410_uartcfg
2 {
3 unsigned char hwport; /* 硬件端口號 */
4 unsigned char unused;
5 unsigned short flags;
6 unsigned long uart_flags; /* 缺省的uart標誌 */
7
8 unsigned long ucon; /* 端口的ucon值 */
9 unsigned long ulcon; /* 端口的ulcon值 */
10 unsigned long ufcon; /* 端口的ufcon值 */
11
12 struct s3c24xx_uart_clksrc *clocks;
13 unsigned int clocks_size;
14 };
14.7.3 S3C2410串口驅動初始化與釋放
在S3C2410 串口驅動的模塊加載函數中會調用uart_register_driver()註冊s3c24xx_uart_drv這個uart_driver,同時經過s3c2410_serial_init()→s3c24xx_serial_init()→platform_driver_register()的調用導致s3c24xx_serial_probe()被執行,而s3c24xx_serial_probe()函數中會調用 s3c24xx_serial_init_port()初始化UART端口並調用uart_add_one_port()添加端口,整個過程的對應代碼如 清單14.26。
代碼清單14.26 S3C2410串口驅動初始化過程
1 static int __init s3c24xx_serial_modinit(void)
2 {
3 int ret;
4 //註冊uart_driver
5 ret = uart_register_driver(&s3c24xx_uart_drv);
6 if (ret < 0) {
7 printk(KERN_ERR "failed to register UART driver/n");
8 return -1;
9 }
10 //初始化s3c2410的串口
11 s3c2410_serial_init();
12
13 return 0;
14 }
15
16 static inline int s3c2410_serial_init(void)
17 {
18 return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
19 }
20
21 static int s3c24xx_serial_init(struct platform_driver *drv,
22 struct s3c24xx_uart_info *info)
23 {
24 dbg("s3c24xx_serial_init(%p,%p)/n", drv, info);
25 return platform_driver_register(drv);//註冊平臺驅動
26 }
27
28 //平臺驅動probe()函數
29 static int s3c24xx_serial_probe(struct platform_device *dev,
30 struct s3c24xx_uart_info *info)
31 {
32 struct s3c24xx_uart_port *ourport;
33 int ret;
34
35 dbg("s3c24xx_serial_probe(%p, %p) %d/n", dev, info, probe_index);
36
37 ourport = &s3c24xx_serial_ports[probe_index];
38 probe_index++;
39
40 dbg("%s: initialising port %p.../n", __FUNCTION__, ourport);
41 //初始化uart端口
42 ret = s3c24xx_serial_init_port(ourport, info, dev);
43 if (ret < 0)
44 goto probe_err;
45
46 dbg("%s: adding port/n", __FUNCTION__);
47 //添加uart_port
48 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
49 platform_set_drvdata(dev, &ourport->port);
50
51 return 0;
52
53 probe_err:
54 return ret;
55 }
56 //42行調用的s3c24xx_serial_init_port()函數
57 static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
58 struct s3c24xx_uart_info *info,
59 struct platform_device *platdev)
60 {
61 struct uart_port *port = &ourport->port;
62 struct s3c2410_uartcfg *cfg;
63 struct resource *res;
64
65 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p/n", port, platdev);
66
67 if (platdev == NULL)
68 return -ENODEV;
69
70 cfg = s3c24xx_dev_to_cfg(&platdev->dev);
71
72 if (port->mapbase != 0)
73 return 0;
74
75 if (cfg->hwport > 3)
76 return -EINVAL;
77
78 /* 爲端口設置info成員 */
79 port->dev = &platdev->dev;
80 ourport->info = info;
81
82 /* 初始化fifosize */
83 ourport->port.fifosize = info->fifosize;
84
85 dbg("s3c24xx_serial_init_port: %p (hw %d).../n", port, cfg->hwport);
86
87 port->uartclk = 1;
88 /* 如果使用流控 */
89 if (cfg->uart_flags & UPF_CONS_FLOW) {
90 dbg("s3c24xx_serial_init_port: enabling flow control/n");
91 port->flags |= UPF_CONS_FLOW;
92 }
93
94 /* 利用平臺資源中記錄的信息初始化uart端口的基地址、中斷號 */
95 res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
96 if (res == NULL) {
97 printk(KERN_ERR "failed to find memory resource for uart/n");
98 return -EINVAL;
99 }
100
101 dbg("resource %p (%lx..%lx)/n", res, res->start, res->end);
102
103 port->mapbase = res->start;
104 port->membase = S3C24XX_VA_UART+(res->start - S3C24XX_PA_UART);
105 port->irq = platform_get_irq(platdev, 0);
106
107 ourport->clk = clk_get(&platdev->dev, "uart");
108
109 dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld/n",
110 port->mapbase, port->membase, port->irq, port->uartclk);
111
112 /* 復位fifo並設置uart */
113 s3c24xx_serial_resetport(port, cfg);
114 return 0;
115 }
116 //113行調用的s3c24xx_serial_resetport()函數
117 static inline int s3c24xx_serial_resetport(struct uart_port * port,
118 struct s3c2410_uartcfg *cfg)
119 {
120 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
121
122 return (info->reset_port)(port, cfg);
123 }
124 //122行調用的info->reset_port()函數
125 static int s3c2410_serial_resetport(struct uart_port *port,
126 struct s3c2410_uartcfg *cfg)
127 {
128 dbg("s3c2410_serial_resetport: port=%p (%08lx), cfg=%p/n",
129 port, port->mapbase, cfg);
130
131 wr_regl(port, S3C2410_UCON, cfg->ucon);
132 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
133
134 /* 復位2個fifo */
135 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
136 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
137
138 return 0;
139 }
在S3C2410串口驅動模塊被卸載時,它會最終調用uart_remove_one_port()釋放uart_port並調用uart_unregister_driver()註銷uart_driver,如代碼清單14.27所示。
代碼清單14.27 S3C2410串口驅動釋放過程
1 static void __exit s3c24xx_serial_modexit(void)
2 {
3 s3c2410_serial_exit();
4 //註銷uart_driver
5 uart_unregister_driver(&s3c24xx_uart_drv);
6 }
7
8 static inline void s3c2410_serial_exit(void)
9 {
10 //註銷平臺驅動
11 platform_driver_unregister(&s3c2410_serial_drv);
12 }
13
14 static int s3c24xx_serial_remove(struct platform_device *dev)
15 {
16 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
17
18 //移除uart端口
19 if (port)
20 uart_remove_one_port(&s3c24xx_uart_drv, port);
21
22 return 0;
23 }
上述代碼中對S3C24xx_serial_remove()的調用發生在platform_driver_unregister()之後,由於S3C2410的UART是集成於SoC芯片內部的一個獨立的硬件單元,因此也被作爲1個平臺設備而定義。
14.7.4 S3C2410串口數據收發
S3C2410串口驅動uart_ops結構體的startup ()成員函數s3c24xx_serial_startup()用於啓動端口,申請端口的發送、接收中斷,使能端口的發送和接收,其實現如代碼清單14.28。
代碼清單14.28 S3C2410串口驅動startup ()函數
1 static int s3c24xx_serial_startup(struct uart_port *port)
2 {
3 struct s3c24xx_uart_port *ourport = to_ourport(port);
4 int ret;
5
6 dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)/n",
7 port->mapbase, port->membase);
8
9 rx_enabled(port) = 1;//置接收使能狀態爲1
10 //申請接收中斷
11 ret = request_irq(RX_IRQ(port),
12 s3c24xx_serial_rx_chars, 0,
13 s3c24xx_serial_portname(port), ourport);
14
15 if (ret != 0) {
16 printk(KERN_ERR "cannot get irq %d/n", RX_IRQ(port));
17 return ret;
18 }
19
20 ourport->rx_claimed = 1;
21
22 dbg("requesting tx irq.../n");
23
24 tx_enabled(port) = 1;//置發送使能狀態爲1
25 //申請發送中斷
26 ret = request_irq(TX_IRQ(port),
27 s3c24xx_serial_tx_chars, 0,
28 s3c24xx_serial_portname(port), ourport);
29
30 if (ret) {
31 printk(KERN_ERR "cannot get irq %d/n", TX_IRQ(port));
32 goto err;
33 }
34
35 ourport->tx_claimed = 1;
36
37 dbg("s3c24xx_serial_startup ok/n");
38
39 /* 端口復位代碼應該已經爲端口控制設置了正確的寄存器 */
40
41 return ret;
42
43 err:
44 s3c24xx_serial_shutdown(port);
45 return ret;
46 }
s3c24xx_serial_startup()的“反函數”爲s3c24xx_serial_shutdown(),其釋放中斷,禁止發送和接收,實現如代碼清單14.29。
代碼清單14.29 S3C2410串口驅動shutdown ()函數
1 static void s3c24xx_serial_shutdown(struct uart_port *port)
2 {
3 struct s3c24xx_uart_port *ourport = to_ourport(port);
4
5 if (ourport->tx_claimed) {
6 free_irq(TX_IRQ(port), ourport);
7 tx_enabled(port) = 0; //置發送使能狀態爲0
8 ourport->tx_claimed = 0;
9 }
10
11 if (ourport->rx_claimed) {
12 free_irq(RX_IRQ(port), ourport);
13 ourport->rx_claimed = 0;
14 rx_enabled(port) = 0; //置接收使能狀態爲1
15 }
16 }
S3C2410 串口驅動uart_ops結構體的tx_empty()成員函數s3c24xx_serial_tx_empty()用於判斷髮送緩衝區是否爲空,其實現 如代碼清單14.30,當使能FIFO模式的時候,判斷UFSTATn寄存器,否則判斷UTRSTATn寄存器的相應位。
代碼清單14.30 S3C2410串口驅動tx_empty()函數
1 /* 檢查發送緩衝區/FIFO是否爲空 */
2 static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
3 {
4 //fifo模式,檢查UFSTATn寄存器
5 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
6 unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
7 unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
8
9 if (ufcon &S3C2410_UFCON_FIFOMODE)
10 {
11 if ((ufstat &info->tx_fifomask) != 0 || //Tx fifo數據數非0
12 (ufstat &info->tx_fifofull)) // Tx fifo滿
13 return 0; //0:非空
14
15 return 1; //1:空
16 }
17
18 return s3c24xx_serial_txempty_nofifo(port);
19 }
20
21 static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
22 {
23 //非fifo模式,檢查UTRSTATn寄存器
24 return (rd_regl(port, S3C2410_UTRSTAT) &S3C2410_UTRSTAT_TXE);
25 }
S3C2410 串口驅動uart_ops結構體的start_tx ()成員函數s3c24xx_serial_start_tx()用於啓動發送,而stop_rx()成員函數 s3c24xx_serial_stop_rx()用於停止發送,代碼清單14.31給出了這2個函數的實現。
代碼清單14.31 S3C2410串口驅動start_tx ()、stop_rx()函數
1 static void s3c24xx_serial_start_tx(struct uart_port *port)
2 {
3 if (!tx_enabled(port)) {//如果端口發送未使能
4 if (port->flags & UPF_CONS_FLOW)
5 s3c24xx_serial_rx_disable(port);
6
7 enable_irq(TX_IRQ(port));//使能發送中斷
8 tx_enabled(port) = 1;//置端口發送使能狀態爲1
9 }
10 }
11
12 static void s3c24xx_serial_stop_tx(struct uart_port *port)
13 {
14 if (tx_enabled(port)) {//如果端口發送已使能
15 disable_irq(TX_IRQ(port));//禁止發送中斷
16 tx_enabled(port) = 0;//置端口發送使能狀態爲0
17 if (port->flags & UPF_CONS_FLOW)
18 s3c24xx_serial_rx_enable(port);
19 }
20 }
S3C2410 串口驅動uart_ops結構體的stop_rx ()成員函數s3c24xx_serial_stop_rx ()用於停止接收,代碼清單14.32給出了這個函數的實現。注意uart_ops中沒有start_rx()成員,因爲接收並不是由“我方”啓動,而是 由“它方”的發送觸發“我方”的接收中斷,“我方”再被動響應。
代碼清單14.32 S3C2410串口驅動stop_rx ()函數
1 static void s3c24xx_serial_stop_rx(struct uart_port *port)
2 {
3 if (rx_enabled(port)) {//如果接收爲使能
4 dbg("s3c24xx_serial_stop_rx: port=%p/n", port);
5 disable_irq(RX_IRQ(port));//禁止接收中斷
6 rx_enabled(port) = 0;//置接收使能狀態爲0
7 }
8 }
在S3C2410 串口驅動中,與數據收發關係最密切的函數不是上述uart_ops成員函數,而是s3c24xx_serial_startup()爲發送和接收中斷註冊 的中斷處理函數s3c24xx_serial_rx_chars()和s3c24xx_serial_tx_chars()。s3c24xx_serial_rx_chars ()讀取URXHn寄存器以獲得接收到的字符,並調用uart_insert_char()將該字符添加了tty設備的flip緩衝區中,當接收到64個 字符或者不再能接受到字符後,調用tty_flip_buffer_push()函數向上層“推”tty設備的flip緩衝,其實現如代碼清單 14.33。
代碼清單14.33 S3C2410串口驅動接收中斷處理函數
1 static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id, struct
2 pt_regs *regs)
3 {
4 struct s3c24xx_uart_port *ourport = dev_id;
5 struct uart_port *port = &ourport->port; //獲得uart_port
6 struct tty_struct *tty = port->info->tty; //獲得tty_struct
7 unsigned int ufcon, ch, flag, ufstat, uerstat;
8 int max_count = 64;
9
10 while (max_count-- > 0)
11 {
12 ufcon = rd_regl(port, S3C2410_UFCON);
13 ufstat = rd_regl(port, S3C2410_UFSTAT);
14 //如果接收到0個字符
15 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
16 break;
17
18 uerstat = rd_regl(port, S3C2410_UERSTAT);
19 ch = rd_regb(port, S3C2410_URXH); //讀出字符
20
21 if (port->flags &UPF_CONS_FLOW)
22 {
23 int txe = s3c24xx_serial_txempty_nofifo(port);
24
25 if (rx_enabled(port)) //如果端口爲使能接收狀態
26 {
27 if (!txe) //如果發送緩衝區爲空
28 {
29 rx_enabled(port) = 0; //置端口爲使能接收狀態爲0
30 continue;
31 }
32 }
33 else //端口爲禁止接收狀態
34 {
35 if (txe) //如果發送緩衝區非空
36 {
37 ufcon |= S3C2410_UFCON_RESETRX;
38 wr_regl(port, S3C2410_UFCON, ufcon);
39 rx_enabled(port) = 1;//置端口爲使能接收狀態爲1
40 goto out;
41 }
42 continue;
43 }
44 }
45
46 /* 將接收到的字符寫入buffer */
47 flag = TTY_NORMAL;
48 port->icount.rx++;
49
50 if (unlikely(uerstat &S3C2410_UERSTAT_ANY))
51 {
52 dbg("rxerr: port ch=0x%02x, rxs=0x%08x/n", ch, uerstat);
53
54 if (uerstat &S3C2410_UERSTAT_BREAK)
55 {
56 dbg("break!/n");
57 port->icount.brk++;
58 if (uart_handle_break(port))
59 goto ignore_char;
60 }
61
62 if (uerstat &S3C2410_UERSTAT_FRAME)
63 port->icount.frame++;
64 if (uerstat &S3C2410_UERSTAT_OVERRUN)
65 port->icount.overrun++;
66
67 uerstat &= port->read_status_mask;
68
69 if (uerstat &S3C2410_UERSTAT_BREAK)
70 flag = TTY_BREAK;
71 else if (uerstat &S3C2410_UERSTAT_PARITY)
72 flag = TTY_PARITY;
73 else if (uerstat &(S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
74 flag = TTY_FRAME;
75 }
76
77 if (uart_handle_sysrq_char(port, ch, regs)) //處理sysrq字符
78 goto ignore_char;
79 //插入字符到tty設備的flip 緩衝
80 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
81
82 ignore_char: continue;
83 }
84 tty_flip_buffer_push(tty); //刷新tty設備的flip 緩衝
85
86 out: return IRQ_HANDLED;
87 }
上述代碼第80行的uart_insert_char()函數是串口核心層對tty_insert_flip_char()的封裝,它作爲內聯函數被定義於serial_core.h文件中。
如代碼清單14.34,s3c24xx_serial_tx_chars()讀取uart_info中環形緩衝區中的字符,寫入調用UTXHn寄存器。
代碼清單14.34 S3C2410串口驅動發送中斷處理函數
1 static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id, struct pt_regs
2 *regs)
3 {
4 struct s3c24xx_uart_port *ourport = id;
5 struct uart_port *port = &ourport->port;
6 struct circ_buf *xmit = &port->info->xmit; //得到環形緩衝區
7 int count = 256; //最多1次發256個字符
8
9 if (port->x_char) //如果定義了xchar,發送
10 {
11 wr_regb(port, S3C2410_UTXH, port->x_char);
12 port->icount.tx++;
13 port->x_char = 0;
14 goto out;
15 }
16
17 /* 如果沒有更多的字符需要發送,或者uart Tx停止,則停止uart並退出 */
18 if (uart_circ_empty(xmit) || uart_tx_stopped(port))
19 {
20 s3c24xx_serial_stop_tx(port);
21 goto out;
22 }
23
24 /* 嘗試把環行buffer中的數據發空 */
25 while (!uart_circ_empty(xmit) && count-- > 0)
26 {
27 if (rd_regl(port, S3C2410_UFSTAT) &ourport->info->tx_fifofull)
28 break;
29
30 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
31 xmit->tail = (xmit->tail + 1) &(UART_XMIT_SIZE - 1);
32 port->icount.tx++;
33 }
34 /* 如果環形緩衝區中剩餘的字符少於WAKEUP_CHARS,喚醒上層 */
35 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
36 uart_write_wakeup(port);
37
38 if (uart_circ_empty(xmit)) //如果發送環形buffer爲空
39 s3c24xx_serial_stop_tx(port); //停止發送
40
41 out: return IRQ_HANDLED;
42 }
上述代碼第35行的宏WAKEUP_CHARS的含義爲:當發送環形緩衝區中的字符數小於該數時,驅動將請求上層向下傳遞更多的數據,uart_write_wakeup()完成此目的。
uart_circ_chars_pending()、uart_circ_empty()是定義於serial_core.h中的宏,分別返回環形緩衝區剩餘的字符數以及判斷緩衝區是否爲空。
14.7.5 S3C2410串口線路設置
S3C2410 串口驅動uart_ops結構體的set_termios()成員函數用於改變端口的參數設置,包括波特率、字長、停止位、奇偶校驗等,它會根據傳遞給它 的port、termios參數成員的值設置S3C2410 UART的ULCONn、UCONn、UMCONn等寄存器,其實現如代碼清單14.35。
代碼清單14.35 S3C2410串口驅動set_termios()函數
1 static void s3c24xx_serial_set_termios(struct uart_port *port,
2 struct termios *termios,
3 struct termios *old)
4 {
5 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
6 struct s3c24xx_uart_port *ourport = to_ourport(port);
7 struct s3c24xx_uart_clksrc *clksrc = NULL;
8 struct clk *clk = NULL;
9 unsigned long flags;
10 unsigned int baud, quot;
11 unsigned int ulcon;
12 unsigned int umcon;
13
14 /* 不支持modem控制信號線 */
15 termios->c_cflag &= ~(HUPCL | CMSPAR);
16 termios->c_cflag |= CLOCAL;
17
18 /* 請求內核計算分頻以便產生對應的波特率 */
19 baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
20
21 if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
22 quot = port->custom_divisor;
23 else
24 quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
25
26 /* 檢查以確定是否需要改變時鐘源 */
27 if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
28 s3c24xx_serial_setsource(port, clksrc);
29
30 if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
31 clk_disable(ourport->baudclk);
32 ourport->baudclk = NULL;
33 }
34
35 clk_enable(clk);
36
37 ourport->clksrc = clksrc;
38 ourport->baudclk = clk;
39 }
40
41 /* 設置字長 */
42 switch (termios->c_cflag & CSIZE) {
43 case CS5:
44 dbg("config: 5bits/char/n");
45 ulcon = S3C2410_LCON_CS5;
46 break;
47 case CS6:
48 dbg("config: 6bits/char/n");
49 ulcon = S3C2410_LCON_CS6;
50 break;
51 case CS7:
52 dbg("config: 7bits/char/n");
53 ulcon = S3C2410_LCON_CS7;
54 break;
55 case CS8:
56 default:
57 dbg("config: 8bits/char/n");
58 ulcon = S3C2410_LCON_CS8;
59 break;
60 }
61
62 /* 保留以前的lcon IR設置 */
63 ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
64
65 if (termios->c_cflag & CSTOPB)
66 ulcon |= S3C2410_LCON_STOPB;
67 /* 設置是否採用RTS、CTS自動流空 */
68 umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
69
70 if (termios->c_cflag & PARENB) {
71 if (termios->c_cflag & PARODD)
72 ulcon |= S3C2410_LCON_PODD;//計校驗
73 else
74 ulcon |= S3C2410_LCON_PEVEN;//偶校驗
75 } else {
76 ulcon |= S3C2410_LCON_PNONE;//無校驗
77 }
78
79 spin_lock_irqsave(&port->lock, flags);
80
81 dbg("setting ulcon to %08x, brddiv to %d/n", ulcon, quot);
82
83 wr_regl(port, S3C2410_ULCON, ulcon);
84 wr_regl(port, S3C2410_UBRDIV, quot);
85 wr_regl(port, S3C2410_UMCON, umcon);
86
87 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x/n",
88 rd_regl(port, S3C2410_ULCON),
89 rd_regl(port, S3C2410_UCON),
90 rd_regl(port, S3C2410_UFCON));
91
92 /* 更新端口的超時 */
93 uart_update_timeout(port, termios->c_cflag, baud);
94
95 /* 我們對什麼字符狀態狀態標誌感興趣?*/
96 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
97 if (termios->c_iflag & INPCK)
98 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
99
100 /* 我們要忽略什麼字符狀態標誌?*/
101 port->ignore_status_mask = 0;
102 if (termios->c_iflag & IGNPAR)
103 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
104 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
105 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
106
107 /* 如果CREAD未設置,忽略所用字符 */
108 if ((termios->c_cflag & CREAD) == 0)
109 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
110
111 spin_unlock_irqrestore(&port->lock, flags);
112 }
由於S3C2410集成UART並不包含完整的Modem控制信號線,因此其uart_ops結構體的get_mctrl()、set_mctrl()成員 函數的實現非常簡單,如代碼清單14.36,get_mctrl()返回DSR一直有效,而CTS則根據UMSTATn寄存器的內容獲得, set_mctrl()目前爲空。
代碼清單14.36 S3C2410串口驅動get_mctrl()和set_mctrl()函數
1 static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
2 {
3 unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
4
5 if (umstat & S3C2410_UMSTAT_CTS)//CTS信號有效(低電平)
6 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
7 else
8 return TIOCM_CAR | TIOCM_DSR;
9 }
10
11 static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
12 {
13 /* todo:可能移除AFC,並手工進行CTS */
14 }
14.8總結
TTY設備驅動的主體工作圍繞tty_driver這個結構體的成員函數展開,主要應實現其中的數據發送和接收流程以及tty設備線路設置接口函數。
針對串口,內核實現了串口核心層,這個層實現了串口設備通用的tty_driver。因此,串口設備驅動的主體工作從tty_driver轉移到了uart_driver。