大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。
- 硬件资源及描写叙述
s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作。UART 使用系统时钟能够支持最高 115.2Kbps 的波特率。每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO。
寄存器 | 名称 | 地址 | 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) |
UART 线性控制寄存器(ULCONn) | ULCON0 ULCON1 ULCON2 |
0x50000000 0x50004000 0x50008000 |
linux-2.6.31/arch/arm/plat-s3c/include/plat/regs-serial.h
#define S3C2410_PA_UART0 (S3C24XX_PA_UART) #define S3C2410_PA_UART1 (S3C24XX_PA_UART + 0x4000 ) #define S3C2410_PA_UART2 (S3C24XX_PA_UART + 0x8000 ) #define S3C2443_PA_UART3 (S3C24XX_PA_UART + 0xC000 ) linux-2.6.31/arch/arm/plat-s3c/include/plat/map.h #define S3C24XX_VA_UART S3C_VA_UART//静态映射后的虚拟地址 #define S3C2410_PA_UART (0x50000000)//物理地址 #define S3C24XX_SZ_UART SZ_1M |
UART 控制寄存器(UCONn) | UCON0 UCON1 UCON2 | 0x50000004 0x50004004 0x50008004 | #define S3C2410_UCON (0x04) |
UART FIFO 控制寄存器(UFCONn) | UFCON0 UFCON1 UFCON2 | 0x50000008 0x50004008 0x50008008 | #define S3C2410_UFCON (0x08) |
UART MODEM 控制寄存器(UMCONn) | UMCON0 UMCON1 | 0x5000000C 0x5000400C | #define S3C2410_UMCON (0x0C) |
UART 接收发送状态寄存器(UTRSTATn) | UTRSTAT0 UTRSTAT1 UTRSTAT2 | 0x50000010 0x50004010 0x50008010 | #define S3C2410_UTRSTAT (0x10) |
UART 错误状态寄存器(UERSTATn) | UERSTAT0 UERSTAT1 UERSTAT2 | 0x50000014 0x50004014 0x50008014 | #define S3C2410_UERSTAT (0x14) |
UART FIFO 状态寄存器(UFSTATn) | UFSTAT0 UFSTAT1 UFSTAT2 | 0x50000018 0x50004018 0x50008018 | #define S3C2410_UFSTAT (0x18) |
UART MODEM 状态寄存器(UMSTATn) | UFSTAT0 UFSTAT1 | 0x5000001C 0x5000401C | #define S3C2410_UMSTAT (0x1C) |
UART 发送缓存寄存器(UTXHn) | UTXH0
UTXH1 UTXH2 | 0x50000020(L) 0x50000023(B) 0x50004020(L) 0x50004023(B) 0x50008020(L) 0x50008023(B) | #define S3C2410_UTXH (0x20) |
UART 接收缓存寄存器(URXHn) | URXH0
URXH1 URXH2 | 0x50000024(L) 0x50000027(B) 0x50004024(L) 0x50004027(B) 0x50008024(L) 0x50008027(B) | #define S3C2410_URXH (0x24) |
UART 波特率除数寄存器(UBRDIVn) | UBRDIV0 UBRDIV1 UBRDIV2 | 0x50000028 0x50004028 0x50008028 | #define S3C2410_UBRDIV (0x28) |
ULCON | 位 | 描写叙述 | 在linux中的描写叙述 linux-2.6.31/arch/arm/plat-s3c/include/plat/regs-serial.h |
Reserved | [7] | ||
Infrared Mode | [6] | 决定是否使用红外模式 0 =正常模式操作 1 = 红外接收发送模式 |
#define S3C2410_LCON_IRM (1<<6) |
Parity Mode | [5:3] | 在UART发送接收操作中定义奇偶码的生成和检 验类型 0xx = No parity 100 = Odd parity 101 = Even parity 110 = Parity forced/checked as 1 111 = Parity forced/checked as 0 |
#define S3C2410_LCON_PNONE (0x0) #define S3C2410_LCON_PEVEN (0x5 << 3) #define S3C2410_LCON_PODD (0x4 << 3) #define S3C2410_LCON_PMASK (0x7 << 3) |
Number of Stop Bit | [2] | 定义度搜按个停止位用于帧末信号 0 =每帧一个停止位 1 =每帧两个停止位 |
#define S3C2410_LCON_STOPB (1<<2) |
Word Length | [1:0] | 指出发送接收每帧的数据位数 00 = 5-bits 01 = 6-bits 10 = 7-bits 11 = 8-bits |
#define S3C2410_LCON_CS5 (0x0) #define S3C2410_LCON_CS6 (0x1) #define S3C2410_LCON_CS7 (0x2) #define S3C2410_LCON_CS8 (0x3) #define S3C2410_LCON_CSMASK (0x3) |
UFCON | 位 | 描写叙述 | 在linux中的描写叙述 |
Tx FIFO Trigger Level | [7:6] | 决定发送FIFO的触发等级 00 = Empty 01 = 16-byte 10 = 32-byte 11 = 48-byte |
#define S3C2440_UFCON_TXTRIG0 (0<<6) #define S3C2440_UFCON_TXTRIG16 (1<<6) #define S3C2440_UFCON_TXTRIG32 (2<<6) #define S3C2440_UFCON_TXTRIG48 (3<<6) |
Rx FIFO Trigger Level | [5:4] | 决定接收FIFO的触发等级 00 = 1-byte 01 = 8-byte 10 = 16-byte 11 = 32-byte |
#define S3C2440_UFCON_RXTRIG1 (0<<4) #define S3C2440_UFCON_RXTRIG8 (1<<4) #define S3C2440_UFCON_RXTRIG16 (2<<4) #define S3C2440_UFCON_RXTRIG32 (3<<4) |
Reserved | [3] | ||
Tx FIFO Reset | [2] | 在重置FIFO后自己主动清除 0 = Normal 1= Tx FIFO reset |
#define S3C2410_UFCON_RESETTX (1<<2) |
Rx FIFO Reset | [1] | 在重置FIFO后自己主动清除 0 = Normal 1= Rx FIFO reset |
#define S3C2410_UFCON_RESETRX (1<<1) #define S3C2410_UFCON_RESETBOTH (3<<1) |
FIFO Enable | [0] | 0 = Disable 1 = Enable |
|
默认设置为 #define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \ S3C2410_UFCON_TXTRIG0 | \ S3C2410_UFCON_RXTRIG8 ) |
使能FIFO Tx FIFO为空时触发中断 Rx FIFO中包括8个字节时触发中断 |
||
UFSTAT | 位 | 描写叙述 | 在linux中的表示 |
Reserved | [15] | ||
Tx FIFO Full | [14] | 仅仅要在发送操作中发送FIFO满,则自己主动置 1。 0 = 0-byte ≤ Tx FIFO data ≤ 63-byte 1 = Full |
#define S3C2440_UFSTAT_TXFULL (1<<14) |
Tx FIFO Count | [13:8] | 发送FIFO中的数据数量 | #define S3C2440_UFSTAT_TXSHIFT (8) |
Reserved | [7] | ||
Rx FIFO Full | [6] | 仅仅要在接收操作中接收FIFO满,则自己主动置 1。 0 = 0-byte ≤ Rx FIFO data ≤ 63-byte 1 = Full |
#define S3C2440_UFSTAT_RXFULL (1<<6) |
Rx FIFO Count | [5:0] | 接收FIFO中的数据数量 | #define S3C2440_UFSTAT_RXSHIFT (0) |
- uart驱动程序概述
linux的uart驱动程序建立在tty驱动程序之上(串口也是一个终端),程序源码主要在drivers/serial/文件夹下。当中 serial_core.c实现了uart设备的通用tty驱动层,当中定义了一组通用接口,包含3个结构体:uart_driver, uart_port,uart_ops(
include/serial_core.h
)。因此,实现一个平台的uart驱动程序仅仅要实现这3个结构体就可以。
serial_core(定义): | samsumg.c (实现): |
struct uart_driver { struct module *owner; const char *driver_name; const char *dev_name; int major; int minor; int nr; struct console *cons; /* | static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .dev_name = “s3c2410_serial”,//设备名称,这个名称必须和 //根文件系统中/etc/inittab文件里的串口名称一致 .nr = CONFIG_SERIAL_SAMSUNG_UARTS, .cons = S3C24XX_SERIAL_CONSOLE, .driver_name = S3C24XX_SERIAL_NAME,//驱动名称 .major = S3C24XX_SERIAL_MAJOR, .minor = S3C24XX_SERIAL_MINOR, }; |
uart_driver封装了tty_driver,使底层uart驱动不用关心ttr_driver。一个tty驱动程序必须注冊/注销 tty_driver,而uart驱动则变为注冊/注销uart_driver。使用例如以下接口:
int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_driver *drv);
实际上,uart_register_driver()和uart_unregister_driver()中分别包括了tty_register_driver()和tty_unregister_driver()的操作。
serial_core(定义): | samsumg.h samsumg.c(实现): |
struct uart_port { spinlock_t lock; /* port lock */ unsigned long iobase; /* in/out[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */ unsigned int (*serial_in)(struct uart_port *, int); void (*serial_out)(struct uart_port *, int, int); unsigned int irq; /* irq number */ unsigned int uartclk; /* base uart clock */ unsigned int fifosize; /* tx fifo size */ unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ unsigned char iotype; /* io access style */ unsigned char unused1; #define UPIO_PORT (0) unsigned int read_status_mask; /* driver specific */ struct console *cons; /* struct console, if any */ upf_t flags; #define UPF_FOURPORT ((__force upf_t) (1 << 1)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) unsigned int mctrl; /* current modem ctrl settings */ | struct s3c24xx_uart_port {//封装了uart_port unsigned char rx_claimed; unsigned char tx_claimed; unsigned int pm_level; unsigned long baudclk_rate; unsigned int rx_irq; struct s3c24xx_uart_info *info; #ifdef CONFIG_CPU_FREQ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { //3 uarts default [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 CONFIG_SERIAL_SAMSUNG_UARTS > 2 [2] = { |
uart_port用于描写叙述一个UARTport(直接相应于一个串口)的I/Oport或I/O内存地址、FIFO大小、port类型等信息。串口核心层提供例如以下函数来加入�1个port:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);
对上述函数的调用应该发生在uart_register_driver()之后,uart_add_one_port()的一个最重要作用是封装了 tty_register_device()。
uart_add_one_port()的“反函数”是uart_remove_one_port(),当中会调用tty_unregister_device(),原型为:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port);
serial_core.h(定义): | samsumg.c (实现): |
struct uart_ops { unsigned int (*tx_empty)(struct uart_port *); void (*set_mctrl)(struct uart_port *, unsigned int mctrl); unsigned int (*get_mctrl)(struct uart_port *); void (*stop_tx)(struct uart_port *); void (*start_tx)(struct uart_port *); void (*send_xchar)(struct uart_port *, char ch); void (*stop_rx)(struct uart_port *); void (*enable_ms)(struct uart_port *); void (*break_ctl)(struct uart_port *, int ctl); int (*startup)(struct uart_port *); void (*shutdown)(struct uart_port *); void (*flush_buffer)(struct uart_port *); void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); void (*set_ldisc)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate); int (*set_wake)(struct uart_port *, unsigned int state); /* /* /* | static struct uart_ops s3c24xx_serial_ops = { .pm = s3c24xx_serial_pm, .tx_empty = s3c24xx_serial_tx_empty, .get_mctrl = s3c24xx_serial_get_mctrl, .set_mctrl = s3c24xx_serial_set_mctrl, .stop_tx = s3c24xx_serial_stop_tx, .start_tx = s3c24xx_serial_start_tx, .stop_rx = s3c24xx_serial_stop_rx, .enable_ms = s3c24xx_serial_enable_ms, .break_ctl = s3c24xx_serial_break_ctl, .startup = s3c24xx_serial_startup, .shutdown = s3c24xx_serial_shutdown, .set_termios = s3c24xx_serial_set_termios, .type = s3c24xx_serial_type, .release_port = s3c24xx_serial_release_port, .request_port = s3c24xx_serial_request_port, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port, }; |
在使用串口核心层这个通用串口tty驱动层的接口后,一个串口驱动要完毕的主要工作将包含:
- 定义uart_driver、uart_ops、uart_port等结构体的实例并在适当的地方依据详细硬件和驱动的情况初始化它们,当然详细设备 xxx的驱动能够将这些结构套在新定义的xxx_uart_driver、xxx_uart_ops、xxx_uart_port之内。
- 在模块初始化时调用uart_register_driver()和uart_add_one_port()以注冊UART驱动并加入�端口,在模块卸载时调用uart_unregister_driver()和uart_remove_one_port()以注销UART驱动并移除端口。
- 依据详细硬件的datasheet实现uart_ops中的成员函数,这些函数的实现成为UART驱动的主体工作。
- 串口驱动初始化过程
在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()加入�端口。
platform_device_driver 參考:
Linux Platform Device and Driver
Linux driver model —– platform
回过头来看s3c24xx_uart_info结构体(s3c24xx_uart_port的成员),是一些针对s3c2440 uart 的信息,在/drivers/serial/s3c2440.c中:
static struct s3c24xx_uart_info s3c2440_uart_inf = { .name = “Samsung S3C2440 UART”, .type = PORT_S3C2440, .fifosize = 64, .rx_fifomask = S3C2440_UFSTAT_RXMASK, .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, .rx_fifofull = S3C2440_UFSTAT_RXFULL, .tx_fifofull = S3C2440_UFSTAT_TXFULL, .tx_fifomask = S3C2440_UFSTAT_TXMASK, .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, .get_clksrc = s3c2440_serial_getsource, .set_clksrc = s3c2440_serial_setsource, .reset_port = s3c2440_serial_resetport, }; |
- 串口操作函数,uart_ops接口函数
S3C2410串口驱动uart_ops结构体的startup ()成员函数s3c24xx_serial_startup()用于启动port,申请port的发送、接收中断,使能port的发送和接收。
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”, rx_enabled(port) = 1;//接收使能 ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, if (ret != 0) { ourport->rx_claimed = 1; dbg(“requesting tx irq…\n”); tx_enabled(port) = 1;//发送使能 ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, if (ret) { ourport->tx_claimed = 1; dbg(“s3c24xx_serial_startup ok\n”); /* the port reset code should have done the correct return ret; err: |
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) { if (ourport->rx_claimed) { |
tx_empty()成员函数s3c24xx_serial_tx_empty()用于推断发送缓冲区是否为空,当使能FIFO模式的时候,推断UFSTATn寄存器,否则推断UTRSTATn寄存器的对应位。
static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) { 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) { return 1; return s3c24xx_serial_txempty_nofifo(port); static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) | 在samsung.h中定义了例如以下操作寄存器的宏: /* register access controls */ #define portaddr(port, reg) ((port)->membase + (reg)) #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) #define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) |
start_tx ()成员函数s3c24xx_serial_start_tx()用于启动发送,而stop_rx()成员函数 s3c24xx_serial_stop_tx()用于停止发送。
static void s3c24xx_serial_start_tx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); if (!tx_enabled(port)) { enable_irq(ourport->tx_irq); | static void s3c24xx_serial_stop_tx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); if (tx_enabled(port)) { |
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; unsigned int udivslot = 0; /* /* baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) /* check to see if we need to change clock source 检查以确定是否须要改变时钟源 */ if (ourport->clksrc != clksrc || ourport->baudclk != clk) { if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { clk_enable(clk); ourport->clksrc = clksrc; if (ourport->info->has_divslot) { udivslot = udivslot_table[div & 15]; /* preserve original lcon IR settings */ if (termios->c_cflag & CSTOPB) umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0; if (termios->c_cflag & PARENB) { spin_lock_irqsave(&port->lock, flags); dbg(“setting ulcon to %08x, brddiv to %d, udivslot %08x\n”, wr_regl(port, S3C2410_ULCON, ulcon); if (ourport->info->has_divslot) dbg(“uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n”, /* /* /* /* spin_unlock_irqrestore(&port->lock, flags); |
- 接收和发送中断处理函数
在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缓冲。
s3c24xx_serial_tx_chars()读取uart_info中环形缓冲区中的字符,写入调用UTXHn寄存器。
#define S3C2410_UERSTAT_PARITY (0x1000)
static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id) while (max_count– > 0) { if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) uerstat = rd_regl(port, S3C2410_UERSTAT); if (port->flags & UPF_CONS_FLOW) { if (rx_enabled(port)) { /* insert the character into the buffer */ flag = TTY_NORMAL; if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) { /* check for break */ if (uerstat & S3C2410_UERSTAT_FRAME) uerstat &= port->read_status_mask; if (uerstat & S3C2410_UERSTAT_BREAK) if (uart_handle_sysrq_char(port, ch)) uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ignore_char: out: | static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) if (port->x_char) { /* if there isnt anything more to transmit, or the uart is now if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { /* try and drain the buffer… */ while (!uart_circ_empty(xmit) && count– > 0) { wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) if (uart_circ_empty(xmit)) out: |
- 串口測试
getty 功能说明:设置终端机模式,连线速率和管制线路,可是s3c2440_serial1无法工作,原因可能是由于开发板上仅仅有一个串口。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/118829.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...