From: hhb Date: Mon, 27 Jun 2011 09:40:18 +0000 (+0800) Subject: rk29:UART->add new uart driver rk_serial.c which can be configurated in menuconfig... X-Git-Tag: firefly_0821_release~10164^2~11 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=ee1cd711356b310f047bd18a2d26fb9a8987430f;p=firefly-linux-kernel-4.4.55.git rk29:UART->add new uart driver rk_serial.c which can be configurated in menuconfig,but the old driver is default used --- diff --git a/arch/arm/mach-rk29/board-rk29-phonesdk.c b/arch/arm/mach-rk29/board-rk29-phonesdk.c index 7e56806ad05f..3c7cd3d66c39 100644 --- a/arch/arm/mach-rk29/board-rk29-phonesdk.c +++ b/arch/arm/mach-rk29/board-rk29-phonesdk.c @@ -2525,16 +2525,16 @@ static void __init rk29_board_iomux_init(void) } static struct platform_device *devices[] __initdata = { -#ifdef CONFIG_UART1_RK29 +#if defined(CONFIG_UART1_RK29) || defined(CONFIG_UART1_RK29_STANDAR) &rk29_device_uart1, #endif -#ifdef CONFIG_UART0_RK29 +#if defined(CONFIG_UART0_RK29) || defined(CONFIG_UART0_RK29_STANDAR) &rk29_device_uart0, #endif -#ifdef CONFIG_UART2_RK29 +#if defined(CONFIG_UART2_RK29) || defined(CONFIG_UART2_RK29_STANDAR) &rk29_device_uart2, #endif -#ifdef CONFIG_UART3_RK29 +#if defined(CONFIG_UART3_RK29) || defined(CONFIG_UART3_RK29_STANDAR) &rk29_device_uart3, #endif diff --git a/arch/arm/mach-rk29/devices.c b/arch/arm/mach-rk29/devices.c old mode 100755 new mode 100644 index 365faf78660e..1a79563c7b02 --- a/arch/arm/mach-rk29/devices.c +++ b/arch/arm/mach-rk29/devices.c @@ -286,7 +286,7 @@ struct platform_device rk29_device_sdmmc1 = { /* * rk29 4 uarts device */ -#ifdef CONFIG_UART0_RK29 +#if defined(CONFIG_UART0_RK29) || defined(CONFIG_UART0_RK29_STANDAR) static struct resource resources_uart0[] = { { .start = IRQ_UART0, @@ -300,7 +300,7 @@ static struct resource resources_uart0[] = { }, }; #endif -#ifdef CONFIG_UART1_RK29 +#if defined(CONFIG_UART1_RK29) || defined(CONFIG_UART1_RK29_STANDAR) static struct resource resources_uart1[] = { { .start = IRQ_UART1, @@ -314,7 +314,7 @@ static struct resource resources_uart1[] = { }, }; #endif -#ifdef CONFIG_UART2_RK29 +#if defined(CONFIG_UART2_RK29) || defined(CONFIG_UART2_RK29_STANDAR) static struct resource resources_uart2[] = { { .start = IRQ_UART2, @@ -328,7 +328,7 @@ static struct resource resources_uart2[] = { }, }; #endif -#ifdef CONFIG_UART3_RK29 +#if defined(CONFIG_UART3_RK29) || defined(CONFIG_UART3_RK29_STANDAR) static struct resource resources_uart3[] = { { .start = IRQ_UART3, @@ -342,7 +342,7 @@ static struct resource resources_uart3[] = { }, }; #endif -#ifdef CONFIG_UART0_RK29 +#if defined(CONFIG_UART0_RK29) || defined(CONFIG_UART0_RK29_STANDAR) struct platform_device rk29_device_uart0 = { .name = "rk29_serial", .id = 0, @@ -350,7 +350,7 @@ struct platform_device rk29_device_uart0 = { .resource = resources_uart0, }; #endif -#ifdef CONFIG_UART1_RK29 +#if defined(CONFIG_UART1_RK29) || defined(CONFIG_UART1_RK29_STANDAR) struct platform_device rk29_device_uart1 = { .name = "rk29_serial", .id = 1, @@ -358,7 +358,7 @@ struct platform_device rk29_device_uart1 = { .resource = resources_uart1, }; #endif -#ifdef CONFIG_UART2_RK29 +#if defined(CONFIG_UART2_RK29) || defined(CONFIG_UART2_RK29_STANDAR) struct platform_device rk29_device_uart2 = { .name = "rk29_serial", .id = 2, @@ -366,7 +366,7 @@ struct platform_device rk29_device_uart2 = { .resource = resources_uart2, }; #endif -#ifdef CONFIG_UART3_RK29 +#if defined(CONFIG_UART3_RK29) || defined(CONFIG_UART3_RK29_STANDAR) struct platform_device rk29_device_uart3 = { .name = "rk29_serial", .id = 3, diff --git a/arch/arm/mach-rk29/iomux.c b/arch/arm/mach-rk29/iomux.c old mode 100755 new mode 100644 index b032ff829440..45623ee4f249 --- a/arch/arm/mach-rk29/iomux.c +++ b/arch/arm/mach-rk29/iomux.c @@ -247,30 +247,30 @@ int rk29_iomux_init(void) rk29_mux_set(&rk29_muxs[i]); } -#ifdef CONFIG_UART0_RK29 +#if defined(CONFIG_UART0_RK29) || defined(CONFIG_UART0_RK29_STANDAR) rk29_mux_api_set(GPIO1B7_UART0SOUT_NAME, GPIO1L_UART0_SOUT); rk29_mux_api_set(GPIO1B6_UART0SIN_NAME, GPIO1L_UART0_SIN); -#ifdef CONFIG_UART0_CTS_RTS_RK29 +#if defined(CONFIG_UART0_CTS_RTS_RK29) || defined(CONFIG_UART0_CTS_RTS_RK29_STANDAR) rk29_mux_api_set(GPIO1C1_UART0RTSN_SDMMC1WRITEPRT_NAME, GPIO1H_UART0_RTS_N); rk29_mux_api_set(GPIO1C0_UART0CTSN_SDMMC1DETECTN_NAME, GPIO1H_UART0_CTS_N); #endif #endif -#ifdef CONFIG_UART1_RK29 +#if defined(CONFIG_UART1_RK29) || defined(CONFIG_UART1_RK29_STANDAR) rk29_mux_api_set(GPIO2A5_UART1SOUT_NAME, GPIO2L_UART1_SOUT); rk29_mux_api_set(GPIO2A4_UART1SIN_NAME, GPIO2L_UART1_SIN); #endif -#ifdef CONFIG_UART2_RK29 +#if defined(CONFIG_UART2_RK29) || defined(CONFIG_UART2_RK29_STANDAR) rk29_mux_api_set(GPIO2B1_UART2SOUT_NAME, GPIO2L_UART2_SOUT); rk29_mux_api_set(GPIO2B0_UART2SIN_NAME, GPIO2L_UART2_SIN); -#ifdef CONFIG_UART2_CTS_RTS_RK29 +#if defined(CONFIG_UART2_CTS_RTS_RK29) || defined(CONFIG_UART2_CTS_RTS_RK29_STANDAR) rk29_mux_api_set(GPIO2A7_UART2RTSN_NAME, GPIO2L_UART2_RTS_N); rk29_mux_api_set(GPIO2A6_UART2CTSN_NAME, GPIO2L_UART2_CTS_N); #endif #endif -#ifdef CONFIG_UART3_RK29 +#if defined(CONFIG_UART3_RK29) || defined(CONFIG_UART3_RK29_STANDAR) rk29_mux_api_set(GPIO2B3_UART3SOUT_NAME, GPIO2L_UART3_SOUT); rk29_mux_api_set(GPIO2B2_UART3SIN_NAME, GPIO2L_UART3_SIN); -#ifdef CONFIG_UART3_CTS_RTS_RK29 +#if defined(CONFIG_UART3_CTS_RTS_RK29) || defined(CONFIG_UART3_CTS_RTS_RK29_STANDAR) rk29_mux_api_set(GPIO2B5_UART3RTSN_I2C3SCL_NAME, GPIO2L_UART3_RTS_N); rk29_mux_api_set(GPIO2B4_UART3CTSN_I2C3SDA_NAME, GPIO2L_UART3_CTS_N); #endif diff --git a/drivers/input/touchscreen/gt818_ts.c b/drivers/input/touchscreen/gt818_ts.c index 1b2b943cfd58..95ee6c976196 100644 --- a/drivers/input/touchscreen/gt818_ts.c +++ b/drivers/input/touchscreen/gt818_ts.c @@ -95,7 +95,7 @@ static int i2c_read_bytes(struct i2c_client *client, u8 *buf, int len) msgs[1].len = len-2; msgs[1].buf = &buf[2]; msgs[1].scl_rate = GT818_I2C_SCL; - //msgs[1].udelay = client->udelay; + msgs[1].udelay = client->udelay; ret = i2c_transfer(client->adapter, msgs, 2); if(ret < 0) @@ -114,7 +114,7 @@ static int i2c_write_bytes(struct i2c_client *client,u8 *data,int len) msg.len = len; msg.buf = data; msg.scl_rate = GT818_I2C_SCL; - //msg.udelay = client->udelay; + msg.udelay = client->udelay; ret = i2c_transfer(client->adapter, &msg, 1); if(ret < 0) printk("%s:i2c_transfer fail =%d\n",__func__, ret); @@ -150,7 +150,6 @@ static int i2c_end_cmd(struct gt818_ts_data *ts) static int goodix_init_panel(struct gt818_ts_data *ts) { int ret = -1; -// unsigned char i2c_control_buf[3] = {0x06,0x92,0x03}; #if 1 u8 config_info[] = { @@ -192,7 +191,6 @@ static int goodix_init_panel(struct gt818_ts_data *ts) }; #endif -// ret = i2c_write_bytes(ts->client, i2c_control_buf, (sizeof(i2c_control_buf)/sizeof(i2c_control_buf[0]))); ret = i2c_write_bytes(ts->client, config_info, (sizeof(config_info)/sizeof(config_info[0]))); if (ret < 0) return ret; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 0b8d90a75cfe..6f3101db77ba 100755 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1511,7 +1511,6 @@ config SERIAL_RK29 config UART0_RK29 bool "RockChip rk29 serial port 0 support" depends on SERIAL_RK29 - config UART0_CTS_RTS_RK29 bool "RockChip rk29 serial port 0 cts rts support" depends on UART0_RK29 @@ -1523,24 +1522,68 @@ config UART1_RK29 config UART2_RK29 bool "RockChip rk29 serial port 2 support" depends on SERIAL_RK29 - config UART2_CTS_RTS_RK29 bool "RockChip rk29 serial port 2 cts rts support" - depends on UART2_RK29 - + depends on UART2_RK29 + config UART3_RK29 bool "RockChip rk29 serial port 3 support" depends on SERIAL_RK29 - config UART3_CTS_RTS_RK29 bool "RockChip rk29 serial port 3 cts rts support" depends on UART3_RK29 - + config SERIAL_RK29_CONSOLE bool "Rockchip rk29 serial console support" depends on SERIAL_RK29=y select SERIAL_CORE_CONSOLE + + +config SERIAL_RK29_STANDAR + bool "RockChip rk29 serial port support standar" + depends on ARM && ARCH_RK29 + select SERIAL_CORE + +config UART0_RK29_STANDAR + bool "RockChip rk29 serial port 0 support standar" + depends on SERIAL_RK29_STANDAR + +config UART0_CTS_RTS_RK29_STANDAR + bool "RockChip rk29 serial port 0 cts rts support standar" + depends on UART0_RK29_STANDAR +config UART0_DMA_RK29 + bool "RockChip rk29 serial port 0 DMA support standar" + depends on UART0_RK29_STANDAR + +config UART1_RK29_STANDAR + bool "RockChip rk29 serial port 1 support standar" + depends on SERIAL_RK29_STANDAR +config UART2_RK29_STANDAR + bool "RockChip rk29 serial port 2 support standar" + depends on SERIAL_RK29_STANDAR +config UART2_CTS_RTS_RK29_STANDAR + bool "RockChip rk29 serial port 2 cts rts support standar" + depends on UART2_RK29_STANDAR +config UART2_DMA_RK29 + bool "RockChip rk29 serial port 2 DMA support standar" + depends on UART2_RK29_STANDAR + +config UART3_RK29_STANDAR + bool "RockChip rk29 serial port 3 support standar" + depends on SERIAL_RK29_STANDAR +config UART3_CTS_RTS_RK29_STANDAR + bool "RockChip rk29 serial port 3 cts rts support standar" + depends on UART3_RK29_STANDAR +config UART3_DMA_RK29 + bool "RockChip rk29 serial port 3 DMA support standar" + depends on UART3_RK29_STANDAR + +config SERIAL_RK29_CONSOLE_STANDAR + bool "Rockchip rk29 serial console support standar" + depends on SERIAL_RK29_STANDAR=y + select SERIAL_CORE_CONSOLE + config SERIAL_SC8800 tristate "SC8800 support" depends on SPI diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 0b91d5dfd723..f76f17844d59 100755 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -82,5 +82,6 @@ obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_RK2818) += rk2818_serial.o obj-$(CONFIG_SERIAL_RK29) += rk29_serial.o +obj-$(CONFIG_SERIAL_RK29_STANDAR) += rk_serial.o obj-$(CONFIG_SERIAL_SC8800) += sc8800.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o diff --git a/drivers/serial/rk_serial.c b/drivers/serial/rk_serial.c new file mode 100644 index 000000000000..02282418cac4 --- /dev/null +++ b/drivers/serial/rk_serial.c @@ -0,0 +1,1829 @@ +/* + * Driver for RK-UART controller. + * Based on drivers/tty/serial/8250.c + * + * Copyright (C) 2011 Rochchip. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + * + * Author: hhb@rock-chips.com + * Date: 2011.06.18 + */ + +#ifndef CONFIG_SERIAL_RK_CONSOLE +#if defined(CONFIG_SERIAL_RK29_CONSOLE_STANDAR) || defined(CONFIG_SERIAL_RK29_CONSOLE) +#define CONFIG_SERIAL_RK_CONSOLE +#endif +#endif + +#if defined(CONFIG_SERIAL_RK_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define PORT_RK 90 +#define UART_USR 0x1F /* UART Status Register */ +#define UART_IER_PTIME 0x80 /* Programmable THRE Interrupt Mode Enable */ +#define RX_TIMEOUT (3000*10) //uint ms + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +#define UART_NR 4 //uart port number + +/* configurate whether the port transmit-receive by DMA */ +#define OPEN_DMA 1 +#define CLOSE_DMA 0 + +#ifdef CONFIG_UART0_DMA_RK29 +#define UART0_USE_DMA OPEN_DMA +#else +#define UART0_USE_DMA CLOSE_DMA +#endif + +#ifdef CONFIG_UART2_DMA_RK29 +#define UART2_USE_DMA OPEN_DMA +#else +#define UART2_USE_DMA CLOSE_DMA +#endif + +#ifdef CONFIG_UART3_DMA_RK29 +#define UART3_USE_DMA OPEN_DMA +#else +#define UART3_USE_DMA CLOSE_DMA +#endif + +#define UART1_USE_DMA CLOSE_DMA + +#define DMA_TX_TRRIGE_LEVEL 30 + +#define USE_TIMER 1 // use timer for dma transport +#define THRE_MODE 0X00 //0yhh + +static struct uart_driver serial_rk_reg; + +/* + * Debugging. + */ +#define DBG_PORT 1 +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +#ifdef DEBUG +extern void printascii(const char *); + +static void dbg(const char *fmt, ...) +{ + va_list va; + char buff[256]; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + printascii(buff); +} + +#define DEBUG_INTR(fmt...) if (!uart_console(&up->port)) dbg(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + + +/* added by hhb@rock-chips.com for uart dma transfer */ + +struct rk29_uart_dma_t { + u32 use_dma; //1:used + u32 rx_dma_start; + enum dma_ch rx_dmach; + enum dma_ch tx_dmach; + u32 tx_dma_inited; + u32 rx_dma_inited; + spinlock_t tx_lock; + spinlock_t rx_lock; + char * rx_buffer; + char * tx_buffer; + dma_addr_t rx_phy_addr; + dma_addr_t tx_phy_addr; + u32 rx_buffer_size; + u32 tx_buffer_size; + + u32 rb_cur_pos; + u32 rb_pre_pos; + u32 rx_size; + char use_timer; + char tx_dma_used; + /* timer to poll activity on rx dma */ + struct timer_list rx_timer; + int rx_timeout; + +}; + +struct uart_rk_port { + struct uart_port port; + struct platform_device *pdev; + struct clk *clk; + unsigned int tx_loadsz; /* transmit fifo load size */ + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned char iir; + + /* + * Some bits in registers are cleared on a read, so they must + * be saved whenever the register is read but the bits will not + * be immediately processed. + */ +#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS + unsigned char lsr_saved_flags; +#if 0 +#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA + unsigned char msr_saved_flags; +#endif + + char name[12]; + char fifo[32]; + char fifo_size; + unsigned long port_activity; + struct work_struct uart_work; + struct work_struct uart_work_rx; + struct workqueue_struct *uart_wq; + struct rk29_uart_dma_t *prk29_uart_dma_t; +}; + +static void serial_rk_release_dma_tx(struct uart_port *port); +static int serial_rk_start_tx_dma(struct uart_port *port); +static void serial_rk_rx_timeout(unsigned long uart); +static void serial_rk_release_dma_rx(struct uart_port *port); +static int serial_rk_start_rx_dma(struct uart_port *port); +static int serial_rk_startup(struct uart_port *port); +static inline unsigned int serial_in(struct uart_rk_port *up, int offset) +{ + offset = offset << 2; + return readb(up->port.membase + offset); +} + +/* Save the LCR value so it can be re-written when a Busy Detect IRQ occurs. */ +static inline void dwapb_save_out_value(struct uart_rk_port *up, int offset, + unsigned char value) +{ + if (offset == UART_LCR) + up->lcr = value; +} + +/* Read the IER to ensure any interrupt is cleared before returning from ISR. */ +static inline void dwapb_check_clear_ier(struct uart_rk_port *up, int offset) +{ + if (offset == UART_TX || offset == UART_IER) + serial_in(up, UART_IER); +} + +static inline void serial_out(struct uart_rk_port *up, int offset, unsigned char value) +{ + dwapb_save_out_value(up, offset, value); + writeb(value, up->port.membase + (offset << 2)); + dwapb_check_clear_ier(up, offset); +} + +/* Uart divisor latch read */ +static inline int serial_dl_read(struct uart_rk_port *up) +{ + return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8; +} + +/* Uart divisor latch write */ +static inline void serial_dl_write(struct uart_rk_port *up, unsigned int value) +{ + serial_out(up, UART_DLL, value & 0xff); + serial_out(up, UART_DLM, value >> 8 & 0xff); +} + +static void serial_lcr_write(struct uart_rk_port *up, unsigned char value) +{ + unsigned int tmout = 10000; + + for (;;) { + unsigned char lcr; + serial_out(up, UART_LCR, value); + lcr = serial_in(up, UART_LCR); + if (lcr == value) + break; + /* Read the USR to clear any busy interrupts */ + serial_in(up, UART_USR); + serial_in(up, UART_RX); + if (--tmout == 0) + break; + udelay(1); + } +} + +static inline void serial_rk_enable_ier_thri(struct uart_rk_port *up) +{ + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + + +static inline void serial_rk_disable_ier_thri(struct uart_rk_port *up) +{ + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +/* + * FIFO support. + */ +static void serial_rk_clear_fifos(struct uart_rk_port *up) +{ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +static inline void __stop_tx(struct uart_rk_port *p) +{ + if (p->ier & UART_IER_THRI) { + p->ier &= ~UART_IER_THRI; + serial_out(p, UART_IER, p->ier); + } +} + +static void serial_rk_stop_tx(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + + if(OPEN_DMA == prk29_uart_dma_t->use_dma){ + serial_rk_release_dma_tx(port); + } + __stop_tx(up); +} + + +static void serial_rk_start_tx(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); +/* + * struct circ_buf *xmit = &port->state->xmit; + int size = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); + if(size > 64){ + serial_rk_start_tx_dma(port); + } + else{ + serial_rk_enable_ier_thri(up); + } +*/ + + if(0 == serial_rk_start_tx_dma(port)){ + serial_rk_enable_ier_thri(up); + } + +} + + +static void serial_rk_stop_rx(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + + if(OPEN_DMA == prk29_uart_dma_t->use_dma){ + serial_rk_release_dma_rx(port); + } + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + + +static void serial_rk_enable_ms(struct uart_port *port) +{ + /* no MSR capabilities */ +#if 0 + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + + dev_dbg(port->dev, "%s\n", __func__); + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +#endif +} + + +/* + * Start transmitting by dma. + */ +#define DMA_SERIAL_BUFFER_SIZE UART_XMIT_SIZE + +/* added by hhb@rock-chips.com for uart dma transfer*/ +static struct rk29_uart_dma_t rk29_uart_ports_dma_t[] = { + {UART0_USE_DMA, 0, DMACH_UART0_RX, DMACH_UART0_TX}, + {UART1_USE_DMA, 0, DMACH_UART1_RX, DMACH_UART1_TX}, + {UART2_USE_DMA, 0, DMACH_UART2_RX, DMACH_UART2_TX}, + {UART3_USE_DMA, 0, DMACH_UART3_RX, DMACH_UART3_TX}, +}; + + +/* DMAC PL330 add by hhb@rock-chips.com */ +static struct rk29_dma_client rk29_uart_dma_client = { + .name = "rk29xx-uart-dma", +}; + +/*TX*/ + +static void serial_rk_release_dma_tx(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + if(!port){ + return; + } + if(prk29_uart_dma_t && prk29_uart_dma_t->tx_dma_inited) { + rk29_dma_free(prk29_uart_dma_t->tx_dmach, &rk29_uart_dma_client); + prk29_uart_dma_t->tx_dma_inited = 0; + } +} + +/*this function will be called every time after rk29_dma_enqueue() be invoked*/ +static void serial_rk_dma_txcb(void *buf, int size, enum rk29_dma_buffresult result) { + struct uart_port *port = buf; + struct uart_rk_port *up = container_of(port, struct uart_rk_port, port); + struct circ_buf *xmit = &port->state->xmit; + + if(result != RK29_RES_OK){ + return; + } + + port->icount.tx += size; + xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + spin_lock(&(up->prk29_uart_dma_t->tx_lock)); + up->prk29_uart_dma_t->tx_dma_used = 0; + spin_unlock(&(up->prk29_uart_dma_t->tx_lock)); + if (!uart_circ_empty(xmit)) { + serial_rk_start_tx_dma(port); + } + + up->port_activity = jiffies; +// dev_info(up->port.dev, "s:%d\n", size); +} + +static int serial_rk_init_dma_tx(struct uart_port *port) { + + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + if(!port || !prk29_uart_dma_t){ + dev_info(up->port.dev, "serial_rk_init_dma_tx fail\n"); + return -1; + } + + if(prk29_uart_dma_t->tx_dma_inited) { + return 0; + } + + if (rk29_dma_request(prk29_uart_dma_t->tx_dmach, &rk29_uart_dma_client, NULL) == -EBUSY) { + dev_info(up->port.dev, "rk29_dma_request tx fail\n"); + return -1; + } + + if (rk29_dma_set_buffdone_fn(prk29_uart_dma_t->tx_dmach, serial_rk_dma_txcb)) { + dev_info(up->port.dev, "rk29_dma_set_buffdone_fn tx fail\n"); + return -1; + } + if (rk29_dma_devconfig(prk29_uart_dma_t->tx_dmach, RK29_DMASRC_MEM, (unsigned long)(port->iobase + UART_TX))) { + dev_info(up->port.dev, "rk29_dma_devconfig tx fail\n"); + return -1; + } + if (rk29_dma_config(prk29_uart_dma_t->tx_dmach, 1, 1)) { + dev_info(up->port.dev, "rk29_dma_config tx fail\n"); + return -1; + } + + prk29_uart_dma_t->tx_dma_inited = 1; + dev_info(up->port.dev, "serial_rk_init_dma_tx sucess\n"); + return 0; + +} + +static int serial_rk_start_tx_dma(struct uart_port *port) +{ + + struct circ_buf *xmit = &port->state->xmit; + struct uart_rk_port *up = container_of(port, struct uart_rk_port, port); + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + + if(0 == prk29_uart_dma_t->use_dma){ + return CLOSE_DMA; + } + + if(-1 == serial_rk_init_dma_tx(port)){ + goto err_out; + } + + if (1 == prk29_uart_dma_t->tx_dma_used){ + return 1; + } + if(!uart_circ_empty(xmit)){ + if (rk29_dma_enqueue(prk29_uart_dma_t->tx_dmach, port, + prk29_uart_dma_t->tx_phy_addr + xmit->tail, + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE))) { + goto err_out; + } + } + rk29_dma_ctrl(prk29_uart_dma_t->tx_dmach, RK29_DMAOP_START); + spin_lock(&(prk29_uart_dma_t->tx_lock)); + up->prk29_uart_dma_t->tx_dma_used = 1; + spin_unlock(&(prk29_uart_dma_t->tx_lock)); + + return 1; +err_out: + dev_info(up->port.dev, "-serial_rk_start_tx_dma-error-\n"); + return -1; + +} + + + +/*RX*/ +static void serial_rk_dma_rxcb(void *buf, int size, enum rk29_dma_buffresult result) { + +// printk("^\n"); + +} + +static void serial_rk_release_dma_rx(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + if(!port){ + return; + } + if(prk29_uart_dma_t && prk29_uart_dma_t->rx_dma_inited) { + del_timer(&prk29_uart_dma_t->rx_timer); + rk29_dma_free(prk29_uart_dma_t->rx_dmach, &rk29_uart_dma_client); + prk29_uart_dma_t->rb_pre_pos = 0; + prk29_uart_dma_t->rx_dma_inited = 0; + prk29_uart_dma_t->rx_dma_start = 0; + } +} + + +static int serial_rk_init_dma_rx(struct uart_port *port) { + + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + if(!port || !prk29_uart_dma_t){ + dev_info(up->port.dev, "serial_rk_init_dma_rx: port fail\n"); + return -1; + } + if(prk29_uart_dma_t->rx_dma_inited) { + return 0; + } + + if (rk29_dma_request(prk29_uart_dma_t->rx_dmach, &rk29_uart_dma_client, NULL) == -EBUSY) { + dev_info(up->port.dev, "rk29_dma_request fail rx \n"); + return -1; + } + + if (rk29_dma_set_buffdone_fn(prk29_uart_dma_t->rx_dmach, serial_rk_dma_rxcb)) { + dev_info(up->port.dev, "rk29_dma_set_buffdone_fn rx fail\n"); + return -1; + } + if (rk29_dma_devconfig(prk29_uart_dma_t->rx_dmach, RK29_DMASRC_HW, (unsigned long)(port->iobase + UART_RX))) { + dev_info(up->port.dev, "rk29_dma_devconfig rx fail\n"); + return -1; + } + + if (rk29_dma_config(prk29_uart_dma_t->rx_dmach, 1, 1)) { + dev_info(up->port.dev, "rk29_dma_config rx fail\n"); + return -1; + } + + rk29_dma_setflags(prk29_uart_dma_t->rx_dmach, RK29_DMAF_CIRCULAR); + + prk29_uart_dma_t->rx_dma_inited = 1; + dev_info(up->port.dev, "serial_rk_init_dma_rx sucess\n"); + return 0; + +} + +static int serial_rk_start_rx_dma(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + if(0 == prk29_uart_dma_t->use_dma){ + return 0; + } + + if(prk29_uart_dma_t->rx_dma_start == 1){ + return 0; + } + + if(-1 == serial_rk_init_dma_rx(port)){ + dev_info(up->port.dev, "*******serial_rk_init_dma_rx*******error*******\n"); + return -1; + } + + if (rk29_dma_enqueue(prk29_uart_dma_t->rx_dmach, (void *)up, prk29_uart_dma_t->rx_phy_addr, + prk29_uart_dma_t->rx_buffer_size/2)) { + dev_info(up->port.dev, "*******rk29_dma_enqueue fail*****\n"); + return -1; + } + + if (rk29_dma_enqueue(prk29_uart_dma_t->rx_dmach, (void *)up, + prk29_uart_dma_t->rx_phy_addr+prk29_uart_dma_t->rx_buffer_size/2, + prk29_uart_dma_t->rx_buffer_size/2)) { + dev_info(up->port.dev, "*******rk29_dma_enqueue fail*****\n"); + return -1; + } + + rk29_dma_ctrl(prk29_uart_dma_t->rx_dmach, RK29_DMAOP_START); + prk29_uart_dma_t->rx_dma_start = 1; + if(prk29_uart_dma_t->use_timer == 1){ + mod_timer(&prk29_uart_dma_t->rx_timer, jiffies + + msecs_to_jiffies(prk29_uart_dma_t->rx_timeout)); + } + up->port_activity = jiffies; + return 1; +} + +static void serial_rk_update_rb_addr(struct uart_rk_port *up){ + dma_addr_t current_pos = 0; + dma_addr_t rx_current_pos = 0; + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + spin_lock(&(up->prk29_uart_dma_t->rx_lock)); + rk29_dma_getposition(prk29_uart_dma_t->rx_dmach, ¤t_pos, &rx_current_pos); + + prk29_uart_dma_t->rb_cur_pos = (rx_current_pos - prk29_uart_dma_t->rx_phy_addr); + prk29_uart_dma_t->rx_size = CIRC_CNT(prk29_uart_dma_t->rb_cur_pos, + prk29_uart_dma_t->rb_pre_pos, prk29_uart_dma_t->rx_buffer_size); + + spin_unlock(&(up->prk29_uart_dma_t->rx_lock)); +} + +static void serial_rk_report_dma_rx(unsigned long uart) +{ + struct uart_rk_port *up = (struct uart_rk_port *)uart; + struct rk29_uart_dma_t *prk29_uart_dma_t = up->prk29_uart_dma_t; + if(prk29_uart_dma_t->use_timer == 1){ + serial_rk_update_rb_addr(up); + } + if(prk29_uart_dma_t->rx_size > 0) { + spin_lock(&(up->prk29_uart_dma_t->rx_lock)); + + if(prk29_uart_dma_t->rb_cur_pos > prk29_uart_dma_t->rb_pre_pos){ + tty_insert_flip_string(up->port.state->port.tty, prk29_uart_dma_t->rx_buffer + + prk29_uart_dma_t->rb_pre_pos, prk29_uart_dma_t->rx_size); + tty_flip_buffer_push(up->port.state->port.tty); + } + else if(prk29_uart_dma_t->rb_cur_pos < prk29_uart_dma_t->rb_pre_pos){ + + tty_insert_flip_string(up->port.state->port.tty, prk29_uart_dma_t->rx_buffer + + prk29_uart_dma_t->rb_pre_pos, CIRC_CNT_TO_END(prk29_uart_dma_t->rb_cur_pos, + prk29_uart_dma_t->rb_pre_pos, prk29_uart_dma_t->rx_buffer_size)); + tty_flip_buffer_push(up->port.state->port.tty); + + if(prk29_uart_dma_t->rb_cur_pos != 0){ + tty_insert_flip_string(up->port.state->port.tty, prk29_uart_dma_t->rx_buffer, + prk29_uart_dma_t->rb_cur_pos); + tty_flip_buffer_push(up->port.state->port.tty); + } + } + + prk29_uart_dma_t->rb_pre_pos = (prk29_uart_dma_t->rb_pre_pos + prk29_uart_dma_t->rx_size) + & (prk29_uart_dma_t->rx_buffer_size - 1); + up->port.icount.rx += prk29_uart_dma_t->rx_size; + spin_unlock(&(up->prk29_uart_dma_t->rx_lock)); + prk29_uart_dma_t->rx_timeout = 7; + up->port_activity = jiffies; + } + + +#if 1 + if (jiffies_to_msecs(jiffies - up->port_activity) < RX_TIMEOUT) { + if(prk29_uart_dma_t->use_timer == 1){ + mod_timer(&prk29_uart_dma_t->rx_timer, jiffies + msecs_to_jiffies(prk29_uart_dma_t->rx_timeout)); + } + } else { + +#if 1 + + + prk29_uart_dma_t->rx_timeout = 20; + mod_timer(&prk29_uart_dma_t->rx_timer, jiffies + msecs_to_jiffies(prk29_uart_dma_t->rx_timeout)); +#else +// serial_out(up, 0x2a, 0x01); + serial_rk_release_dma_rx(&up->port); + serial_out(up, 0x2a, 0x01); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); +// serial_out(up, 0x22, 0x01); + dev_info(up->port.dev, "*****enable recv int*****\n"); + + //serial_rk_start_rx_dma(&up->port); +#endif + } + + +#else + if(prk29_uart_dma_t->use_timer == 1){ + mod_timer(&prk29_uart_dma_t->rx_timer, jiffies + msecs_to_jiffies(prk29_uart_dma_t->rx_timeout)); + } +#endif + +} + +static void serial_rk_rx_timeout(unsigned long uart) +{ + struct uart_rk_port *up = (struct uart_rk_port *)uart; + + //serial_rk_report_dma_rx(up); + queue_work(up->uart_wq, &up->uart_work); +} + +static void serial_rk_report_revdata_workfunc(struct work_struct *work) +{ + struct uart_rk_port *up = + container_of(work, struct uart_rk_port, uart_work); + serial_rk_report_dma_rx((unsigned long)up); + spin_lock(&(up->prk29_uart_dma_t->rx_lock)); + + if(up->prk29_uart_dma_t->use_timer == 1){ + + }else{ + tty_insert_flip_string(up->port.state->port.tty, up->fifo, up->fifo_size); + tty_flip_buffer_push(up->port.state->port.tty); + up->port.icount.rx += up->fifo_size; + } + + spin_unlock(&(up->prk29_uart_dma_t->rx_lock)); + +} + + +static void serial_rk_start_dma_rx(struct work_struct *work) +{ + struct uart_rk_port *up = + container_of(work, struct uart_rk_port, uart_work_rx); + serial_rk_start_rx_dma(&up->port); +} + + + +static void +receive_chars(struct uart_rk_port *up, unsigned int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch, lsr = *status; + int max_count = 256; + char flag; + + do { + if (likely(lsr & UART_LSR_DR)) + ch = serial_in(up, UART_RX); + else + /* + * Intel 82571 has a Serial Over Lan device that will + * set UART_LSR_BI without setting UART_LSR_DR when + * it receives a break. To avoid reading from the + * receive buffer without UART_LSR_DR bit set, we + * just force the read character to be 0 + */ + ch = 0; + + flag = TTY_NORMAL; + up->port.icount.rx++; + + lsr |= up->lsr_saved_flags; + up->lsr_saved_flags = 0; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + /* + * For statistics only + */ + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (lsr & UART_LSR_PE) + up->port.icount.parity++; + else if (lsr & UART_LSR_FE) + up->port.icount.frame++; + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + + + /* + * Mask off conditions which should be ignored. + */ + lsr &= up->port.read_status_mask; + + if (lsr & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + flag = TTY_BREAK; + } else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); + +ignore_char: + lsr = serial_in(up, UART_LSR); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); + *status = lsr; +} + +static void transmit_chars(struct uart_rk_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_tx_stopped(&up->port)) { + __stop_tx(up); + return; + } + if (uart_circ_empty(xmit)) { + __stop_tx(up); + return; + } + + count = up->tx_loadsz; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + __stop_tx(up); +} + +static unsigned int check_modem_status(struct uart_rk_port *up) +{ + unsigned int status = serial_in(up, UART_MSR); + +#if 0 + status |= up->msr_saved_flags; + up->msr_saved_flags = 0; + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + up->port.state != NULL) { + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } +#endif + + return status; +} + + +/* + * This handles the interrupt from one port. + */ +static void serial_rk_handle_port(struct uart_rk_port *up) +{ + unsigned int status; + unsigned long flags; + spin_lock_irqsave(&up->port.lock, flags); + + status = serial_in(up, UART_LSR); + + DEBUG_INTR("status = %x...", status); + + if(up->prk29_uart_dma_t->use_dma == 1) { + + if(up->iir & UART_IIR_RLSI){ + + if (status & (UART_LSR_DR | UART_LSR_BI)) { + up->port_activity = jiffies; + up->ier &= ~UART_IER_RLSI; + up->ier &= ~UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + //receive_chars(up, &status); + //mod_timer(&up->prk29_uart_dma_t->rx_timer, jiffies + + //msecs_to_jiffies(up->prk29_uart_dma_t->rx_timeout)); + if(serial_rk_start_rx_dma(&up->port) == -1){ + receive_chars(up, &status); + } + } + } + +/* + if (status & UART_LSR_THRE) { + transmit_chars(up); + } +*/ + + }else { //dma mode disable + + if (status & (UART_LSR_DR | UART_LSR_BI)) { + receive_chars(up, &status); + } + check_modem_status(up); + if (status & UART_LSR_THRE) { + transmit_chars(up); + } + } + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* + * This is the serial driver's interrupt routine. + */ + +static irqreturn_t serial_rk_interrupt(int irq, void *dev_id) +{ + struct uart_rk_port *up = dev_id; + int handled = 0; + unsigned int iir; + + iir = serial_in(up, UART_IIR); + DEBUG_INTR("%s(%d) iir = 0x%02x ", __func__, irq, iir); + up->iir = iir; + + if (!(iir & UART_IIR_NO_INT)) { + serial_rk_handle_port(up); + handled = 1; + } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { + /* The DesignWare APB UART has an Busy Detect (0x07) + * interrupt meaning an LCR write attempt occured while the + * UART was busy. The interrupt must be cleared by reading + * the UART status register (USR) and the LCR re-written. */ + serial_in(up, UART_USR); + serial_out(up, UART_LCR, up->lcr); + + handled = 1; + DEBUG_INTR("busy "); + } + DEBUG_INTR("end(%d).\n", handled); + + return IRQ_RETVAL(handled); +} + +static unsigned int serial_rk_tx_empty(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + unsigned long flags; + unsigned int lsr; + + dev_dbg(port->dev, "%s\n", __func__); + spin_lock_irqsave(&up->port.lock, flags); + lsr = serial_in(up, UART_LSR); + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + spin_unlock_irqrestore(&up->port.lock, flags); + + return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; +} + +static unsigned int serial_rk_get_mctrl(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + unsigned int status; + unsigned int ret; + + status = check_modem_status(up); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + dev_dbg(port->dev, "%s 0x%08x\n", __func__, ret); + return ret; +} + +static void serial_rk_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + unsigned char mcr = 0; + + dev_dbg(port->dev, "+%s\n", __func__); + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + mcr |= up->mcr; + + serial_out(up, UART_MCR, mcr); + dev_dbg(port->dev, "-%s mcr: 0x%02x\n", __func__, mcr); +} + +static void serial_rk_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + unsigned long flags; + + dev_dbg(port->dev, "+%s\n", __func__); + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_lcr_write(up, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); + dev_dbg(port->dev, "-%s lcr: 0x%02x\n", __func__, up->lcr); +} + +/* + * Wait for transmitter & holding register to empty + */ +static void wait_for_xmitr(struct uart_rk_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + for (;;) { + status = serial_in(up, UART_LSR); + + up->lsr_saved_flags |= status & LSR_SAVE_FLAGS; + + if ((status & bits) == bits) + break; + if (--tmout == 0) + break; + udelay(1); + } +} + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial_rk_get_poll_char(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + unsigned char lsr = serial_in(up, UART_LSR); + + while (!(lsr & UART_LSR_DR)) + lsr = serial_in(up, UART_LSR); + + return serial_in(up, UART_RX); +} + +static void serial_rk_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + wait_for_xmitr(up, BOTH_EMPTY); + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(up, UART_TX, c); + if (c == 10) { + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_TX, 13); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_IER, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +static int serial_rk_startup(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + unsigned long flags; + int retval; + + + dev_dbg(port->dev, "%s\n", __func__); + + /* + * Allocate the IRQ + */ + retval = request_irq(up->port.irq, serial_rk_interrupt, up->port.irqflags, + up->name, up); + if (retval) + return retval; + + up->mcr = 0; + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_rk_clear_fifos(up); + + /* + * Clear the interrupt registers. + */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_lcr_write(up, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + up->port.mctrl |= TIOCM_OUT2; + + serial_rk_set_mctrl(&up->port, up->port.mctrl); + + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Clear the interrupt registers again for luck, and clear the + * saved flags to avoid getting false values from polling + * routines or the previous session. + */ + serial_in(up, UART_LSR); + serial_in(up, UART_RX); + serial_in(up, UART_IIR); + serial_in(up, UART_MSR); + up->lsr_saved_flags = 0; +#if 0 + up->msr_saved_flags = 0; +#endif + + + if (1 == up->prk29_uart_dma_t->use_dma) { + + if(up->port.state->xmit.buf != up->prk29_uart_dma_t->tx_buffer){ + free_page((unsigned long)up->port.state->xmit.buf); + up->port.state->xmit.buf = up->prk29_uart_dma_t->tx_buffer; + } + +#if 1 + serial_rk_start_rx_dma(&up->port); +#else + up->ier |= UART_IER_RDI; + up->ier |= UART_IER_RLSI; + serial_out(up, UART_IER, up->ier); +#endif + up->port_activity = jiffies; + + }else{ + up->ier |= UART_IER_RDI; + up->ier |= UART_IER_RLSI; + serial_out(up, UART_IER, up->ier); + } + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + + return 0; +} + + +static void serial_rk_shutdown(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + unsigned long flags; + + dev_dbg(port->dev, "%s\n", __func__); + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_out(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_rk_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_lcr_write(up, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_rk_clear_fifos(up); + + /* + * Read data port to reset things, and then free the irq + */ + (void) serial_in(up, UART_RX); + + free_irq(up->port.irq, up); +} + +static void +serial_rk_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + unsigned char cval, fcr = 0; + unsigned long flags, bits; + unsigned int baud, quot; + + dev_dbg(port->dev, "+%s\n", __func__); + + //start bit + bits += 1; + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + bits += 5; + break; + case CS6: + cval = UART_LCR_WLEN6; + bits += 6; + break; + case CS7: + cval = UART_LCR_WLEN7; + bits += 7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + bits += 8; + break; + } + + if (termios->c_cflag & CSTOPB){ + cval |= UART_LCR_STOP; + bits += 2; + } + else{ + bits += 1; + } + if (termios->c_cflag & PARENB){ + cval |= UART_LCR_PARITY; + bits += 1; + } + if (!(termios->c_cflag & PARODD)){ + cval |= UART_LCR_EPAR; + } +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + + quot = uart_get_divisor(port, baud); + + + DEBUG_INTR(up->port.dev, "*****baud:%d*******\n", baud); + DEBUG_INTR(up->port.dev, "*****quot:%d*******\n", quot); + + if (baud < 2400){ + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + } + else{ + //added by hhb@rock-chips.com + if(up->prk29_uart_dma_t->use_timer == 1){ + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_01; + //set time out value according to the baud rate +/* + up->prk29_uart_dma_t->rx_timeout = bits*1000*1024/baud + 1; + + if(up->prk29_uart_dma_t->rx_timeout < 10){ + up->prk29_uart_dma_t->rx_timeout = 10; + } + if(up->prk29_uart_dma_t->rx_timeout > 25){ + up->prk29_uart_dma_t->rx_timeout = 25; + } + printk("%s:time:%d, bits:%d, baud:%d\n", __func__, up->prk29_uart_dma_t->rx_timeout, bits, baud); + up->prk29_uart_dma_t->rx_timeout = 7; +*/ + } + else{ + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | UART_FCR_T_TRIG_01; + } + } + + /* + * MCR-based auto flow control. When AFE is enabled, RTS will be + * deasserted when the receive FIFO contains more characters than + * the trigger, or the MCR RTS bit is cleared. In the case where + * the remote UART is not using CTS auto flow control, we must + * have sufficient FIFO entries for the latency of the remote + * UART to respond. IOW, at least 32 bytes of FIFO. + */ + up->mcr &= ~UART_MCR_AFE; + if (termios->c_cflag & CRTSCTS){ + up->mcr |= UART_MCR_AFE; + //dev_info(up->port.dev, "*****UART_MCR_AFE*******\n"); + } + + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; +#if 0 + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; +#endif + + serial_out(up, UART_IER, up->ier); + + serial_lcr_write(up, cval | UART_LCR_DLAB);/* set DLAB */ + + serial_dl_write(up, quot); + + serial_lcr_write(up, cval); /* reset DLAB */ + up->lcr = cval; /* Save LCR */ + + serial_out(up, UART_FCR, fcr); /* set fcr */ +// fcr |= UART_FCR_DMA_SELECT; +// serial_out(up, UART_FCR, fcr); /* set fcr */ + serial_rk_set_mctrl(&up->port, up->port.mctrl); + + spin_unlock_irqrestore(&up->port.lock, flags); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + dev_dbg(port->dev, "-%s baud %d\n", __func__, baud); +} + +#if 0 +static void +serial_rk_set_ldisc(struct uart_port *port, int new) +{ + if (new == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + serial_rk_enable_ms(port); + } else + port->flags &= ~UPF_HARDPPS_CD; +} +#endif + +static void +serial_rk_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + + dev_dbg(port->dev, "%s: %s\n", __func__, state ? "disable" : "enable"); + if (state) + clk_disable(up->clk); + else + clk_enable(up->clk); +} + +static void serial_rk_release_port(struct uart_port *port) +{ + dev_dbg(port->dev, "%s\n", __func__); +} + +static int serial_rk_request_port(struct uart_port *port) +{ + dev_dbg(port->dev, "%s\n", __func__); + return 0; +} + +static void serial_rk_config_port(struct uart_port *port, int flags) +{ + dev_dbg(port->dev, "%s\n", __func__); + port->type = PORT_RK; +} + +static int +serial_rk_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + dev_dbg(port->dev, "%s\n", __func__); + return -EINVAL; +} + +static const char * +serial_rk_type(struct uart_port *port) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + + dev_dbg(port->dev, "%s: %s\n", __func__, up->name); + return up->name; +} + +static struct uart_ops serial_rk_pops = { + .tx_empty = serial_rk_tx_empty, + .set_mctrl = serial_rk_set_mctrl, + .get_mctrl = serial_rk_get_mctrl, + .stop_tx = serial_rk_stop_tx, + .start_tx = serial_rk_start_tx, + .stop_rx = serial_rk_stop_rx, + .enable_ms = serial_rk_enable_ms, + .break_ctl = serial_rk_break_ctl, + .startup = serial_rk_startup, + .shutdown = serial_rk_shutdown, + .set_termios = serial_rk_set_termios, +#if 0 + .set_ldisc = serial_rk_set_ldisc, +#endif + .pm = serial_rk_pm, + .type = serial_rk_type, + .release_port = serial_rk_release_port, + .request_port = serial_rk_request_port, + .config_port = serial_rk_config_port, + .verify_port = serial_rk_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial_rk_get_poll_char, + .poll_put_char = serial_rk_put_poll_char, +#endif +}; + +#ifdef CONFIG_SERIAL_RK_CONSOLE + +static struct uart_rk_port *serial_rk_console_ports[UART_NR]; + +static void serial_rk_console_putchar(struct uart_port *port, int ch) +{ + struct uart_rk_port *up = + container_of(port, struct uart_rk_port, port); + + wait_for_xmitr(up, UART_LSR_THRE); + serial_out(up, UART_TX, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +serial_rk_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_rk_port *up = serial_rk_console_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + touch_nmi_watchdog(); + + local_irq_save(flags); + if (up->port.sysrq) { + /* serial_rk_handle_port() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial_rk_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_IER, ier); + +#if 0 + /* + * The receive handling will happen properly because the + * receive ready bit will still be set; it is not cleared + * on read. However, modem control will not, we must + * call it if we have saved something in the saved flags + * while processing with interrupts off. + */ + if (up->msr_saved_flags) + check_modem_status(up); +#endif + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init serial_rk_console_setup(struct console *co, char *options) +{ + struct uart_rk_port *up; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (unlikely(co->index >= UART_NR || co->index < 0)) + return -ENODEV; + + if (serial_rk_console_ports[co->index] == NULL) + return -ENODEV; + up = serial_rk_console_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static struct console serial_rk_console = { + .name = "ttyS", + .write = serial_rk_console_write, + .device = uart_console_device, + .setup = serial_rk_console_setup, + .flags = CON_PRINTBUFFER | CON_ANYTIME, + .index = -1, + .data = &serial_rk_reg, +}; + +static void serial_rk_add_console_port(struct uart_rk_port *up) +{ + serial_rk_console_ports[up->pdev->id] = up; +} + +#define SERIAL_CONSOLE &serial_rk_console +#else +#define SERIAL_CONSOLE NULL + +static inline void serial_rk_add_console_port(struct uart_rk_port *up) +{} + +#endif + +static struct uart_driver serial_rk_reg = { + .owner = THIS_MODULE, + .driver_name = "rk29_serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .cons = SERIAL_CONSOLE, + .nr = UART_NR, +}; + +static int __devinit serial_rk_probe(struct platform_device *pdev) +{ + struct uart_rk_port *up; + struct resource *mem; + int irq; + int ret = -ENOSPC; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return irq; + } + + if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->dev.driver->name)) { + dev_err(&pdev->dev, "memory region already claimed\n"); + return -EBUSY; + } + + up = kzalloc(sizeof(*up), GFP_KERNEL); + if (up == NULL) { + ret = -ENOMEM; + goto do_release_region; + } + + sprintf(up->name, "rk29_serial.%d", pdev->id); + up->pdev = pdev; + up->clk = clk_get(&pdev->dev, "uart"); + if (unlikely(IS_ERR(up->clk))) { + ret = PTR_ERR(up->clk); + goto do_free; + } + up->tx_loadsz = 30; + up->prk29_uart_dma_t = &rk29_uart_ports_dma_t[pdev->id]; + up->port.dev = &pdev->dev; + up->port.type = PORT_RK; + up->port.irq = irq; + up->port.iotype = UPIO_DWAPB; + + up->port.regshift = 2; + up->port.fifosize = 32; + up->port.ops = &serial_rk_pops; + up->port.line = pdev->id; + up->port.iobase = mem->start; + up->port.membase = ioremap_nocache(mem->start, mem->end - mem->start + 1); + if (!up->port.membase) { + ret = -ENOMEM; + goto do_put_clk; + } + up->port.mapbase = mem->start; + up->port.irqflags = 0; + up->port.uartclk = clk_get_rate(up->clk); + + /* set dma config */ + if(1 == up->prk29_uart_dma_t->use_dma) { + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + + //timer + up->prk29_uart_dma_t->use_timer = USE_TIMER; + up->prk29_uart_dma_t->rx_timer.function = serial_rk_rx_timeout; + up->prk29_uart_dma_t->rx_timer.data = (unsigned long)up; + up->prk29_uart_dma_t->rx_timeout = 7; + up->prk29_uart_dma_t->rx_timer.expires = jiffies + msecs_to_jiffies(up->prk29_uart_dma_t->rx_timeout); + init_timer(&up->prk29_uart_dma_t->rx_timer); + //tx buffer + up->prk29_uart_dma_t->tx_buffer_size = UART_XMIT_SIZE; + up->prk29_uart_dma_t->tx_buffer = dmam_alloc_coherent(up->port.dev, up->prk29_uart_dma_t->tx_buffer_size, + &up->prk29_uart_dma_t->tx_phy_addr, DMA_MEMORY_MAP); + if(!up->prk29_uart_dma_t->tx_buffer){ + dev_info(up->port.dev, "dmam_alloc_coherent dma_tx_buffer fail\n"); + } + else{ + dev_info(up->port.dev, "dma_tx_buffer 0x%08x\n", (unsigned) up->prk29_uart_dma_t->tx_buffer); + dev_info(up->port.dev, "dma_tx_phy 0x%08x\n", (unsigned) up->prk29_uart_dma_t->tx_phy_addr); + } + //rx buffer + up->prk29_uart_dma_t->rx_buffer_size = UART_XMIT_SIZE*32; + up->prk29_uart_dma_t->rx_buffer = dmam_alloc_coherent(up->port.dev, up->prk29_uart_dma_t->rx_buffer_size, + &up->prk29_uart_dma_t->rx_phy_addr, DMA_MEMORY_MAP); + up->prk29_uart_dma_t->rb_pre_pos = 0; + if(!up->prk29_uart_dma_t->rx_buffer){ + dev_info(up->port.dev, "dmam_alloc_coherent dma_rx_buffer fail\n"); + } + else { + dev_info(up->port.dev, "dma_rx_buffer 0x%08x\n", (unsigned) up->prk29_uart_dma_t->rx_buffer); + dev_info(up->port.dev, "up 0x%08x\n", (unsigned)up->prk29_uart_dma_t); + } + + // work queue + INIT_WORK(&up->uart_work, serial_rk_report_revdata_workfunc); + INIT_WORK(&up->uart_work_rx, serial_rk_start_dma_rx); + up->uart_wq = create_singlethread_workqueue("uart_workqueue"); + up->prk29_uart_dma_t->rx_dma_start = 0; + spin_lock_init(&(up->prk29_uart_dma_t->tx_lock)); + spin_lock_init(&(up->prk29_uart_dma_t->rx_lock)); + serial_rk_init_dma_rx(&up->port); + serial_rk_init_dma_tx(&up->port); + up->ier |= THRE_MODE; // enable THRE interrupt mode + serial_out(up, UART_IER, up->ier); + clk_enable(up->clk); // enable the config uart clock + } + + + serial_rk_add_console_port(up); + ret = uart_add_one_port(&serial_rk_reg, &up->port); + if (ret != 0) + goto do_iounmap; + + platform_set_drvdata(pdev, up); + dev_info(&pdev->dev, "membase 0x%08x\n", (unsigned) up->port.membase); + + return 0; + +do_iounmap: + iounmap(up->port.membase); + up->port.membase = NULL; +do_put_clk: + clk_put(up->clk); +do_free: + kfree(up); +do_release_region: + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return ret; +} + +static int __devexit serial_rk_remove(struct platform_device *pdev) +{ + struct uart_rk_port *up = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + if (up) { + struct resource *mem; + destroy_workqueue(up->uart_wq); + uart_remove_one_port(&serial_rk_reg, &up->port); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(up->port.membase); + up->port.membase = NULL; + clk_put(up->clk); + kfree(up); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + } + + return 0; +} + +static int serial_rk_suspend(struct platform_device *dev, pm_message_t state) +{ + struct uart_rk_port *up = platform_get_drvdata(dev); + + if (up) + uart_suspend_port(&serial_rk_reg, &up->port); + printk("%s:%s\n", __func__, dev->name); + return 0; +} + +static int serial_rk_resume(struct platform_device *dev) +{ + struct uart_rk_port *up = platform_get_drvdata(dev); + + if (up) + uart_resume_port(&serial_rk_reg, &up->port); + printk("%s:%s\n", __func__, dev->name); + return 0; +} + +static struct platform_driver serial_rk_driver = { + .probe = serial_rk_probe, + .remove = __devexit_p(serial_rk_remove), + .suspend = serial_rk_suspend, + .resume = serial_rk_resume, + .driver = { +#if defined(CONFIG_SERIAL_RK29_STANDAR) + .name = "rk29_serial", +#elif defined(CONFIG_SERIAL_RK2818) + .name = "rk2818_serial", +#else + .name = "rk_serial", +#endif + .owner = THIS_MODULE, + }, +}; + +static int __init serial_rk_init(void) +{ + int ret; + + ret = uart_register_driver(&serial_rk_reg); + if (ret) + return ret; + + ret = platform_driver_register(&serial_rk_driver); + if (ret != 0) + uart_unregister_driver(&serial_rk_reg); + + return ret; +} + +static void __exit serial_rk_exit(void) +{ + platform_driver_unregister(&serial_rk_driver); + uart_unregister_driver(&serial_rk_reg); +} + +module_init(serial_rk_init); +module_exit(serial_rk_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("RK UART driver"); +