Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2836651
  • 博文数量: 523
  • 博客积分: 11908
  • 博客等级: 上将
  • 技术积分: 5475
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-03 15:50
文章分类

全部博文(523)

文章存档

2019年(3)

2013年(4)

2012年(71)

2011年(78)

2010年(57)

2009年(310)

分类: LINUX

2009-06-29 22:44:44

14.7实例:S3C2410 UART的驱动
14.7.1 S3C2410串口硬件描述
    S3C2410 内部具有3个独立的UART控制器,每个控制器都可以工作在Interrupt(中断)模式或DMA(直接内存访问)模式,也就是说UART控制器可以在 CPUUART控制器传送资料的时候产生中断或DMA请求。S3C2410集成的每个UART均具有16字节的FIFO,支持的最高波特率可达到 230.4Kbps
ULCONnUART 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
UCONnUART 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
     UFCONnUART 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 )
UFSTATnUART 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)
UTXHnUART Transmit Buffer Register)和 URXHnUART Receive Buffer Register)分别是UART发送和接收数据寄存器,这2个寄存器存放着发送和接收的数据。
UTRSTATnUART TX/RX Status Register)寄存器反映了发送和接收的状态,通过这个寄存器,驱动程序可以判断URXHn中是否有数据接收到或UTXHn是否为空,这个寄存器主要在非FIFO模式时使用。
UMCONnUART 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 }
   

 

阅读(1908) | 评论(2) | 转发(2) |
给主人留下些什么吧!~~

chinaunix网友2009-07-23 11:33:58

ding

chinaunix网友2009-07-23 11:33:52

ding