本次说一下tty子系统的驱动编程,因为UART相关的寄存器比较多,同时,应用比较广泛,所以本次的驱动程序量也不少,而且只是完成和特定CPU相关的一部分,通用的部分本次都没有涉及到.在写驱动之前,我们先来了解一下硬件资源和tty子系统框架.

1、串口通信简介

串口通信指串口按位(bit)发送和接收字节,串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线 接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2 米;而对于串口而言,长度可达1200米。

串口通信所采用的通信协议为RS-232,RS-232通信方式允许简单连接三线:Tx、Rx和地线。但是对于数据传输,双方必须对数据定时采用使用相同的波特率。RS-232(ANSI/EIA-232标准)是IBM-PC及其兼容机上的串行连接标准。可用于许多用途,比如连接鼠标、打印机或者Modem,同时也 可以接工业仪器仪表。用于驱动和连线的改进,实际应用中RS-232的传输长度或者速度常常超过标准的值。RS-232只限于PC串口和设备间点对点的通信。

2、串口的通信基本模型如下图所示:

3,硬件资源;

如上图,tiny4412的板子上引出了3个UART接口,分别是UART0 , 2, 3,它有5组UART,另外一组UART1用在了WIFI + BT模块,UART4没有引出来,这几组UART的区别主要是FIFO的大小,UART0的FIFO为256byte,UART1, 4的FIFO大小为64byte,UART2, 3的FIFO大小为16 byte,这5组都支持DMA操作,中断等,最大传输速率是4Mbps(512k byte/s),在这里,我们会使用UART3来测试串口功能.

4,UART寄存器及地址

如上图是官方给的UART寄存器map,这个跟STM32是很类似的,需要了解的小伙伴也可以直接找STM32的UART资料了解.UART0上集成了红外功能,上一次,我们通过GPIO口中断的方式,开发了红外驱动,这次也可以使用UART0来做红外驱动,相对简单一些,但是,我们的UART0已经用作了调试终端,所以,就先不这样做了.

下面是UART的一些寄存器:

网上有很多对于寄存器进行讲解如何使用的一些博文和视频,这方面的基础知识,请自行学习,这里就不多说了,接下来说一下tty子系统的一下知识.

上图是TTY子系统的层次架构,我们需要实现的就是底层驱动程序,因为这一部分是和特定CPU相关的,其它几层都是通用的.

上图是UART驱动程序的映射图,也就是tty的框架.了解了这些之后,我们看一看代码实现的具体细节实现(图用的是网上的):

有了具体的实现框图之后,我们来实现一下驱动程序(驱动暂时没这么顺利,没调好输入部分,先贴一个三星官方的):#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)

#define SUPPORT_SYSRQ

#endif

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

#include

#endif

#include

#include

#include

#include

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

#include

#include

#endif

#include

#include

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

#include

#endif

#include "samsung.h"

/* UART name and device definitions */

#define S3C24XX_SERIAL_NAME"ttySAC"

#define S3C24XX_SERIAL_MAJOR204

#define S3C24XX_SERIAL_MINOR64

/* Baudrate definition*/

#define MAX_BAUD3000000

#define MIN_BAUD0

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

/* definitions for dma mode */

#define ENABLE_UART_DMA_MODEtrue

#define DMA_TRANS_LIMIT64

#define RX_BUFFER_SIZE256

#define BURST_1BYTE0

#endif

/* macros to change one thing to another */

#define tx_enabled(port) ((port)->unused[0])

#define rx_enabled(port) ((port)->unused[1])

/* flag to ignore all characters coming in */

#define RXSTAT_DUMMY_READ (0x10000000)

/* ? - where has parity gone?? */

#define S3C2410_UERSTAT_PARITY (0x1000)

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

static struct s3c2410_dma_client samsung_uart_dma_client = {

.name = "samsung-uart-dma",

};

static void prepare_dma(struct uart_dma_data *dma,

unsigned len, dma_addr_t buf);

#endif

static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port)

{

return container_of(port, struct s3c24xx_uart_port, port);

}

/* translate a port to the device name */

static inline const char *s3c24xx_serial_portname(struct uart_port *port)

{

return to_platform_device(port->dev)->name;

}

static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)

{

return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);

}

/*

* s3c64xx and later SoC's include the interrupt mask and status registers in

* the controller itself, unlike the s3c24xx SoC's which have these registers

* in the interrupt controller. Check if the port type is s3c64xx or higher.

*/

static int s3c24xx_serial_has_interrupt_mask(struct uart_port *port)

{

return to_ourport(port)->info->type == PORT_S3C6400;

}

static void s3c24xx_serial_rx_enable(struct uart_port *port)

{

unsigned long flags;

unsigned int ucon, ufcon;

int count = 10000;

spin_lock_irqsave(&port->lock, flags);

while (--count && !s3c24xx_serial_txempty_nofifo(port))

udelay(100);

ufcon = rd_regl(port, S3C2410_UFCON);

ufcon |= S3C2410_UFCON_RESETRX;

wr_regl(port, S3C2410_UFCON, ufcon);

ucon = rd_regl(port, S3C2410_UCON);

ucon |= S3C2410_UCON_RXIRQMODE;

wr_regl(port, S3C2410_UCON, ucon);

rx_enabled(port) = 1;

spin_unlock_irqrestore(&port->lock, flags);

}

static void s3c24xx_serial_rx_disable(struct uart_port *port)

{

unsigned long flags;

unsigned int ucon;

spin_lock_irqsave(&port->lock, flags);

ucon = rd_regl(port, S3C2410_UCON);

ucon &= ~S3C2410_UCON_RXIRQMODE;

wr_regl(port, S3C2410_UCON, ucon);

rx_enabled(port) = 0;

spin_unlock_irqrestore(&port->lock, flags);

}

static void s3c24xx_serial_stop_tx(struct uart_port *port)

{

struct s3c24xx_uart_port *ourport = to_ourport(port);

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

struct exynos_uart_dma *uart_dma = &ourport->uart_dma;

#endif

if (tx_enabled(port)) {

if (s3c24xx_serial_has_interrupt_mask(port))

__set_bit(S3C64XX_UINTM_TXD,

portaddrl(port, S3C64XX_UINTM));

else

disable_irq_nosync(ourport->tx_irq);

tx_enabled(port) = 0;

if (port->flags & UPF_CONS_FLOW)

s3c24xx_serial_rx_enable(port);

}

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

uart_dma->ops->stop(uart_dma->tx.ch);

#endif

}

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

if (s3c24xx_serial_has_interrupt_mask(port))

__clear_bit(S3C64XX_UINTM_TXD,

portaddrl(port, S3C64XX_UINTM));

else

enable_irq(ourport->tx_irq);

tx_enabled(port) = 1;

}

}

static void s3c24xx_serial_stop_rx(struct uart_port *port)

{

struct s3c24xx_uart_port *ourport = to_ourport(port);

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

struct exynos_uart_dma *uart_dma = &ourport->uart_dma;

#endif

if (rx_enabled(port)) {

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

if (s3c24xx_serial_has_interrupt_mask(port))

__set_bit(S3C64XX_UINTM_RXD,

portaddrl(port, S3C64XX_UINTM));

else

disable_irq_nosync(ourport->rx_irq);

rx_enabled(port) = 0;

}

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

uart_dma->ops->stop(uart_dma->rx.ch);

#endif

}

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

static void uart_rx_dma_request(struct s3c24xx_uart_port *ourport)

{

struct uart_port *port = &ourport->port;

struct exynos_uart_dma *uart_dma = &ourport->uart_dma;

uart_dma->rx_dst_addr = dma_map_single(port->dev,

uart_dma->rx_buff,

uart_dma->rx.req_size,

DMA_FROM_DEVICE);

if (dma_mapping_error(port->dev, uart_dma->rx_dst_addr))

pr_err("Rx DMA mapping error!!!\n");

/* prepare rx dma mode */

prepare_dma(&uart_dma->rx,

uart_dma->rx.req_size, uart_dma->rx_dst_addr);

}

static void callback_uart_rx_dma(void *data)

{

struct exynos_uart_dma *uart_dma = container_of(data,

struct exynos_uart_dma, rx);

struct s3c24xx_uart_port *ourport = container_of(uart_dma,

struct s3c24xx_uart_port, uart_dma);

struct uart_port *port = &ourport->port;

struct tty_struct *tty = port->state->port.tty;

unsigned int uerstat = ourport->err_occurred;

unsigned int received_size = 0;

dma_addr_t src_addr = 0;

dma_addr_t dst_addr = 0;

uart_dma->ops->getposition(uart_dma->rx.ch, &src_addr, &dst_addr);

received_size = dst_addr - (unsigned int)uart_dma->rx_dst_addr;

dma_unmap_single(port->dev, uart_dma->rx_dst_addr,

received_size, DMA_FROM_DEVICE);

/* Error check after DMA transfer */

if (uerstat != 0) {

pr_err("UART Rx DMA Error(0x%x)!!!\n", uerstat);

if (uerstat & S3C2410_UERSTAT_BREAK) {

dbg("break!\n");

port->icount.brk += received_size;

}

if (uerstat & S3C2410_UERSTAT_FRAME)

port->icount.frame += received_size;

if (uerstat & S3C2410_UERSTAT_OVERRUN)

port->icount.overrun += received_size;

uerstat &= port->read_status_mask;

ourport->err_occurred = 0;

}

tty_insert_flip_string(tty, uart_dma->rx_buff, received_size);

tty_flip_buffer_push(tty);

if (uart_dma->rx.busy == 1)

uart_rx_dma_request(ourport);

}

static void enable_rx_dma(struct uart_port *port)

{

unsigned long flags;

unsigned int ufcon, ucon;

spin_lock_irqsave(&port->lock, flags);

ufcon = rd_regl(port, S3C2410_UFCON);

ufcon &= ~(0x7 << 4);

ufcon |= (0x1 << 2) | (0x1 << 1) | S5PV210_UFCON_RXTRIG64;

wr_regl(port, S3C2410_UFCON, ufcon);

/* set Rx mode to DMA mode */

ucon = rd_regl(port, S3C2410_UCON);

ucon &= ~((0x7 << UCON_RXBURST_SZ) |

(0x1 << UCON_TIMEOUT_VAL) |

(0x1 << UCON_EMPTYINT_EN) |

(0x1 << UCON_DMASUS_EN) |

UCON_RXMODE_CL);

ucon |= (BURST_1BYTE << UCON_RXBURST_SZ) |

(0xf << UCON_TIMEOUT_VAL) |

(0x1 << UCON_EMPTYINT_EN) |

(0x1 << UCON_DMASUS_EN) |

(0x1 << UCON_TIMEOUT_EN) |

UCON_RXDMA_MODE;

wr_regl(port, S3C2410_UCON, ucon);

spin_unlock_irqrestore(&port->lock, flags);

}

static void callback_uart_tx_dma(void *data)

{

struct exynos_uart_dma *uart_dma;

struct s3c24xx_uart_port *ourport;

struct uart_port *port;

struct circ_buf *xmit;

unsigned long ucon, uintm;

uart_dma = container_of(data, struct exynos_uart_dma, tx);

ourport = container_of(uart_dma, struct s3c24xx_uart_port, uart_dma);

port = &ourport->port;

dbg("callback_uart_dma\n");

dma_unmap_single(port->dev, uart_dma->tx_src_addr,

uart_dma->tx.req_size, DMA_TO_DEVICE);

xmit = &port->state->xmit;

xmit->tail = (xmit->tail + uart_dma->tx.req_size)&(UART_XMIT_SIZE - 1);

port->icount.tx += uart_dma->tx.req_size;

/* Set Tx mode to interrupt mode */

ucon = rd_regl(port, S3C2410_UCON);

ucon &= ~((0x7 << UCON_TXBURST_SZ) | UCON_TXMODE_CL);

ucon |= UCON_TXCPU_MODE;

wr_regl(port, S3C2410_UCON, ucon);

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)

uart_write_wakeup(port);

if (uart_circ_empty(xmit)) {

s3c24xx_serial_stop_tx(port);

} else {

/* Unmask tx DMA */

uintm = rd_regl(port, S3C64XX_UINTM);

uintm &= ~(0x1 << 2);

wr_regl(port, S3C64XX_UINTM, uintm);

}

}

static void prepare_dma(struct uart_dma_data *dma,

unsigned len, dma_addr_t buf)

{

struct exynos_uart_dma *uart_dma;

struct samsung_dma_prep info;

struct samsung_dma_config config;

info.cap = DMA_SLAVE;

info.len = len;

info.fp = callback_uart_tx_dma;

info.fp_param = dma;

info.direction = dma->direction;

info.buf = buf;

config.direction = dma->direction;

config.fifo = dma->fifo_base;

/* burst size = 1byte (1, 4, 8bytes) */

config.width = DMA_SLAVE_BUSWIDTH_1_BYTE;

if (dma->direction == DMA_MEM_TO_DEV) {

uart_dma = container_of((void *)dma,

struct exynos_uart_dma, tx);

info.fp = callback_uart_tx_dma;

} else {

uart_dma = container_of((void *)dma,

struct exynos_uart_dma, rx);

info.fp = callback_uart_rx_dma;

}

uart_dma->ops->config(dma->ch, &config);

uart_dma->ops->prepare(dma->ch, &info);

uart_dma->ops->trigger(dma->ch);

}

static int acquire_dma(struct exynos_uart_dma *uart_dma)

{

struct samsung_dma_req req;

uart_dma->ops = samsung_dma_get_ops();

req.cap = DMA_SLAVE;

req.client = &samsung_uart_dma_client;

if (uart_dma->rx.busy == 0) {

uart_dma->rx.ch = uart_dma->ops->request(uart_dma->rx.req_ch,

&req);

uart_dma->rx.busy = 1;

}

uart_dma->tx.ch = uart_dma->ops->request(uart_dma->tx.req_ch, &req);

return 1;

}

#endif

static void s3c24xx_serial_enable_ms(struct uart_port *port)

{

}

static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port)

{

return to_ourport(port)->info;

}

static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port)

{

struct s3c24xx_uart_port *ourport;

if (port->dev == NULL)

return NULL;

ourport = container_of(port, struct s3c24xx_uart_port, port);

return ourport->cfg;

}

static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,

unsigned long ufstat)

{

struct s3c24xx_uart_info *info = ourport->info;

if (ufstat & info->rx_fifofull)

return ourport->port.fifosize;

return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;

}

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

static void uart_rx_drain_fifo(struct s3c24xx_uart_port *ourport, int size)

{

struct uart_port *port = &ourport->port;

struct tty_struct *tty = port->state->port.tty;

unsigned int ch, flag, ufstat, uerstat;

int count = 0;

while (size-- > 0) {

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

/* insert the character into the buffer */

flag = TTY_NORMAL;

port->icount.rx++;

if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {

pr_err("DMA rx drain fifo error!\n");

if (uerstat & S3C2410_UERSTAT_FRAME) {

pr_err("frame!!");

port->icount.frame++;

}

if (uerstat & S3C2410_UERSTAT_OVERRUN) {

pr_err("overrun!!");

port->icount.overrun++;

}

uerstat &= port->read_status_mask;

if (uerstat & S3C2410_UERSTAT_BREAK) {

pr_err("break!!");

flag = TTY_BREAK;

} else if (uerstat & S3C2410_UERSTAT_PARITY) {

pr_err("parity!!");

flag = TTY_PARITY;

} else if (uerstat & (S3C2410_UERSTAT_FRAME |

S3C2410_UERSTAT_OVERRUN))

flag = TTY_FRAME;

}

uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,

ch, flag);

count++;

}

tty_flip_buffer_push(tty);

}

#endif

/* ? - where has parity gone?? */

#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->state->port.tty;

#ifndef CONFIG_SERIAL_SAMSUNG_DMA

unsigned int ufcon, ch, flag, ufstat, uerstat;

int max_count = 64;

#else

struct exynos_uart_dma *uart_dma = &ourport->uart_dma;

unsigned int ufcon, ufstat, utrstat, uerstat = 0;

unsigned int ch, flag, received_size = 0;

dma_addr_t src_addr = 0;

dma_addr_t dst_addr = 0;

int max_count = 256;

if (uart_dma->use_dma == 0)

goto rx_use_cpu;

utrstat = rd_regl(port, S3C2410_UTRSTAT);

uart_dma->ops->getposition(uart_dma->rx.ch, &src_addr, &dst_addr);

received_size = dst_addr - (unsigned int)uart_dma->rx_dst_addr;

if ((received_size == 0) && (((utrstat >> 16) & 0xff) == 0)) {

wr_regl(port, S3C2410_UTRSTAT, UTRSTAT_TIMEOUT);

goto out;

}

uart_dma->rx.busy = 0;

callback_uart_rx_dma(&uart_dma->rx);

uart_dma->ops->flush(uart_dma->rx.ch);

uart_dma->rx.busy = 1;

uart_rx_drain_fifo(ourport, (utrstat >> 16) & 0xff);

uart_rx_dma_request(ourport);

wr_regl(port, S3C2410_UTRSTAT, UTRSTAT_TIMEOUT);

goto out;

rx_use_cpu:

#endif

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->state->xmit;

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

struct exynos_uart_dma *uart_dma = &ourport->uart_dma;

int remain_data;

unsigned long ucon;

unsigned long uintm;

#endif

unsigned long flags;

int count = 256;

spin_lock_irqsave(&port->lock, flags);

if (port->x_char) {

wr_regb(port, S3C2410_UTXH, port->x_char);

port->icount.tx++;

port->x_char = 0;

goto out;

}

/* if there isn't 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;

}

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

if (uart_dma->use_dma == 0)

goto tx_use_cpu;

remain_data = uart_circ_chars_pending(xmit);

if (remain_data >= DMA_TRANS_LIMIT) {

ucon = rd_regl(port, S3C2410_UCON);

ucon &= ~((0x7 << UCON_TXBURST_SZ) | UCON_TXMODE_CL);

ucon |= (BURST_1BYTE << UCON_TXBURST_SZ) | UCON_TXDMA_MODE;

wr_regl(port, S3C2410_UCON, ucon);

/* Mask Tx interrupt */

uintm = rd_regl(port, S3C64XX_UINTM);

uintm |= (0x1 << 2);

wr_regl(port, S3C64XX_UINTM, uintm);

/*

* If head is over maximum buffer size,

* DMA should transfer data up to end of buffer

*/

if (xmit->head - xmit->tail < 0)

uart_dma->tx.req_size = UART_XMIT_SIZE - xmit->tail;

else

uart_dma->tx.req_size = remain_data;

uart_dma->tx_src_addr = dma_map_single(port->dev,

&(xmit->buf[xmit->tail]),

uart_dma->tx.req_size,

DMA_TO_DEVICE);

if (dma_mapping_error(port->dev, uart_dma->tx_src_addr)) {

pr_err("DMA Mapping Error!!!\n");

goto tx_use_cpu;

}

dbg("%s: prepare_dma\n", __func__);

/* parepare DMA */

prepare_dma(&uart_dma->tx, uart_dma->tx.req_size,

uart_dma->tx_src_addr);

goto out;

}

tx_use_cpu:

#endif

/* 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) {

spin_unlock(&port->lock);

uart_write_wakeup(port);

spin_lock(&port->lock);

}

if (uart_circ_empty(xmit))

s3c24xx_serial_stop_tx(port);

out:

spin_unlock_irqrestore(&port->lock, flags);

return IRQ_HANDLED;

}

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

static irqreturn_t s3c24xx_serial_err_chars(int irq, void *id)

{

struct s3c24xx_uart_port *ourport = id;

struct uart_port *port = &ourport->port;

ourport->err_occurred = rd_regl(port, S3C2410_UERSTAT);

pr_err("Rx DMA Error!!!(0x%x)\n", ourport->err_occurred);

return IRQ_HANDLED;

}

#endif

/* interrupt handler for s3c64xx and later SoC's.*/

static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)

{

struct s3c24xx_uart_port *ourport = id;

struct uart_port *port = &ourport->port;

unsigned int pend = rd_regl(port, S3C64XX_UINTP);

irqreturn_t ret = IRQ_HANDLED;

if (pend & S3C64XX_UINTM_RXD_MSK) {

ret = s3c24xx_serial_rx_chars(irq, id);

wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK);

}

if (pend & S3C64XX_UINTM_TXD_MSK) {

ret = s3c24xx_serial_tx_chars(irq, id);

wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK);

}

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

if (pend & S3C64XX_UINTM_ERR_MSK) {

ret = s3c24xx_serial_err_chars(irq, id);

wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_ERR_MSK);

}

#endif

return ret;

}

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

}

/* no modem control lines */

static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)

{

unsigned int umstat = rd_regb(port, S3C2410_UMSTAT);

if (umstat & S3C2410_UMSTAT_CTS)

return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;

else

return TIOCM_CAR | TIOCM_DSR;

}

static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)

{

/* todo - possibly remove AFC and do manual CTS */

}

static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)

{

unsigned long flags;

unsigned int ucon;

spin_lock_irqsave(&port->lock, flags);

ucon = rd_regl(port, S3C2410_UCON);

if (break_state)

ucon |= S3C2410_UCON_SBREAK;

else

ucon &= ~S3C2410_UCON_SBREAK;

wr_regl(port, S3C2410_UCON, ucon);

spin_unlock_irqrestore(&port->lock, flags);

}

static void s3c24xx_serial_shutdown(struct uart_port *port)

{

struct s3c24xx_uart_port *ourport = to_ourport(port);

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

struct exynos_uart_dma *uart_dma = &ourport->uart_dma;

#endif

if (ourport->tx_claimed) {

if (!s3c24xx_serial_has_interrupt_mask(port))

free_irq(ourport->tx_irq, ourport);

tx_enabled(port) = 0;

ourport->tx_claimed = 0;

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

/* Free DMA tx channels */

if (uart_dma->use_dma == 1) {

uart_dma->ops->release(uart_dma->tx.ch,

&samsung_uart_dma_client);

dbg("%s tx free DMA : %x\n", __func__, port->mapbase);

}

#endif

}

if (ourport->rx_claimed) {

if (!s3c24xx_serial_has_interrupt_mask(port))

free_irq(ourport->rx_irq, ourport);

ourport->rx_claimed = 0;

rx_enabled(port) = 0;

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

/* Free DMA rx channels */

if (uart_dma->use_dma == 1 && uart_dma->rx.busy == 1) {

uart_dma->rx.busy = 0;

dma_unmap_single(port->dev, uart_dma->rx_dst_addr,

uart_dma->rx.req_size, DMA_FROM_DEVICE);

uart_dma->ops->release(uart_dma->rx.ch,

&samsung_uart_dma_client);

kfree(uart_dma->rx_buff);

dbg("%s rx free DMA : %x\n", __func__, port->mapbase);

}

#endif

}

/* Clear pending interrupts and mask all interrupts */

if (s3c24xx_serial_has_interrupt_mask(port)) {

wr_regl(port, S3C64XX_UINTP, 0xf);

wr_regl(port, S3C64XX_UINTM, 0xf);

}

}

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

if (ret != 0) {

pr_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) {

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

}

/* power power management control */

static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,

unsigned int old)

{

struct s3c24xx_uart_port *ourport = to_ourport(port);

ourport->pm_level = level;

switch (level) {

case 3:

if (!IS_ERR(ourport->baudclk))

clk_disable(ourport->baudclk);

clk_disable(ourport->clk);

break;

case 0:

clk_enable(ourport->clk);

if (!IS_ERR(ourport->baudclk))

clk_enable(ourport->baudclk);

break;

default:

pr_err("s3c24xx_serial: unknown pm %d\n", level);

}

}

/* baud rate calculation

*

* The UARTs on the S3C2410/S3C2440 can take their clocks from a number

* of different sources, including the peripheral clock ("pclk") and an

* external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")

* with a programmable extra divisor.

*

* The following code goes through the clock sources, and calculates the

* baud clocks (and the resultant actual baud rates) and then tries to

* pick the closest one and select that.

*

*/

#define MAX_CLK_NAME_LENGTH 15

static inline int s3c24xx_serial_getsource(struct uart_port *port)

{

struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);

unsigned int ucon;

if (info->num_clks == 1)

return 0;

ucon = rd_regl(port, S3C2410_UCON);

ucon &= info->clksel_mask;

return ucon >> info->clksel_shift;

}

static void s3c24xx_serial_setsource(struct uart_port *port,

unsigned int clk_sel)

{

struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);

unsigned int ucon;

if (info->num_clks == 1)

return;

ucon = rd_regl(port, S3C2410_UCON);

if ((ucon & info->clksel_mask) >> info->clksel_shift == clk_sel)

return;

ucon &= ~info->clksel_mask;

ucon |= clk_sel << info->clksel_shift;

wr_regl(port, S3C2410_UCON, ucon);

}

static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,

unsigned int req_baud, struct clk **best_clk,

unsigned int *clk_num)

{

struct s3c24xx_uart_info *info = ourport->info;

struct clk *clk;

unsigned long rate;

unsigned int cnt, baud, quot, clk_sel, best_quot = 0;

char clkname[MAX_CLK_NAME_LENGTH];

int calc_deviation, deviation = (1 << 30) - 1;

clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel :

ourport->info->def_clk_sel;

for (cnt = 0; cnt < info->num_clks; cnt++) {

if (!(clk_sel & (1 << cnt)))

continue;

sprintf(clkname, "clk_uart_baud%d", cnt);

clk = clk_get(ourport->port.dev, clkname);

if (IS_ERR(clk))

continue;

rate = clk_get_rate(clk);

if (!rate)

continue;

if (ourport->info->has_divslot) {

unsigned long div = rate / req_baud;

/* The UDIVSLOT register on the newer UARTs allows us to

* get a divisor adjustment of 1/16th on the baud clock.

*

* We don't keep the UDIVSLOT value (the 16ths we

* calculated by not multiplying the baud by 16) as it

* is easy enough to recalculate.

*/

quot = div / 16;

baud = rate / div;

} else {

quot = (rate + (8 * req_baud)) / (16 * req_baud);

baud = rate / (quot * 16);

}

quot--;

calc_deviation = req_baud - baud;

if (calc_deviation < 0)

calc_deviation = -calc_deviation;

if (calc_deviation < deviation) {

*best_clk = clk;

best_quot = quot;

*clk_num = cnt;

deviation = calc_deviation;

}

}

return best_quot;

}

/* udivslot_table[]

*

* This table takes the fractional value of the baud divisor and gives

* the recommended setting for the UDIVSLOT register.

*/

static u16 udivslot_table[16] = {

[0] = 0x0000,

[1] = 0x0080,

[2] = 0x0808,

[3] = 0x0888,

[4] = 0x2222,

[5] = 0x4924,

[6] = 0x4A52,

[7] = 0x54AA,

[8] = 0x5555,

[9] = 0xD555,

[10] = 0xD5D5,

[11] = 0xDDD5,

[12] = 0xDDDD,

[13] = 0xDFDD,

[14] = 0xDFDF,

[15] = 0xFFDF,

};

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 clk *clk = ERR_PTR(-EINVAL);

unsigned long flags;

unsigned int baud, quot, clk_sel = 0;

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, MIN_BAUD, MAX_BAUD);

quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel);

if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)

quot = port->custom_divisor;

if (IS_ERR(clk))

return;

/* check to see if we need to change clock source */

if (ourport->baudclk != clk) {

s3c24xx_serial_setsource(port, clk_sel);

if (!IS_ERR(ourport->baudclk)) {

clk_disable(ourport->baudclk);

ourport->baudclk = ERR_PTR(-EINVAL);

}

clk_enable(clk);

ourport->baudclk = clk;

ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;

}

if (ourport->info->has_divslot) {

unsigned int div = ourport->baudclk_rate / baud;

if (cfg->has_fracval) {

udivslot = (div & 15);

dbg("fracval = %04x\n", udivslot);

} else {

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

}

static const char *s3c24xx_serial_type(struct uart_port *port)

{

switch (port->type) {

case PORT_S3C2410:

return "S3C2410";

case PORT_S3C2440:

return "S3C2440";

case PORT_S3C2412:

return "S3C2412";

case PORT_S3C6400:

return "S3C6400/10";

default:

return NULL;

}

}

#define MAP_SIZE (0x100)

static void s3c24xx_serial_release_port(struct uart_port *port)

{

release_mem_region(port->mapbase, MAP_SIZE);

}

static int s3c24xx_serial_request_port(struct uart_port *port)

{

const char *name = s3c24xx_serial_portname(port);

return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;

}

static void s3c24xx_serial_config_port(struct uart_port *port, int flags)

{

struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);

if (flags & UART_CONFIG_TYPE &&

s3c24xx_serial_request_port(port) == 0)

port->type = info->type;

}

/*

* verify the new serial_struct (for TIOCSSERIAL).

*/

static int

s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)

{

struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);

if (ser->type != PORT_UNKNOWN && ser->type != info->type)

return -EINVAL;

return 0;

}

static void s3c24xx_serial_wake_peer(struct uart_port *port)

{

struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);

if (cfg->wake_peer)

cfg->wake_peer(port);

}

#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE

static struct console s3c24xx_serial_console;

#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console

#else

#define S3C24XX_SERIAL_CONSOLE NULL

#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,

.wake_peer= s3c24xx_serial_wake_peer,

};

static struct uart_driver s3c24xx_uart_drv = {

.owner= THIS_MODULE,

.driver_name= "s3c2410_serial",

.nr= CONFIG_SERIAL_SAMSUNG_UARTS,

.cons= S3C24XX_SERIAL_CONSOLE,

.dev_name= S3C24XX_SERIAL_NAME,

.major= S3C24XX_SERIAL_MAJOR,

.minor= S3C24XX_SERIAL_MINOR,

};

static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {

[0] = {

.port = {

.lock= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),

.iotype= UPIO_MEM,

.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,

.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,

.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,

.uartclk= 0,

.fifosize= 16,

.ops= &s3c24xx_serial_ops,

.flags= UPF_BOOT_AUTOCONF,

.line= 3,

}

}

#endif

};

/* s3c24xx_serial_resetport

*

* reset the fifos and other the settings.

*/

static void s3c24xx_serial_resetport(struct uart_port *port,

struct s3c2410_uartcfg *cfg)

{

struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);

unsigned long ucon = rd_regl(port, S3C2410_UCON);

unsigned int ucon_mask;

ucon_mask = info->clksel_mask;

if (info->type == PORT_S3C2440)

ucon_mask |= S3C2440_UCON0_DIVMASK;

ucon &= ucon_mask;

wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);

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

/* reset both fifos */

wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);

wr_regl(port, S3C2410_UFCON, cfg->ufcon);

/* some delay is required after fifo reset */

udelay(1);

}

#ifdef CONFIG_CPU_FREQ

static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,

unsigned long val, void *data)

{

struct s3c24xx_uart_port *port;

struct uart_port *uport;

port = container_of(nb, struct s3c24xx_uart_port, freq_transition);

uport = &port->port;

/* check to see if port is enabled */

if (port->pm_level != 0)

return 0;

/* try and work out if the baudrate is changing, we can detect

* a change in rate, but we do not have support for detecting

* a disturbance in the clock-rate over the change.

*/

if (IS_ERR(port->baudclk))

goto exit;

if (port->baudclk_rate == clk_get_rate(port->baudclk))

goto exit;

if (val == CPUFREQ_PRECHANGE) {

/* we should really shut the port down whilst the

* frequency change is in progress. */

} else if (val == CPUFREQ_POSTCHANGE) {

struct ktermios *termios;

struct tty_struct *tty;

if (uport->state == NULL)

goto exit;

tty = uport->state->port.tty;

if (tty == NULL)

goto exit;

termios = tty->termios;

if (termios == NULL) {

printk(KERN_WARNING "%s: no termios?\n", __func__);

goto exit;

}

s3c24xx_serial_set_termios(uport, termios, NULL);

}

exit:

return 0;

}

static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)

{

port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;

return cpufreq_register_notifier(&port->freq_transition,

CPUFREQ_TRANSITION_NOTIFIER);

}

static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)

{

cpufreq_unregister_notifier(&port->freq_transition,

CPUFREQ_TRANSITION_NOTIFIER);

}

#else

static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)

{

return 0;

}

static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)

{

}

#endif

/* s3c24xx_serial_init_port

*

* initialise a single serial port from the platform device given

*/

static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,

struct platform_device *platdev)

{

struct uart_port *port = &ourport->port;

struct s3c2410_uartcfg *cfg = ourport->cfg;

struct resource *res;

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

struct exynos_uart_dma *uart_dma = &ourport->uart_dma;

#endif

int ret;

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

if (platdev == NULL)

return -ENODEV;

if (port->mapbase != 0)

return 0;

/* setup info for port */

port->dev= &platdev->dev;

/* Startup sequence is different for s3c64xx and higher SoC's */

if (s3c24xx_serial_has_interrupt_mask(port))

s3c24xx_serial_ops.startup = s3c64xx_serial_startup;

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 */

res = platform_get_resource(platdev, IORESOURCE_MEM, 0);

if (res == NULL) {

pr_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 = S3C_VA_UART + (res->start & 0xfffff);

ret = platform_get_irq(platdev, 0);

if (ret < 0)

port->irq = 0;

else {

port->irq = ret;

ourport->rx_irq = ret;

ourport->tx_irq = ret + 1;

}

ret = platform_get_irq(platdev, 1);

if (ret > 0)

ourport->tx_irq = ret;

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

/* Keep all interrupts masked and cleared */

if (s3c24xx_serial_has_interrupt_mask(port)) {

wr_regl(port, S3C64XX_UINTM, 0xf);

wr_regl(port, S3C64XX_UINTP, 0xf);

wr_regl(port, S3C64XX_UINTSP, 0xf);

}

dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",

port->mapbase, port->membase, port->irq,

ourport->rx_irq, ourport->tx_irq, port->uartclk);

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

/* set tx/rx fifo base for dma */

if (uart_dma->use_dma) {

uart_dma->tx.fifo_base = port->mapbase + S3C2410_UTXH;

uart_dma->rx.fifo_base = port->mapbase + S3C2410_URXH;

}

#endif

/* reset the fifos (and setup the uart) */

s3c24xx_serial_resetport(port, cfg);

return 0;

}

static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,

struct device_attribute *attr,

char *buf)

{

struct uart_port *port = s3c24xx_dev_to_port(dev);

struct s3c24xx_uart_port *ourport = to_ourport(port);

if (IS_ERR(ourport->baudclk))

return -EINVAL;

return snprintf(buf, PAGE_SIZE, "* %s\n",

ourport->baudclk->name ?: "(null)");

}

static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);

/* Device driver serial port probe */

static const struct of_device_id s3c24xx_uart_dt_match[];

static inline struct s3c24xx_serial_drv_data *s3c24xx_get_driver_data(

struct platform_device *pdev)

{

#ifdef CONFIG_OF

if (pdev->dev.of_node) {

const struct of_device_id *match;

match = of_match_node(s3c24xx_uart_dt_match, pdev->dev.of_node);

return (struct s3c24xx_serial_drv_data *)match->data;

}

#endif

return (struct s3c24xx_serial_drv_data *)

platform_get_device_id(pdev)->driver_data;

}

static int s3c24xx_serial_probe(struct platform_device *pdev)

{

struct s3c24xx_uart_port *ourport;

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

struct exynos_uart_dma *uart_dma;

struct resource*dma_tx, *dma_rx;

#endif

int ret;

dbg("s3c24xx_serial_probe(%p) %d\n", pdev, pdev->id);

if (pdev->id >= CONFIG_SERIAL_SAMSUNG_UARTS)

return -EINVAL;

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

dma_tx = platform_get_resource(pdev, IORESOURCE_DMA, 0);

if (dma_tx == NULL) {

dev_err(&pdev->dev, "Unable to get UART-Tx dma resource\n");

return -ENXIO;

}

dma_rx = platform_get_resource(pdev, IORESOURCE_DMA, 1);

if (dma_rx == NULL) {

dev_err(&pdev->dev, "Unable to get UART-Rx dma resource\n");

return -ENXIO;

}

#endif

ourport = &s3c24xx_serial_ports[pdev->id];

ourport->drv_data = s3c24xx_get_driver_data(pdev);

if (!ourport->drv_data) {

dev_err(&pdev->dev, "could not find driver data\n");

return -ENODEV;

}

#ifdef CONFIG_SERIAL_SAMSUNG_DMA

uart_dma = &ourport->uart_dma;

uart_dma->use_dma = ENABLE_UART_DMA_MODE;

if (uart_dma->use_dma) {

uart_dma->rx.busy = 0;

uart_dma->tx.req_ch = dma_tx->start;

uart_dma->rx.req_ch = dma_rx->start;

uart_dma->tx.direction = DMA_MEM_TO_DEV;

uart_dma->rx.direction = DMA_DEV_TO_MEM;

}

#endif

ourport->baudclk = ERR_PTR(-EINVAL);

ourport->info = ourport->drv_data->info;

ourport->cfg = (pdev->dev.platform_data) ?

(struct s3c2410_uartcfg *)pdev->dev.platform_data :

ourport->drv_data->def_cfg;

ourport->port.fifosize = (ourport->info->fifosize) ?

ourport->info->fifosize :

ourport->drv_data->fifosize[pdev->id];

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

ret = s3c24xx_serial_init_port(ourport, pdev);

if (ret < 0)

goto probe_err;

dbg("%s: adding port\n", __func__);

uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);

platform_set_drvdata(pdev, &ourport->port);

ret = device_create_file(&pdev->dev, &dev_attr_clock_source);

if (ret < 0)

dev_err(&pdev->dev, "failed to add clock source attr.\n");

ret = s3c24xx_serial_cpufreq_register(ourport);

if (ret < 0)

dev_err(&pdev->dev, "failed to add cpufreq notifier\n");

return 0;

probe_err:

return ret;

}

static int __devexit s3c24xx_serial_remove(struct platform_device *dev)

{

struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);

if (port) {

s3c24xx_serial_cpufreq_deregister(to_ourport(port));

device_remove_file(&dev->dev, &dev_attr_clock_source);

uart_remove_one_port(&s3c24xx_uart_drv, port);

}

return 0;

}

/* UART power management code */

#ifdef CONFIG_PM_SLEEP

unsigned int s3c24xx_serial_mask_save[CONFIG_SERIAL_SAMSUNG_UARTS];

static int s3c24xx_serial_suspend(struct device *dev)

{

struct uart_port *port = s3c24xx_dev_to_port(dev);

s3c24xx_serial_mask_save[port->line] = rd_regl(port, S3C64XX_UINTM);

if (port)

uart_suspend_port(&s3c24xx_uart_drv, port);

return 0;

}

static int s3c24xx_serial_resume(struct device *dev)

{

struct uart_port *port = s3c24xx_dev_to_port(dev);

struct s3c24xx_uart_port *ourport = to_ourport(port);

if (port) {

clk_enable(ourport->clk);

wr_regl(port, S3C64XX_UINTM,

s3c24xx_serial_mask_save[port->line]);

s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));

clk_disable(ourport->clk);

uart_resume_port(&s3c24xx_uart_drv, port);

}

return 0;

}

static const struct dev_pm_ops s3c24xx_serial_pm_ops = {

.suspend = s3c24xx_serial_suspend,

.resume = s3c24xx_serial_resume,

};

#define SERIAL_SAMSUNG_PM_OPS(&s3c24xx_serial_pm_ops)

#else /* !CONFIG_PM_SLEEP */

#define SERIAL_SAMSUNG_PM_OPSNULL

#endif /* CONFIG_PM_SLEEP */

/* Console code */

#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE

static struct uart_port *cons_uart;

static int

s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)

{

struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);

unsigned long ufstat, utrstat;

if (ufcon & S3C2410_UFCON_FIFOMODE) {

/* fifo mode - check amount of data in fifo registers... */

ufstat = rd_regl(port, S3C2410_UFSTAT);

return (ufstat & info->tx_fifofull) ? 0 : 1;

}

/* in non-fifo mode, we go and use the tx buffer empty */

utrstat = rd_regl(port, S3C2410_UTRSTAT);

return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;

}

static void

s3c24xx_serial_console_putchar(struct uart_port *port, int ch)

{

unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);

while (!s3c24xx_serial_console_txrdy(port, ufcon))

barrier();

wr_regb(cons_uart, S3C2410_UTXH, ch);

}

static void

s3c24xx_serial_console_write(struct console *co, const char *s,

unsigned int count)

{

uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);

}

static void __init

s3c24xx_serial_get_options(struct uart_port *port, int *baud,

int *parity, int *bits)

{

struct clk *clk;

unsigned int ulcon;

unsigned int ucon;

unsigned int ubrdiv;

unsigned long rate;

unsigned int clk_sel;

char clk_name[MAX_CLK_NAME_LENGTH];

ulcon = rd_regl(port, S3C2410_ULCON);

ucon = rd_regl(port, S3C2410_UCON);

ubrdiv = rd_regl(port, S3C2410_UBRDIV);

dbg("s3c24xx_serial_get_options: port=%p\n"

"registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",

port, ulcon, ucon, ubrdiv);

if ((ucon & 0xf) != 0) {

/* consider the serial port configured if the tx/rx mode set */

switch (ulcon & S3C2410_LCON_CSMASK) {

case S3C2410_LCON_CS5:

*bits = 5;

break;

case S3C2410_LCON_CS6:

*bits = 6;

break;

case S3C2410_LCON_CS7:

*bits = 7;

break;

default:

case S3C2410_LCON_CS8:

*bits = 8;

break;

}

switch (ulcon & S3C2410_LCON_PMASK) {

case S3C2410_LCON_PEVEN:

*parity = 'e';

break;

case S3C2410_LCON_PODD:

*parity = 'o';

break;

case S3C2410_LCON_PNONE:

default:

*parity = 'n';

}

/* now calculate the baud rate */

clk_sel = s3c24xx_serial_getsource(port);

sprintf(clk_name, "clk_uart_baud%d", clk_sel);

clk = clk_get(port->dev, clk_name);

if (!IS_ERR(clk))

rate = clk_get_rate(clk);

else

rate = 1;

*baud = rate / (16 * (ubrdiv + 1));

dbg("calculated baud %d\n", *baud);

}

}

static int __init

s3c24xx_serial_console_setup(struct console *co, char *options)

{

struct uart_port *port;

int baud = 9600;

int bits = 8;

int parity = 'n';

int flow = 'n';

dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",

co, co->index, options);

/* is this a valid port */

if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)

co->index = 0;

port = &s3c24xx_serial_ports[co->index].port;

/* is the port configured? */

if (port->mapbase == 0x0)

return -ENODEV;

cons_uart = port;

dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);

/*

* Check whether an invalid uart number has been specified, and

* if so, search for the first available port that does have

* console support.

*/

if (options)

uart_parse_options(options, &baud, &parity, &bits, &flow);

else

s3c24xx_serial_get_options(port, &baud, &parity, &bits);

dbg("s3c24xx_serial_console_setup: baud %d\n", baud);

return uart_set_options(port, co, baud, parity, bits, flow);

}

static struct console s3c24xx_serial_console = {

.name= S3C24XX_SERIAL_NAME,

.device= uart_console_device,

.flags= CON_PRINTBUFFER,

.index= -1,

.write= s3c24xx_serial_console_write,

.setup= s3c24xx_serial_console_setup,

.data= &s3c24xx_uart_drv,

};

#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */

#ifdef CONFIG_CPU_S3C2410

static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = {

.info = &(struct s3c24xx_uart_info) {

.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,

.def_clk_sel= S3C2410_UCON_CLKSEL0,

.num_clks= 2,

.clksel_mask= S3C2410_UCON_CLKMASK,

.clksel_shift= S3C2410_UCON_CLKSHIFT,

},

.def_cfg = &(struct s3c2410_uartcfg) {

.ucon= S3C2410_UCON_DEFAULT,

.ufcon= S3C2410_UFCON_DEFAULT,

},

};

#define S3C2410_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2410_serial_drv_data)

#else

#define S3C2410_SERIAL_DRV_DATA (kernel_ulong_t)NULL

#endif

#ifdef CONFIG_CPU_S3C2412

static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = {

.info = &(struct s3c24xx_uart_info) {

.name= "Samsung S3C2412 UART",

.type= PORT_S3C2412,

.fifosize= 64,

.has_divslot= 1,

.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,

.def_clk_sel= S3C2410_UCON_CLKSEL2,

.num_clks= 4,

.clksel_mask= S3C2412_UCON_CLKMASK,

.clksel_shift= S3C2412_UCON_CLKSHIFT,

},

.def_cfg = &(struct s3c2410_uartcfg) {

.ucon= S3C2410_UCON_DEFAULT,

.ufcon= S3C2410_UFCON_DEFAULT,

},

};

#define S3C2412_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2412_serial_drv_data)

#else

#define S3C2412_SERIAL_DRV_DATA (kernel_ulong_t)NULL

#endif

#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2416) || \

defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2442)

static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = {

.info = &(struct s3c24xx_uart_info) {

.name= "Samsung S3C2440 UART",

.type= PORT_S3C2440,

.fifosize= 64,

.has_divslot= 1,

.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,

.def_clk_sel= S3C2410_UCON_CLKSEL2,

.num_clks= 4,

.clksel_mask= S3C2412_UCON_CLKMASK,

.clksel_shift= S3C2412_UCON_CLKSHIFT,

},

.def_cfg = &(struct s3c2410_uartcfg) {

.ucon= S3C2410_UCON_DEFAULT,

.ufcon= S3C2410_UFCON_DEFAULT,

},

};

#define S3C2440_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2440_serial_drv_data)

#else

#define S3C2440_SERIAL_DRV_DATA (kernel_ulong_t)NULL

#endif

#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) || \

defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450) || \

defined(CONFIG_CPU_S5PC100)

static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = {

.info = &(struct s3c24xx_uart_info) {

.name= "Samsung S3C6400 UART",

.type= PORT_S3C6400,

.fifosize= 64,

.has_divslot= 1,

.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,

.def_clk_sel= S3C2410_UCON_CLKSEL2,

.num_clks= 4,

.clksel_mask= S3C6400_UCON_CLKMASK,

.clksel_shift= S3C6400_UCON_CLKSHIFT,

},

.def_cfg = &(struct s3c2410_uartcfg) {

.ucon= S3C2410_UCON_DEFAULT,

.ufcon= S3C2410_UFCON_DEFAULT,

},

};

#define S3C6400_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c6400_serial_drv_data)

#else

#define S3C6400_SERIAL_DRV_DATA (kernel_ulong_t)NULL

#endif

#ifdef CONFIG_CPU_S5PV210

static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {

.info = &(struct s3c24xx_uart_info) {

.name= "Samsung S5PV210 UART",

.type= PORT_S3C6400,

.has_divslot= 1,

.rx_fifomask= S5PV210_UFSTAT_RXMASK,

.rx_fifoshift= S5PV210_UFSTAT_RXSHIFT,

.rx_fifofull= S5PV210_UFSTAT_RXFULL,

.tx_fifofull= S5PV210_UFSTAT_TXFULL,

.tx_fifomask= S5PV210_UFSTAT_TXMASK,

.tx_fifoshift= S5PV210_UFSTAT_TXSHIFT,

.def_clk_sel= S3C2410_UCON_CLKSEL0,

.num_clks= 2,

.clksel_mask= S5PV210_UCON_CLKMASK,

.clksel_shift= S5PV210_UCON_CLKSHIFT,

},

.def_cfg = &(struct s3c2410_uartcfg) {

.ucon= S5PV210_UCON_DEFAULT,

.ufcon= S5PV210_UFCON_DEFAULT,

},

.fifosize = { 256, 64, 16, 16 },

};

#define S5PV210_SERIAL_DRV_DATA ((kernel_ulong_t)&s5pv210_serial_drv_data)

#else

#define S5PV210_SERIAL_DRV_DATA(kernel_ulong_t)NULL

#endif

#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \

defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)

static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {

.info = &(struct s3c24xx_uart_info) {

.name= "Samsung Exynos4 UART",

.type= PORT_S3C6400,

.has_divslot= 1,

.rx_fifomask= S5PV210_UFSTAT_RXMASK,

.rx_fifoshift= S5PV210_UFSTAT_RXSHIFT,

.rx_fifofull= S5PV210_UFSTAT_RXFULL,

.tx_fifofull= S5PV210_UFSTAT_TXFULL,

.tx_fifomask= S5PV210_UFSTAT_TXMASK,

.tx_fifoshift= S5PV210_UFSTAT_TXSHIFT,

.def_clk_sel= S3C2410_UCON_CLKSEL0,

.num_clks= 1,

.clksel_mask= 0,

.clksel_shift= 0,

},

.def_cfg = &(struct s3c2410_uartcfg) {

.ucon= S5PV210_UCON_DEFAULT,

.ufcon= S5PV210_UFCON_DEFAULT,

.has_fracval= 1,

},

.fifosize = { 256, 64, 16, 16 },

};

#define EXYNOS4210_SERIAL_DRV_DATA ((kernel_ulong_t)&exynos4210_serial_drv_data)

#else

#define EXYNOS4210_SERIAL_DRV_DATA (kernel_ulong_t)NULL

#endif

static struct platform_device_id s3c24xx_serial_driver_ids[] = {

{

.name= "s3c2410-uart",

.driver_data= S3C2410_SERIAL_DRV_DATA,

}, {

.name= "s3c2412-uart",

.driver_data= S3C2412_SERIAL_DRV_DATA,

}, {

.name= "s3c2440-uart",

.driver_data= S3C2440_SERIAL_DRV_DATA,

}, {

.name= "s3c6400-uart",

.driver_data= S3C6400_SERIAL_DRV_DATA,

}, {

.name= "s5pv210-uart",

.driver_data= S5PV210_SERIAL_DRV_DATA,

}, {

.name= "exynos4210-uart",

.driver_data= EXYNOS4210_SERIAL_DRV_DATA,

},

{ },

};

MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids);

#ifdef CONFIG_OF

static const struct of_device_id s3c24xx_uart_dt_match[] = {

{ .compatible = "samsung,exynos4210-uart",

.data = (void *)EXYNOS4210_SERIAL_DRV_DATA },

{},

};

MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);

#else

#define s3c24xx_uart_dt_match NULL

#endif

static struct platform_driver samsung_serial_driver = {

.probe= s3c24xx_serial_probe,

.remove= __devexit_p(s3c24xx_serial_remove),

.id_table= s3c24xx_serial_driver_ids,

.driver= {

.name= "samsung-uart",

.owner= THIS_MODULE,

.pm= SERIAL_SAMSUNG_PM_OPS,

.of_match_table= s3c24xx_uart_dt_match,

},

};

/* module initialisation code */

static int __init s3c24xx_serial_modinit(void)

{

int ret;

ret = uart_register_driver(&s3c24xx_uart_drv);

if (ret < 0) {

pr_err("failed to register UART driver\n");

return -1;

}

return platform_driver_register(&samsung_serial_driver);

}

static void __exit s3c24xx_serial_modexit(void)

{

uart_unregister_driver(&s3c24xx_uart_drv);

}

module_init(s3c24xx_serial_modinit);

module_exit(s3c24xx_serial_modexit);

MODULE_LICENSE("GPL v2");

我们把上边驱动程序命名为samsung.c,把它放在Linux的/drivers/tty/serial目录下覆盖原来的samsung.c.修改之后,重新编译,用新内核启动开发板.

然后是测试代码:#include

#include

#include

#include

#include

#include

#define BAUDRATE B115200

#define UART_DEVICE "/dev/ttySAC3"

int speed_arr[] = {B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300,

B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300};

int name_arr[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200, 300,

115200, 38400, 19200, 9600, 4800, 2400, 1200, 300};

void set_speed(int fd, int speed)

{

int i, status;

struct termios opt;

tcgetattr(fd, &opt);

for(i = 0; i < sizeof(speed_arr) / sizeof(int); i++){

if(speed == name_arr[i]){

tcflush(fd, TCIOFLUSH);

cfsetispeed(&opt, speed_arr[i]);

cfsetospeed(&opt, speed_arr[i]);

status = tcsetattr(fd, TCSANOW, &opt);

if(status != 0){

perror("tcsetattr failed");

return;

}

tcflush(fd, TCIOFLUSH);

}

}

}

int set_parity(int fd, int databits, int stopbits, int parity)

{

struct termios options;

if ( tcgetattr( fd,&options) != 0) {

perror("SetupSerial 1");

return(FALSE);

}

options.c_cflag &= ~CSIZE;

switch (databits) /*设置数据位数*/

{

case 7:

options.c_cflag |= CS7;

break;

case 8:

options.c_cflag |= CS8;

break;

default:

fprintf(stderr,"Unsupported data size\n"); return (FALSE);

}

switch (parity)

{

case 'n':

case 'N':

options.c_cflag &= ~PARENB; /* Clear parity enable */

options.c_iflag &= ~INPCK; /* Enable parity checking */

break;

case 'o':

case 'O':

options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/

options.c_iflag |= INPCK; /* Disnable parity checking */

break;

case 'e':

case 'E':

options.c_cflag |= PARENB; /* Enable parity */

options.c_cflag &= ~PARODD; /* 转换为偶效验*/

options.c_iflag |= INPCK; /* Disnable parity checking */

break;

case 'S':

case 's': /*as no parity*/

options.c_cflag &= ~PARENB;

options.c_cflag &= ~CSTOPB;break;

default:

fprintf(stderr,"Unsupported parity\n");

return (FALSE);

}

/* 设置停止位*/

switch (stopbits)

{

case 1:

options.c_cflag &= ~CSTOPB;

break;

case 2:

options.c_cflag |= CSTOPB;

break;

default:

fprintf(stderr,"Unsupported stop bits\n");

return (FALSE);

}

/* Set input parity option */

if (parity != 'n')

options.c_iflag |= INPCK;

tcflush(fd,TCIFLUSH);

options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/

options.c_cc[VMIN] = 0; /* Update the options and do it NOW */

if (tcsetattr(fd,TCSANOW,&options) != 0)

{

perror("SetupSerial 3");

return (FALSE);

}

options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/

options.c_oflag &= ~OPOST; /*Output*/

return (TRUE);

}

int main(void)

{

int fd = -1,

ret= -1;

char buf[256] = {0};

printf("start ...\n");

fd = open(UART_DEVICE, O_RDWR);

if(fd < 0){

perror("open failed");

exit(1);

}

set_speed(fd, 115200);

if(!set_parity(fd, 8, 1, 'N')){

perror("set parity");

exit(1);

}

for(;;){

ret = read(fd, buf, 256);

if(0 == res)

continue;

buf[res] = 0;

printf("the context is: %s", buf);

if(0x0d == buf[0])

printf("\n");

if('@' == buf[0])break;

}

if(close(fd) < 0){

perror("close failed");

exit(1);

}

return 0;

}

然后,我们用一个ch340连接UART3,然后另一端接在电脑上,用串口调试助手发送数据给UART3,然后会从UART0发数据显示到控制台,下面是效果图:

linux 串口驱动 4412,⑮tiny4412 Linux驱动开发之tty子系统(UART)驱动程序相关推荐

  1. ⑨tiny4412 Linux驱动开发之1-wire子系统(DS18B20)驱动程序

    本来这次想做LCD背光灯的调节的,但是没有调通,时间很紧迫,就转向了其它东西,昨天调了一下DHT11,今天又调了一下DS18B20,还算有个安慰,本来是想用1-wire子系统做的,但是时间上有点紧,要 ...

  2. ⑭tiny4412 Linux驱动开发之cpufreq子系统驱动程序

    本次我们来说一下CPU动态调频子系统. 首先来看一下三星Exynos 4412的datasheet,如下: 上图就是Exynos 4412的时钟分布图,可以看到CPU的频率可以在1.4GHz~200M ...

  3. ㉓AW-H3 Linux驱动开发之mipi camera(CSI)驱动程序

    本次说一下mipi camera的驱动开发,平台用的是全志的H3芯片,项目代号:sun8iw7p1,这次使用运行在H3上面的Ubuntu进行验证的. Linux代码:https://github.co ...

  4. ㉕AW-A33 Linux驱动开发之audio子系统驱动程序

    在Linux源码里,Aduio这一部分现在是一个独立文件夹叫sound,在2.x的版本时,sound这个目录是在drivers里的,后来从这个里面剥离出来了,很多人不知道其中的原因,我也不知道,我们先 ...

  5. linux 串口工具_Zynq下linux系统搭建

    引言 Zynq器件将arm和FPGA结合,利用了两者各自的优势,arm可以实现灵活的控制,而FPGA部分可以实现算法加速,这大大扩展了zynq的应用.比如深度学习加速,图像处理等等.PL侧表示FPGA ...

  6. linux 串口转网口工具,linux下串口调试工具/串口终端推荐: picocom(转)

    Servlet 3.0 异步模式 Servlet 3.0标准新增了异步处理的支持. 进行异步处理的Servlet和作用于该Servlet的拦截器都必须声明对于异步处理的支持. java 资源监控 ht ...

  7. linux按键检测程序,Tiny4412 Linux驱动之按键(使用查询方式) | 技术部落

    前几天在TIny4412开发板上做了LED点灯的Linux驱动,其实挺简单,GPIO驱动,今天再看一下按键的驱动,毕竟按键用的还是比较广泛的,本文使用查询的方式获取按键值,后面会有文章使用中断的方式进 ...

  8. linux编译input驱动,Linux驱动开发之input子系统

    本文对mousedev.Amimouse和input子系统进行分析,旨在提纲挈领,给出它们之间的调用关系(或者说关联).阅读本文,需要与阅读Linux 2.6内核源码交叉进行,除非你是超人. 背景: ...

  9. linux串口发送数据程序,linux串口驱动分析——发送数据

    一.应用程序中write函数到底层驱动历程 和前文提到的一样,首先先注册串口,使用uart_register_driver函数,依次分别为tty_register_driver,cdev_init函数 ...

最新文章

  1. LeetCode中等题之煎饼排序
  2. FFT与多项式、生成函数题目泛做
  3. 标准模板库之容器-《C++标准库(第二版)》读书笔记
  4. 服务体系总出bug,咸鱼社交挤压,转转的综合性二手电商还好做吗?
  5. DM8168学习--USB的over-current 问题总结
  6. linux日常管理3
  7. 开大你的音响,感受HTML5 Audio API带来的视听盛宴
  8. mysql课程表学时_Mysql 巩固提升 (学生表_课程表_成绩表_教师表)
  9. views是什么意思_views是什么意思_views怎么读_views翻译_用法_发音_词组_同反义词_看( view的名词复数 )-新东方在线英语词典...
  10. 1120 Friend Numbers
  11. Intelli IDEA+jdk++maven+tomcat环境配置
  12. MATLAB中的柱面与球面
  13. VM和CentOS的安装
  14. 水经注下载地图,导出arcgis瓦片,然后通过geoserver+geowebcache发布地图
  15. 添加打印机无法搜索计算机,电脑无法搜索添加局域网打印机怎么办
  16. 产品经理技术脑:怎么看懂接口文档
  17. 沈华伟老师图卷积神经网络教学视频笔记
  18. java中this关键变量
  19. 虚拟现实技术实现理论之梦境论述
  20. 2014迅雷校园招聘(C++)(笔试题(四)

热门文章

  1. mysql 短整型_C++ int,short,long(详解版)
  2. googletest在C语言项目中的使用指南
  3. UHF读写器jna调用 UHFReader x86
  4. 微信小程序 短剧开发技术踩坑指南 仿抖音快手小视频
  5. 华为运营商模式渐现困局:开始围剿企业级用户 (转载)
  6. 启动weblogic时报错: java.lang.NoClassDefFoundError: weblogic/Server
  7. 北大青鸟 一期enbm总结
  8. MMDetection2系列-(3)-部署
  9. getTime 方法
  10. 百问网物联网实战-串口设计