S3C2410 UART驅動

S3C2410 UART的驅動
14.7.1 S3C2410串口硬件描述
    S3C2410
內部具有3個獨立的UART控制器,每個控制器都可以工作在Interrupt(中斷)模式或DMA(直接內存訪問)模式,也就是說UART控制器可以在 CPUUART控制器傳送資料的時候產生中斷或DMA請求。S3C2410集成的每個UART均具有16字節的FIFO,支持的最高波特率可達到 230.4Kbps
ULCONn
UART Line Control Register)寄存器用於S3C2410 UART的線路控制,用於設置模式、每幀的數據位數、停止位數及奇偶校驗,如表14.1
14.1 S3C2410 UARTULCONn寄存器
ULCONn 
              描述
保留 [7] 
紅外模式 [6]           0:正常模式  1:紅外模式
奇偶校驗 [5:3]         0xx:無校驗  100:奇校驗  101:偶校驗  ...
停止位 [2]             01個停止位  12個停止位
字長 [1:0]             005 016 107 118
UCONn
UART Control Register)寄存器用於從整體上控制S3C2410 UART的中斷模式及工作模式(DMA、中斷、輪詢)等,如表14.2
14.2 S3C2410 UARTUCONn寄存器
UCONn 
               描述
時鐘選擇 [10]            UART的波特率產生選擇PCLKUCLK時鐘
Tx
中斷 [9]               0:脈衝 1:電平
Rx
中斷 [8]               0:脈衝 1:電平
Rx
超時使能 [7]          UART被使能,使能/禁止Rx超時中斷  0:禁止  1:使能
Rx
錯誤狀態中斷使能 [6]   使能接收異常中斷(如break、幀錯誤、校驗錯、溢出等)
loopback [5]              0
:正常模式 1:迴環
發送break [4]            設置該位將造成UART1幀的時間內發送break,當發送完break後,該位將自動被清除
發送模式 [3:2]            發送數據到UART的模式,00:禁止 01:中斷或輪詢 10DMA0(僅針對UART0)、DMA3(僅針對UART3 11DMA1(僅針對UART1
接收模式 [1:0]            UART接收數據的模式,00:禁止 01:中斷或輪詢 10DMA0(僅針對UART0
     UFCONn
UART FIFO Conrtol Register)寄存器用於S3C2410 UARTFIFO控制,用於控制FIFO中斷的觸發級別以及復位時是否清空FIFO中的內容,如表14.3
 
14.3 S3C2410 UARTUFCONn寄存器
UFCONn 
              描述
Tx FIFO
觸發級別 [7:6]    決定發送FIFO的觸發級別: 00:空 014字節 108字節 1112字節
Rx FIFO
觸發級別 [5:4]    決定接收FIFO的觸發級別: 004字節 018字節 1012字節 1116字節
Tx FIFO
復位 [2]          復位FIFO後自動清除FIFO  0:正常 1Tx FIFO復位
Rx FIFO
復位 [1]          復位FIFO後自動清除FIFO  0:正常 1Tx FIFO復位
FIFO
使能 [0]             0:禁止 1:使能
代碼清單14.19給出了UFCONn寄存器的位掩碼和默認設置(使能FIFOTx 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)

6  #define S3C2410_UFCON_RESETBOTH   (3<<1)
7  #define S3C2410_UFCON_RESETTX   (1<<2)
8  #define S3C2410_UFCON_RESETRX   (1<<1)

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 UARTUFSTATn寄存器
UFSTATn 
              描述
保留 [15:10] 
Tx FIFO
滿 [9]            Tx FIFO滿後,將自動被設置爲1  00字節 ≤ Tx FIFO數據數 ≤ 15  1Tx FIFO數據數 = 15
Rx FIFO
滿 [8]            Rx FIFO滿後,將自動被設置爲1  00字節 ≤ Rx FIFO數據數 ≤ 15  1Tx 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分別爲40,代碼清單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)和 URXHnUART Receive Buffer Register)分別是UART發送和接收數據寄存器,這2個寄存器存放着發送和接收的數據。
UTRSTATn
UART TX/RX Status Register)寄存器反映了發送和接收的狀態,通過這個寄存器,驅動程序可以判斷URXHn中是否有數據接收到或UTXHn是否爲空,這個寄存器主要在非FIFO模式時使用。
UMCONn
UART Modem Control Register)用於S3C2410 UARTmodem控制,設置是否使用RTS流控,若採用流控,可選擇自動流控(Auto Flow ControlAFC)或由軟件控制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

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;

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_CDCD 信號狀態)、TIOCM_CTSCTS信號狀態)、TIOCM_DSRDSR信號狀態)、TIOCM_RIRI信號狀態)等。如果信號被置爲有效,則對應位將被置位。
端口啓動函數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.226行)是一些針對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的設置(UCONnULCONnUFCONn寄存器等)被封裝到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標誌 */

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  /*
復位2fifo */
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  }

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()之後,由於S3C2410UART是集成於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;

6   dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)/n",
7       port->mapbase, port->membase);

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);

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);

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);

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;

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.34s3c24xx_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個字符

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()成員函數用於改變端口的參數設置,包括波特率、字長、停止位、奇偶校驗等,它會根據傳遞給它 porttermios參數成員的值設置S3C2410 UARTULCONnUCONnUMCONn等寄存器,其實現如代碼清單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    /*
設置是否採用RTSCTS自動流空 */
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.36get_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);

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章