您的位置:首页 > 其它

S3C2410 UART 的驱动实例

2013-04-11 15:36 543 查看
S3C2410 串口硬件描述

S3C2410 内部具有 3 个独立的 UART 控制器,每个控制器都可以工作在 Interrupt(中断)模式或 DMA(直接内存访问)模式,也就是说
UART 控制器可以在 CPU 与UART 控制器传送资料的时候产生中断或 DMA 请求。S3C2410 集成的每个 UART 均具有 16 字节的 FIFO,支持的最高波特率可达到 230.4Kbit/s。

ULCONn(UART Line Control Register)寄存器用于 S3C2410 UART 的线路控制,用于设置模式、每帧的数据位数、停止位数及奇偶校验,如下表所示。



UCONn(UART Control Register)寄存器用于从整体上控制 S3C2410 UART 的中断模式及工作模式(DMA、中断、轮询)等,如下表所示。



UFCONn (UART FIFO Conrtol Register)寄存器用于 S3C2410 UART 的 FIFO 控制,用于控制 FIFO 中断的触发级别以及复位时是否清空 FIFO 中的内容,如下表所示



下面代码所示为 UFCONn 寄存器的位掩码和默认设置(使能 FIFO、 FIFO Tx为空时触发中断,Rx FIFO 中包含 8 个字节时触发中断)。

#define S3C2410_UFCON_FIFOMODE (1<<0)
#define S3C2410_UFCON_TXTRIG0 (0<<6)
#define S3C2410_UFCON_RXTRIG8 (1<<4)
#define S3C2410_UFCON_RXTRIG12 (2<<4)
#define S3C2410_UFCON_RESETBOTH (3<<1)
#define S3C2410_UFCON_RESETTX (1<<2)
#define S3C2410_UFCON_RESETRX (1<<1)
#define S3C2410_UFCON_DEFAULT  (S3C2410_UFCON_FIFOMODE | \
S3C2410_UFCON_TXTRIG0 | \
S3C2410_UFCON_RXTRIG8 )
UFSTATn(UART FIFO Status Register)寄存器用于表示 UART FIFO 的状态,如下表所示。



由于 UFSTATn 寄存器中的 Tx FIFO 中数据的数量和 Rx FIFO 中数据的数量分别占 据 [7:4] 和 [3:0] 位 , 因 此 定 义 S3C2410_UFSTAT_TXSHIFT 和S3C2410_UFSTAT_RXSHIFT 分别为 4 和 0,下面代码所示为 UFSTATn 寄存器的位掩码等信息。

#define S3C2410_UFSTAT_TXFULL (1<<9)
#define S3C2410_UFSTAT_RXFULL (1<<8)
#define S3C2410_UFSTAT_TXMASK (15<<4)
#define S3C2410_UFSTAT_TXSHIFT (4)
#define S3C2410_UFSTAT_RXMASK  (15<<0)
#define S3C2410_UFSTAT_RXSHIFT (0)
UTXHn(UART Transmit Buffer Register)和 URXHn(UART Receive Buffer Register)分别是 UART 发送和接收数据寄存器,这两个寄存器存放着发送和接收的数据。

UTRSTATn(UART TX/RX Status Register)寄存器反映了发送和接收的状态,通过这个寄存器,驱动程序可以判断 URXHn 中是否有数据接收到或 UTXHn 是否为空,这个寄存器主要在非 FIFO 模式时使用。

UMCONn(UART Modem Control Register)用于 S3C2410 UART 的 modem 控制,设置是否使用 RTS 流控,若采用流控,可选择自动流控(Auto Flow Control,AFC)或由软件控制 RTS 信号的“高”或“低”电平。

S3C2410 串口驱动的数据结构

S3C2410 串口驱动中 uart_driver 结构体实例的定义如下面代码所示,设备名为“s3c2410_serial”,驱动名为“ttySAC”。

#define S3C24XX_SERIAL_NAME "ttySAC"
#define S3C24XX_SERIAL_DEVFS "tts/"
#define S3C24XX_SERIAL_MAJOR 204
#define S3C24XX_SERIAL_MINOR 64

static struct uart_driver s3c24xx_uart_drv =
{
.owner = THIS_MODULE,
.dev_name = "s3c2410_serial",
.nr = 3,
.cons  = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME,
.devfs_name = S3C24XX_SERIAL_DEVFS,
.major = S3C24XX_SERIAL_MAJOR,
.minor = S3C24XX_SERIAL_MINOR,
};
S3C2410 串口驱动中定义了结构体 s3c24xx_uart_port,该结构体中封装了 uart_port结 构 体 及 一 些 针 对 S3C2410 UART 的 附 加 信 息 , 下面代码所 示 为s3c24xx_uart_port 结构体及其实例 s3c24xx_serial_ports[]数组。

struct s3c24xx_uart_port
{
unsigned char rx_claimed;
unsigned char tx_claimed;

struct s3c24xx_uart_info *info;
struct s3c24xx_uart_clksrc *clksrc;
struct clk *clk;
struct clk *baudclk;
struct uart_port port;
};
static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
[0] = {
.port = {
.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype		= UPIO_MEM,
.irq		= IRQ_S3CUART_RX0,
.uartclk	= 0,
.fifosize	= 16,
.ops		= &s3c24xx_serial_ops,
.flags		= UPF_BOOT_AUTOCONF,
.line		= 0,
}
},
[1] = {
.port = {
.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
.iotype		= UPIO_MEM,
.irq		= IRQ_S3CUART_RX1,
.uartclk	= 0,
.fifosize	= 16,
.ops		= &s3c24xx_serial_ops,
.flags		= UPF_BOOT_AUTOCONF,
.line		= 1,
}
},
#if NR_PORTS > 2

[2] = {
.port = {
.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
.iotype		= UPIO_MEM,
.irq		= IRQ_S3CUART_RX2,
.uartclk	= 0,
.fifosize	= 16,
.ops		= &s3c24xx_serial_ops,
.flags		= UPF_BOOT_AUTOCONF,
.line		= 2,
}
}
#endif
};
S3C2410 串口驱动中 uart_ops 结构体实例的定义如代码如下所示,将一系列 s3c24xx_ serial_函数赋值给 uart_ops 结构体的成员。

static struct uart_ops s3c24xx_serial_ops = {
.pm		= s3c24xx_serial_pm,
.tx_empty	= s3c24xx_serial_tx_empty,//发送缓冲区空
.get_mctrl	= s3c24xx_serial_get_mctrl,//得到 modem 控制设置
.set_mctrl	= s3c24xx_serial_set_mctrl,//设置 modem 控制(MCR)
.stop_tx	= s3c24xx_serial_stop_tx,//停止接收字符
.start_tx	= s3c24xx_serial_start_tx,//开始传输字符
.stop_rx	= s3c24xx_serial_stop_rx,//停止接收字符
.enable_ms	= s3c24xx_serial_enable_ms,// modem 状态中断使能
.break_ctl	= s3c24xx_serial_break_ctl,// 控制 break 信号的传输
.startup	= s3c24xx_serial_startup,//启动端口
.shutdown	= s3c24xx_serial_shutdown,// 禁用端口
.set_termios	= s3c24xx_serial_set_termios,//改变端口参数
.type		= s3c24xx_serial_type,//返回描述特定端口的常量字符串指针
.release_port	= s3c24xx_serial_release_port,/释放端口占用的内存和I/O 资源
.request_port	= s3c24xx_serial_request_port,
.config_port	= s3c24xx_serial_config_port,
.verify_port	= s3c24xx_serial_verify_port,//验证新的串行端口信息
};
回过头来看 s3c24xx_uart_port 结构体,其中的 s3c24xx_uart_info 成员是一些针对 S3C2410 UART 的信息,其定义如代码清单如下所示。

static struct s3c24xx_uart_info s3c2410_uart_inf = {
.name		= "Samsung S3C2410 UART",
.type		= PORT_S3C2410,
.fifosize	= 16,
.rx_fifomask	= S3C2410_UFSTAT_RXMASK,
.rx_fifoshift	= S3C2410_UFSTAT_RXSHIFT,
.rx_fifofull	= S3C2410_UFSTAT_RXFULL,
.tx_fifofull	= S3C2410_UFSTAT_TXFULL,
.tx_fifomask	= S3C2410_UFSTAT_TXMASK,
.tx_fifoshift	= S3C2410_UFSTAT_TXSHIFT,
.get_clksrc	= s3c2410_serial_getsource,
.set_clksrc	= s3c2410_serial_setsource,
.reset_port	= s3c2410_serial_resetport,
};
在 S3C2410 串口驱动中,针对 UART 的设置(UCONn、ULCONn、UFCONn 寄存器等)被封装到 s3c2410_uartcfg 结构体中,其定义如下所示。

struct s3c2410_uartcfg {
unsigned char	   hwport;	 /* hardware port number */
unsigned char	   unused;
unsigned short	   flags;
upf_t		   uart_flags;	 /* default uart flags */

unsigned long	   ucon;	 /* value of ucon for port */
unsigned long	   ulcon;	 /* value of ulcon for port */
unsigned long	   ufcon;	 /* value of ufcon for port */

struct s3c24xx_uart_clksrc *clocks;
unsigned int		    clocks_size;
};
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()添加端口,整个过程的对应代码如下所示:

static int __init s3c24xx_serial_modinit(void)
{
int ret;

ret = uart_register_driver(&s3c24xx_uart_drv);
if (ret < 0) {
printk(KERN_ERR "failed to register UART driver\n");
return -1;
}

s3c2400_serial_init();
s3c2410_serial_init();
s3c2412_serial_init();
s3c2440_serial_init();

return 0;
}
static inline int s3c2440_serial_init(void)
{
return s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf);
}
static int s3c24xx_serial_init(struct platform_driver *drv,
struct s3c24xx_uart_info *info)
{
dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
return platform_driver_register(drv);
}
probe函数:

static int s3c2440_serial_probe(struct platform_device *dev)
{
dbg("s3c2440_serial_probe: dev=%p\n", dev);
return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
}
static int s3c24xx_serial_probe(struct platform_device *dev,
struct s3c24xx_uart_info *info)
{
struct s3c24xx_uart_port *ourport;
int ret;

dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);

ourport = &s3c24xx_serial_ports[probe_index];
probe_index++;

dbg("%s: initialising port %p...\n", __FUNCTION__, ourport);

ret = s3c24xx_serial_init_port(ourport, info, dev);//初始化 UART 端口
if (ret < 0)
goto probe_err;

dbg("%s: adding port\n", __FUNCTION__);
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);//添加 uart_port
platform_set_drvdata(dev, &ourport->port);

return 0;

probe_err:
return ret;
}
s3c24xx_serial_init_port

static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
struct s3c24xx_uart_info *info,
struct platform_device *platdev)
{
struct uart_port *port = &ourport->port;
struct s3c2410_uartcfg *cfg;
struct resource *res;

dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);

if (platdev == NULL)
return -ENODEV;

cfg = s3c24xx_dev_to_cfg(&platdev->dev);

if (port->mapbase != 0)
return 0;

if (cfg->hwport > 3)
return -EINVAL;

/* setup info for port *//* 为端口设置 info 成员 */
port->dev	= &platdev->dev;
ourport->info	= info;

/* copy the info in from provided structure *//* 初始化 fifosize */
ourport->port.fifosize = info->fifosize;

dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);

port->uartclk = 1;

if (cfg->uart_flags & UPF_CONS_FLOW) {/* 如果使用流控 */
dbg("s3c24xx_serial_init_port: enabling flow control\n");
port->flags |= UPF_CONS_FLOW;
}

/* sort our the physical and virtual addresses for each UART */
/* 利用平台资源中记录的信息初始化 UART 端口的基地址、中断号 */
res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
if (res == NULL) {
printk(KERN_ERR "failed to find memory resource for uart\n");
return -EINVAL;
}

dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);

port->mapbase	= res->start;
port->membase	= S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);
port->irq	= platform_get_irq(platdev, 0);
if (port->irq < 0)
port->irq = 0;

ourport->clk	= clk_get(&platdev->dev, "uart");

dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
port->mapbase, port->membase, port->irq, port->uartclk);

/* reset the fifos (and setup the uart) */
s3c24xx_serial_resetport(port, cfg);/* 复位 fifo 并设置 UART */
return 0;
}
s3c24xx_serial_resetport

static inline int s3c24xx_serial_resetport(struct uart_port * port,
struct s3c2410_uartcfg *cfg)
{
struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);

return (info->reset_port)(port, cfg);
}
这个info_reset_port就是s3c2410_serial_resetport

static int s3c2410_serial_resetport(struct uart_port *port,
struct s3c2410_uartcfg *cfg)
{
dbg("s3c2410_serial_resetport: port=%p (%08lx), cfg=%p\n",
port, port->mapbase, cfg);

wr_regl(port, S3C2410_UCON,  cfg->ucon);
wr_regl(port, S3C2410_ULCON, cfg->ulcon);

/* reset both fifos */
/* 复位两个 fifo */
wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
wr_regl(port, S3C2410_UFCON, cfg->ufcon);

return 0;
}
在 S3C2410 串口驱动模块被卸载时,它会最终调用 uart_remove_one_port()释放uart_port,并调用 uart_unregister_driver()注销 uart_driver,如代码如下所示。

static void _ _exit s3c24xx_serial_modexit(void)
{
s3c2410_serial_exit();
//注销 uart_driver
uart_unregister_driver(&s3c24xx_uart_drv);
}

static inline void s3c2410_serial_exit(void)
{
//注销平台驱动
platform_driver_unregister(&s3c2410_serial_drv);
}

static int s3c24xx_serial_remove(struct platform_device *dev)
{
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
//移除 UART 端口
if (port)
uart_remove_one_port(&s3c24xx_uart_drv, port);
return 0;
}
上述代码中对 S3C24xx_serial_remove()的调用发生在 platform_driver_unregister()之后,由于 S3C2410 的 UART 是集成于 SoC 芯片内部的一个独立的硬件单元,因此也被作为一个平台设备而定义。

S3C2410 串口数据收发

S3C2410 串口驱动 uart_ops 结构体的 startup ()成员函数 s3c24xx_serial_startup()用于启动端口,申请端口的发送、接收中断,使能端口的发送和接收,其实现如代码所示。

static int s3c24xx_serial_startup(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
int ret;

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

rx_enabled(port) = 1;//置接收使能状态为 1
//申请接收中断
ret = request_irq(RX_IRQ(port),
s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);

if (ret != 0) {
printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
return ret;
}

ourport->rx_claimed = 1;

dbg("requesting tx irq...\n");

tx_enabled(port) = 1;//置发送使能状态为 1
//申请发送中断
ret = request_irq(TX_IRQ(port),
s3c24xx_serial_tx_chars, 0,
s3c24xx_serial_portname(port), ourport);

if (ret) {
printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
goto err;
}

ourport->tx_claimed = 1;

dbg("s3c24xx_serial_startup ok\n");

/* the port reset code should have done the correct
* register setup for the port controls */
/* 端口复位代码应该已经为端口控制设置了正确的寄存器 */
return ret;

err:
s3c24xx_serial_shutdown(port);
return ret;
}
s3c24xx_serial_startup()的“反函数”为 s3c24xx_serial_shutdown(),其释放中断,禁止发送和接收,实现如代码 所示。

static void s3c24xx_serial_shutdown(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);

if (ourport->tx_claimed) {
free_irq(TX_IRQ(port), ourport);
tx_enabled(port) = 0;//置发送使能状态为 0\
ourport->tx_claimed = 0;
}

if (ourport->rx_claimed) {
free_irq(RX_IRQ(port), ourport);
ourport->rx_claimed = 0;
rx_enabled(port) = 0;//置接收使能状态为 0
}
}
S3C2410 串口驱动 uart_ops 结构体的 tx_empty()成员函数 s3c24xx_serial_tx_empty()用于判断发送缓冲区是否为空,其实现如代码所示,当使能 FIFO 模式的时候,判断 UFSTATn 寄存器,否则判断 UTRSTATn 寄存器的相应位。

static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
{       //fifo 模式,检查 UFSTATn 寄存器
struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
unsigned long ufcon = rd_regl(port, S3C2410_UFCON);

if (ufcon & S3C2410_UFCON_FIFOMODE) {
if ((ufstat & info->tx_fifomask) != 0 ||   //Tx fifo 数据数非 0
(ufstat & info->tx_fifofull))  // Tx fifo 满
return 0;   //0:非空

return 1;   //1:空
}

return s3c24xx_serial_txempty_nofifo(port);
}
static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
{
return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);//非 fifo 模式,检查 UTRSTATn 寄存器
}

S3C2410 串口驱动 uart_ops 结构体的 start_tx()成员函数 s3c24xx_serial_start_tx(),用于启动发送, stop_rx()成员函数 s3c24xx_serial_stop_rx()用于停止发送,代码如下所示:

static void s3c24xx_serial_start_tx(struct uart_port *port)
{
if (!tx_enabled(port)) {//如果端口发送未使能
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);

enable_irq(TX_IRQ(port));//使能发送中断
tx_enabled(port) = 1;//置端口发送使能状态为 1
}
}
static void s3c24xx_serial_stop_rx(struct uart_port *port)
{
if (rx_enabled(port)) {//如果端口发送已使能
dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
disable_irq(RX_IRQ(port));//禁止发送中断
rx_enabled(port) = 0;//置端口发送使能状态为 0
}
}
在 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 缓冲,其实现如代码如下所示:

static irqreturn_t
s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
struct s3c24xx_uart_port *ourport = dev_id;
struct uart_port *port = &ourport->port;//获得 uart_port
struct tty_struct *tty = port->info->tty;//获得 tty_struct
unsigned int ufcon, ch, flag, ufstat, uerstat;
int max_count = 64;

while (max_count-- > 0) {
ufcon = rd_regl(port, S3C2410_UFCON);
ufstat = rd_regl(port, S3C2410_UFSTAT);
//如果接收到 0 个字符
if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
break;

uerstat = rd_regl(port, S3C2410_UERSTAT);
ch = rd_regb(port, S3C2410_URXH);//读出字符

if (port->flags & UPF_CONS_FLOW) {
int txe = s3c24xx_serial_txempty_nofifo(port);

if (rx_enabled(port)) { //如果端口为使能接收状态
if (!txe) {//如果发送缓冲区为空
rx_enabled(port) = 0;//置端口使能接收状态为 0
continue;
}
} else {//端口为禁止接收状态
if (txe) {//如果发送缓冲区非空
ufcon |= S3C2410_UFCON_RESETRX;
wr_regl(port, S3C2410_UFCON, ufcon);
rx_enabled(port) = 1;//置端口使能接收状态为 1
goto out;
}
continue;
}
}

/* insert the character into the buffer */
/* 将接收到的字符写入 buffer */
flag = TTY_NORMAL;
port->icount.rx++;

if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
ch, uerstat);

/* check for break */
if (uerstat & S3C2410_UERSTAT_BREAK) {
dbg("break!\n");
port->icount.brk++;
if (uart_handle_break(port))
goto ignore_char;
}

if (uerstat & S3C2410_UERSTAT_FRAME)
port->icount.frame++;
if (uerstat & S3C2410_UERSTAT_OVERRUN)
port->icount.overrun++;

uerstat &= port->read_status_mask;

if (uerstat & S3C2410_UERSTAT_BREAK)
flag = TTY_BREAK;
else if (uerstat & S3C2410_UERSTAT_PARITY)
flag = TTY_PARITY;
else if (uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
flag = TTY_FRAME;
}

if (uart_handle_sysrq_char(port, ch))
goto ignore_char;
//插入字符到 tty 设备的 flip 缓冲
uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);

ignore_char:
continue;
}
tty_flip_buffer_push(tty);//刷新 tty 设备的 flip 缓冲

out:
return IRQ_HANDLED;
}
上述代码的 uart_insert_char()函数是串口核心层对 tty_insert_flip_char()的封装,它作为内联函数被定义于 serial_core.h 文件中。

s3c24xx_serial_tx_chars()读取 uart_info 中环形缓冲区中的字符,写入调用 UTXHn 寄存器。

static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->info->xmit;//得到环形缓冲区
int count = 256;//最多一次发 256 个字符

if (port->x_char) {//如果定义了 xchar,发送
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
port->x_char = 0;
goto out;
}

/* if there isnt anything more to transmit, or the uart is now
* stopped, disable the uart and exit
*/
/* 如果没有更多的字符需要发送,或者 UART Tx 停止,则停止 UART 并退出 */
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
s3c24xx_serial_stop_tx(port);
goto out;
}

/*  try and drain the buffer... */
/* 尝试把环行 buffer 中的数据发空 */
while (!uart_circ_empty(xmit) && count-- > 0) {
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;

wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
}
/* 如果环形缓冲区中剩余的字符少于 WAKEUP_CHARS,唤醒上层 */
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
//如果发送环形 buffer 为空
if (uart_circ_empty(xmit))
s3c24xx_serial_stop_tx(port);//停止发送

out:
return IRQ_HANDLED;
}
S3C2410 串口线路设置

S3C2410 串口驱动 uart_ops 结构体的 set_termios()成员函数用于改变端口的参数设置,包括波特率、字长、停止位、奇偶校验等,它会根据传递给它的 port、termios 参数成员的值设置 S3C2410 UART 的 ULCONn、UCONn、UMCONn 等寄存器,其实现如下代码所示。

static void s3c24xx_serial_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
struct s3c24xx_uart_port *ourport = to_ourport(port);
struct s3c24xx_uart_clksrc *clksrc = NULL;
struct clk *clk = NULL;
unsigned long flags;
unsigned int baud, quot;
unsigned int ulcon;
unsigned int umcon;

/*
* We don't support modem control lines.
*//* 不支持 modem 控制信号线 */
termios->c_cflag &= ~(HUPCL | CMSPAR);
termios->c_cflag |= CLOCAL;

/*
* Ask the core to calculate the divisor for us.
*/
/* 请求内核计算分频以便产生对应的波特率 */
baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);

if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
quot = port->custom_divisor;
else
quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);

/* check to see if we need  to change clock source */
/* 检查以确定是否需要改变时钟源 */
if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
s3c24xx_serial_setsource(port, clksrc);

if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
clk_disable(ourport->baudclk);
ourport->baudclk  = NULL;
}

clk_enable(clk);

ourport->clksrc = clksrc;
ourport->baudclk = clk;
}
/* 设置字长 */
switch (termios->c_cflag & CSIZE) {
case CS5:
dbg("config: 5bits/char\n");
ulcon = S3C2410_LCON_CS5;
break;
case CS6:
dbg("config: 6bits/char\n");
ulcon = S3C2410_LCON_CS6;
break;
case CS7:
dbg("config: 7bits/char\n");
ulcon = S3C2410_LCON_CS7;
break;
case CS8:
default:
dbg("config: 8bits/char\n");
ulcon = S3C2410_LCON_CS8;
break;
}

/* preserve original lcon IR settings *//* 保留以前的 lcon IR 设置 */
ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);

if (termios->c_cflag & CSTOPB)
ulcon |= S3C2410_LCON_STOPB;
/* 设置是否采用 RTS、CTS 自动流空 */
umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;

if (termios->c_cflag & PARENB) {
if (termios->c_cflag & PARODD)
ulcon |= S3C2410_LCON_PODD;//奇校验
else
ulcon |= S3C2410_LCON_PEVEN;//偶校验
} else {
ulcon |= S3C2410_LCON_PNONE;//无校验
}

spin_lock_irqsave(&port->lock, flags);

dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);

wr_regl(port, S3C2410_ULCON, ulcon);
wr_regl(port, S3C2410_UBRDIV, quot);
wr_regl(port, S3C2410_UMCON, umcon);

dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
rd_regl(port, S3C2410_ULCON),
rd_regl(port, S3C2410_UCON),
rd_regl(port, S3C2410_UFCON));

/*
* Update the per-port timeout./* 更新端口的超时 */
*/
uart_update_timeout(port, termios->c_cflag, baud);

/*
* Which character status flags are we interested in?
*//*对什么字符状态标志感兴趣?*/
port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
if (termios->c_iflag & INPCK)
port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;

/*
* Which character status flags should we ignore?
*//* 要忽略什么字符状态标志?*/
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;

/*
* Ignore all characters if CREAD is not set.
*//* 如果 CREAD 未设置,忽略所用字符 */
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= RXSTAT_DUMMY_READ;

spin_unlock_irqrestore(&port->lock, flags);
}
由于 S3C2410 集成 UART 并不包含完整的 Modem 控制信号线,因此其 uart_ops结构体的 get_mctrl()、set_mctrl()成员函数的实现非常简单。

总结

TTY 设备驱动的主体工作围绕 tty_driver 这个结构体的成员函数展开,主要应实现其中的数据发送和接收流程以及 tty 设备线路设置接口函数。针对串口,内核实现了串口核心层,这个层实现了串口设备通用的 tty_driver。因此,串口设备驱动的主体工作从 tty_driver 转移到了 uart_driver。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: