寄存器 | 名称 | 地址 | 在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#define S3C_UART_OFFSET (0x4000) |
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) |
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; /* * these are private; the low level driver should not * touch these; they should be initialised to NULL */ struct uart_state *state; struct tty_driver *tty_driver; }; |
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, }; |
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) #define UPIO_HUB6 (1) #define UPIO_MEM (2) #define UPIO_MEM32 (3) #define UPIO_AU (4) /* Au1x00 type IO */ #define UPIO_TSI (5) /* Tsi108/109 type IO */ #define UPIO_DWAPB (6) /* DesignWare APB UART */ #define UPIO_RM9000 (7) /* RM9000 type IO */ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ struct uart_info *info; /* pointer to parent info */ struct uart_icount icount; /* statistics */ struct console *cons; /* struct console, if any */ #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ) unsigned long sysrq; /* sysrq timeout */ #endif upf_t flags; #define UPF_FOURPORT ((__force upf_t) (1 << 1)) #define UPF_SAK ((__force upf_t) (1 << 2)) #define UPF_SPD_MASK ((__force upf_t) (0x1030)) #define UPF_SPD_HI ((__force upf_t) (0x0010)) #define UPF_SPD_VHI ((__force upf_t) (0x0020)) #define UPF_SPD_CUST ((__force upf_t) (0x0030)) #define UPF_SPD_SHI ((__force upf_t) (0x1000)) #define UPF_SPD_WARP ((__force upf_t) (0x1010)) #define UPF_SKIP_TEST ((__force upf_t) (1 << 6)) #define UPF_AUTO_IRQ ((__force upf_t) (1 << 7)) #define UPF_HARDPPS_CD ((__force upf_t) (1 << 11)) #define UPF_LOW_LATENCY ((__force upf_t) (1 << 13)) #define UPF_BUGGY_UART ((__force upf_t) (1 << 14)) #define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15)) #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16)) #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) /* The exact UART type is known and should not be probed. */ #define UPF_FIXED_TYPE ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) #define UPF_FIXED_PORT ((__force upf_t) (1 << 29)) #define UPF_DEAD ((__force upf_t) (1 << 30)) #define UPF_IOREMAP ((__force upf_t) (1 << 31)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) #define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) unsigned int mctrl; /* current modem ctrl settings */ unsigned int timeout; /* character-based timeout */ unsigned int type; /* port type */ const struct uart_ops *ops; unsigned int custom_divisor; unsigned int line; /* port index */ resource_size_t mapbase; /* for ioremap */ struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char suspended; unsigned char unused[2]; void *private_data; /* generic platform data pointer */ }; |
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; unsigned int tx_irq; struct s3c24xx_uart_info *info; struct s3c24xx_uart_clksrc *clksrc; struct clk *clk; struct clk *baudclk; struct uart_port port; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; #endif }; 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] = { .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 #if CONFIG_SERIAL_SAMSUNG_UARTS > 3 [3] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX3, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 3, } } #endif }; |
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); /* * Return a string describing the type of the port */ const char *(*type)(struct uart_port *); /* * Release IO and memory resources used by the port. * This includes iounmap if necessary. */ void (*release_port)(struct uart_port *); /* * Request IO and memory resources used by the port. * This includes iomapping the port if necessary. */ int (*request_port)(struct uart_port *); void (*config_port)(struct uart_port *, int); int (*verify_port)(struct uart_port *, struct serial_struct *); int (*ioctl)(struct uart_port *, unsigned int, unsigned long); #ifdef CONFIG_CONSOLE_POLL void (*poll_put_char)(struct uart_port *, unsigned char); int (*poll_get_char)(struct uart_port *); #endif }; |
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, }; |
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, }; |
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;//接收使能 ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, s3c24xx_serial_portname(port), ourport);//申请接收中断,s3c24xx_serial_rx_chars是中断处理函数 if (ret != 0) { printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); return ret; } ourport->rx_claimed = 1; dbg("requesting tx irq...\n"); tx_enabled(port) = 1;//发送使能 ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, s3c24xx_serial_portname(port), ourport);//申请发送中断 if (ret) { printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); 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; } |
static void s3c24xx_serial_shutdown(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); if (ourport->tx_claimed) { free_irq(ourport->tx_irq, ourport); tx_enabled(port) = 0; ourport->tx_claimed = 0; } if (ourport->rx_claimed) { free_irq(ourport->rx_irq, ourport); ourport->rx_claimed = 0; rx_enabled(port) = 0; } } |
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) { if ((ufstat & info->tx_fifomask) != 0 || (ufstat & info->tx_fifofull)) return 0; return 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); } |
在samsung.h中定义了如下操作寄存器的宏: /* register access controls */ #define portaddr(port, reg) ((port)->membase + (reg)) #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) //read regester long #define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) |
static void s3c24xx_serial_start_tx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); if (!tx_enabled(port)) { if (port->flags & UPF_CONS_FLOW) s3c24xx_serial_rx_disable(port); enable_irq(ourport->tx_irq); tx_enabled(port) = 1; } } |
static void s3c24xx_serial_stop_tx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); if (tx_enabled(port)) { disable_irq_nosync(ourport->tx_irq); tx_enabled(port) = 0; if (port->flags & UPF_CONS_FLOW) s3c24xx_serial_rx_enable(port); } } |
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; /* * We don't support modem control lines. */ 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) { dbg("selecting clock %p\n", 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; ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; } if (ourport->info->has_divslot) { unsigned int div = ourport->baudclk_rate / baud; udivslot = udivslot_table[div & 15]; dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); } /* 设置字长 */ 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 */ ulcon |= (cfg->ulcon & S3C2410_LCON_IRM); if (termios->c_cflag & CSTOPB) ulcon |= S3C2410_LCON_STOPB; 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, udivslot %08x\n", ulcon, quot, udivslot); wr_regl(port, S3C2410_ULCON, ulcon); wr_regl(port, S3C2410_UBRDIV, quot); wr_regl(port, S3C2410_UMCON, umcon); if (ourport->info->has_divslot) wr_regl(port, S3C2443_DIVSLOT, udivslot); 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. */ if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= RXSTAT_DUMMY_READ; spin_unlock_irqrestore(&port->lock, flags); } |
#define S3C2410_UERSTAT_PARITY (0x1000) 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; struct tty_struct *tty = port->info->port.tty; 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); 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; continue; } } else { if (txe) { ufcon |= S3C2410_UFCON_RESETRX; wr_regl(port, S3C2410_UFCON, ufcon); rx_enabled(port) = 1; goto out; } continue; } } /* insert the character into the 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; uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag); ignore_char: continue; } tty_flip_buffer_push(tty); out: return IRQ_HANDLED; } |
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; if (port->x_char) { 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 */ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { s3c24xx_serial_stop_tx(port); goto out; } /* try and drain the 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++; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); if (uart_circ_empty(xmit)) s3c24xx_serial_stop_tx(port); out: return IRQ_HANDLED; } |