Files

2726 lines
80 KiB
C
Executable File

/*
* ms_uart.c- Sigmastar
*
* Copyright (c) [2019~2020] SigmaStar Technology.
*
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License version 2 for more details.
*
*/
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
//#include <linux/serial_reg.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/nmi.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include "ms_platform.h"
#include "mdrv_types.h"
#include "gpio.h"
#include "ms_uart.h"
#include "cam_os_wrapper.h"
#define CONSOLE_DMA 1
#define DEBUG_PAD_MUX 0
#define UART_DEBUG 0
#define MS_UART_8250_BUG_THRE 0
#define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */
#if UART_DEBUG
#define UART_DBG(fmt, arg...) printk(KERN_INFO fmt, ##arg)
#else
#define UART_DBG(fmt, arg...)
#endif
#define UART_ERR(fmt, arg...) printk(KERN_ERR fmt, ##arg)
//#define REG_UART_MODE 0x1F203C0C
//#define REG_UART_SEL 0x1F203D4C
//#define REG_UART_SEL4 0x1F203D50
#define REG_FORCE_RX_DISABLE 0x1F203D5C
#define UART_TX_TASK 0 /* use tasklet to send TX char */
#define UART_USE_LOOPBACK 0 /* use UART loopback mode to avoid external input */
#define UART_USE_SPINLOCK 0 /* set IER use spinlock to ensure atomic */
#if CONSOLE_DMA
#define NR_CONSOLE_PORTS 4
#else
#define NR_CONSOLE_PORTS 1
#endif
#define MS_CONSOLE_DEV "ttyS"
#define REG_DLL_THR_RBR(p) GET_REG_ADDR((u32)p->membase, (0x00))
#define REG_DLH_IER(p) GET_REG_ADDR((u32)p->membase, (0x02))
#define REG_IIR_FCR(p) GET_REG_ADDR((u32)p->membase, (0x04))
#define REG_LCR(p) GET_REG_ADDR((u32)p->membase, (0x06))
#define REG_MCR(p) GET_REG_ADDR((u32)p->membase, (0x08))
#define REG_LSR(p) GET_REG_ADDR((u32)p->membase, (0x0A))
#define REG_MSR(p) GET_REG_ADDR((u32)p->membase, (0x0C))
#define REG_USR(p) GET_REG_ADDR((u32)p->membase, (0x0E))
#define REG_TFL(p) GET_REG_ADDR((u32)p->membase, (0x10))
#define REG_RFL(p) GET_REG_ADDR((u32)p->membase, (0x12))
#define REG_RST(p) GET_REG_ADDR((u32)p->membase, (0x14))
/* Interrupt Enable Register (IER)*/
#define UART_IER_RDI 0x01 /* Received Data Available Interrupt */
#define UART_IER_THRI 0x02 /* Transmitter Holding Register Empty Interrupt */
#define UART_IER_RLSI 0x04 /* Receiver Line Status Interrupt */
#define UART_IER_MSI 0x08 /* Modem Status Interrupt */
/* Interrupt Identification Register (IIR) */
#define UART_IIR_MSI 0x00 /* 0000: Modem Status */
#define UART_IIR_NO_INT 0x01 /* 0001: No pending interrupts */
#define UART_IIR_THRI 0x02 /* 0010: Transmitter Holding Register Empty */
#define UART_IIR_RDI 0x04 /* 0100: Receiver Data Available */
#define UART_IIR_RLSI 0x06 /* 0110: Receiver Line Status */
#define UART_IIR_BUSY 0x07 /* 0111: Busy detect indication (try to write LCR while UART is busy) */
#define UART_IIR_RX_TIMEOUT 0x0C /* 1100: Character timeout */
#define UART_IIR_ID_MASK 0x0F /* Mask Bit[3:0] for IIR */
/* FIFO Control Register (FCR) */
#define UART_FCR_FIFO_ENABLE 0x01 /* Clear & Reset Rx FIFO buffer */
#define UART_FCR_CLEAR_RCVR 0x02 /* Clear & Reset Rx FIFO buffer */
#define UART_FCR_CLEAR_XMIT 0x04 /* Clear & Reset Tx FIFO buffer */
#define UART_FCR_TRIGGER_TX_L0 0x00 /* Trigger Write when emtpy */
#define UART_FCR_TRIGGER_TX_L1 0x10 /* Trigger Write when 2 characters */
#define UART_FCR_TRIGGER_TX_L2 0x20 /* Trigger Write when 1/4 full */
#define UART_FCR_TRIGGER_TX_L3 0x30 /* Trigger Write when 1/2 full */
#define UART_FCR_TRIGGER_RX_L0 0x00 /* Trigger Read when there is 1 char*/
#define UART_FCR_TRIGGER_RX_L1 0x40 /* Trigger Read when 1/4 full */
#define UART_FCR_TRIGGER_RX_L2 0x80 /* Trigger Read when 1/2 full */
#define UART_FCR_TRIGGER_RX_L3 0xC0 /* Trigger Read when 2 less then full */
/* Line Control Register (LCR) */
#define UART_LCR_WL_MASK 0x03 /* Word length mask */
#define UART_LCR_WLEN5 0x00 /* Word length is 5 bits */
#define UART_LCR_WLEN6 0x01 /* Word length is 6 bits */
#define UART_LCR_WLEN7 0x02 /* Word length is 7 bits */
#define UART_LCR_WLEN8 0x03 /* Word length is 8 bits */
#define UART_LCR_STOP_MASK 0x04 /* Stop bit mask */
#define UART_LCR_STOP1 0x00 /* Stop length is 1 bit */
#define UART_LCR_STOP2 0x04 /* Stop length is 1.5 bits (5-bit char), 2 bits (otherwise) */
#define UART_LCR_PARITY_EN 0x08 /* Parity Enable */
#define UART_LCR_PARITY_SEL 0x10 /* Even Parity Select */
#define UART_LCR_SBC 0x40 /* Set break control */
#define UART_LCR_DLAB 0x80 /* Divisor Latch Access bit, 1=Divisor Latch, 0=Normal Register */
#define UART_MCR_DTR 0x01
#define UART_MCR_RTS 0x02
#define UART_MCR_OUT1 0x04
#define UART_MCR_OUT2 0x08
#define UART_MCR_LOOPBACK 0x10
#define UART_MCR_AFCE 0x20
/* Line Status Register */
#define UART_LSR_DR 0x01 /* Data Ready, at least one char in FIFO buffer*/
#define UART_LSR_OE 0x02 /* Overrun Error, FIFO buffer is full */
#define UART_LSR_PE 0x04 /* Parity Error */
#define UART_LSR_FE 0x08 /* Framing Error, no valid stop bit */
#define UART_LSR_BI 0x10 /* Break Interrupt */
#define UART_LSR_THRE 0x20 /* Tx FIFO buffer is empty*/
#define UART_LSR_TEMT 0x40 /* Both TX FIFO buffer & shift register are empty */
#define UART_LSR_TX_ERROR 0x80 /* Tx FIFO buffer is error */
#define UART_USR_BUSY 0x01
#define UART_USR_TXFIFO_NOT_FULL 0x02
#define UART_USR_TXFIFO_EMPTY 0x04
#define UR2DMA_TX_BUF_LENGTH 0x1000 //must be 8 byte aligned, linux should better be PAGE_ALIGN
#define UR2DMA_TX_BUF_MASK 0x0FFF
#define UR2DMA_RX_BUF_LENGTH 0x1000 //must be 8 byte aligned, linux should better be PAGE_ALIGN
#define URDMA_RX_TIMEOUT 0x0F
#define URDMA_TX_TIMEOUT 0x0F
#define URDMA_RX_INTR_LEVEL 0x500 //need to think
#define URDMA_RX_INTR_TYPE_TIMEOUT 1
#define URDMA_RX_INTR_TYPE_THRESHOLD 2
#define URDMA_INTR_STATUS_RX 1
#define URDMA_INTR_STATUS_TX 2
#define URDMA_LOU16(u32Val) ((U16)(u32Val)&0xFFFF)
#define URDMA_HIU16(u32Val) ((U16)((u32Val) >> 16)&0x0FFF)
#define URDMA_logical2bus(x) (x&0x0FFFFFFF)
/* Warning: Little Endian */
typedef struct reg_urdma
{
union
{
volatile U16 reg00;
struct
{
volatile U16 sw_rst : 1; /* BIT0 */
volatile U16 urdma_mode : 1; /* BIT1 */
volatile U16 tx_urdma_en : 1; /* BIT2 */
volatile U16 rx_urdma_en : 1; /* BIT3 */
volatile U16 tx_endian : 1; /* BIT4 */
volatile U16 rx_endian : 1; /* BIT5 */
volatile U16 tx_sw_rst : 1; /* BIT6 */
volatile U16 rx_sw_rst : 1; /* BIT7 */
volatile U16 reserve00 : 3; /* BIT8 ~ BIT10 */
volatile U16 rx_op_mode : 1; /* BIT11 */
volatile U16 tx_busy : 1; /* BIT12 */
volatile U16 rx_busy : 1; /* BIT13 */
volatile U16 reserve01 : 2; /* BIT14 ~ BIT15 */
} ;
} ;
U16 space00;
union
{
volatile U16 reg01;
struct
{
volatile U16 intr_threshold : 12; /* BIT0 ~ BIT11 */
volatile U16 reserve02 : 4; /* BIT12 ~ BIT15 */
} ;
} ;
U16 space01;
union
{
volatile U16 reg02;
struct
{
volatile U16 tx_buf_base_h : 16; /* BIT0 ~ BIT15 */
} ;
} ;
U16 space02;
union
{
volatile U16 reg03;
struct
{
volatile U16 tx_buf_base_l : 16; /* BIT0 ~ BIT15 */
} ;
} ;
U16 space03;
union
{
volatile U16 reg04;
struct
{
volatile U16 tx_buf_size : 13; /* BIT0 ~ BIT12 */
volatile U16 reserve04 : 3; /* BIT13 ~ BIT15 */
} ;
} ;
U16 space04;
union
{
volatile U16 reg05;
struct
{
volatile U16 tx_buf_rptr : 16; /* BIT0 ~ BIT15 */
} ;
} ;
U16 space05;
union
{
volatile U16 reg06;
struct
{
volatile U16 tx_buf_wptr : 16; /* BIT0 ~ BIT15 */
} ;
} ;
U16 space06;
union
{
volatile U16 reg07;
struct
{
volatile U16 tx_timeout : 4; /* BIT0 ~ BIT3 */
volatile U16 reserve05 : 12; /* BIT4 ~ BIT15 */
} ;
} ;
U16 space07;
union
{
volatile U16 reg08;
struct
{
volatile U16 rx_buf_base_h : 16; /* BIT0 ~ BIT7 */
} ;
} ;
U16 space08;
union
{
volatile U16 reg09;
struct
{
volatile U16 rx_buf_base_l : 16; /* BIT0 ~ BIT15 */
} ;
} ;
U16 space09;
union
{
volatile U16 reg0a;
struct
{
volatile U16 rx_buf_size : 13; /* BIT0 ~ BIT12 */
volatile U16 reserve07 : 3; /* BIT13 ~ BIT15 */
} ;
} ;
U16 space0a;
union
{
volatile U16 reg0b;
struct
{
volatile U16 rx_buf_wptr : 16; /* BIT0 ~ BIT15 */
} ;
} ;
U16 space0b;
union
{
volatile U16 reg0c;
struct
{
volatile U16 rx_timeout : 4; /* BIT0 ~ BIT3 */
volatile U16 reserve08 : 12; /* BIT4 ~ BIT15 */
} ;
} ;
U16 space0c;
union
{
volatile U16 reg0d;
struct
{
volatile U16 rx_intr_clr : 1; /* BIT0 */
volatile U16 rx_intr1_en : 1; /* BIT1 */
volatile U16 rx_intr2_en : 1; /* BIT2 */
volatile U16 reserve09 : 1; /* BIT3 */
volatile U16 rx_intr1 : 1; /* BIT4 */
volatile U16 rx_intr2 : 1; /* BIT5 */
volatile U16 reserve0a : 1; /* BIT6 */
volatile U16 rx_mcu_intr : 1; /* BIT7 */
volatile U16 tx_intr_clr : 1; /* BIT8 */
volatile U16 tx_intr_en : 1; /* BIT9 */
volatile U16 reserve0b : 5; /* BIT10 ~ BIT14 */
volatile U16 tx_mcu_intr : 1; /* BIT15 */
} ;
} ;
} reg_urdma;
struct ms_urdma
{
reg_urdma *reg_base;
unsigned int urdma_irq;
u8 *rx_buf;
u8 *tx_buf;
dma_addr_t rx_urdma_base;
dma_addr_t tx_urdma_base;
u32 rx_urdma_size;
u32 tx_urdma_size;
u16 sw_rx_rptr;
};
struct ms_uart_port {
struct uart_port port;
struct ms_urdma *urdma;
struct device *dev;
struct clk *clk;
int use_dma;
#if UART_TX_TASK
struct tasklet_struct xmit_tasklet;
#endif
int rx_guard;
u8 backupIER;
u8 backupLCR;
u8 backupMCR;
u16 backupDivisor;
u8 padmux;
u8 pad_mode;
u8 rs485_gpio_flag;
struct timer_list timer; /* "no irq" timer */
u16 bugs; /* port bugs */
CamOsThread urdma_task;
};
static u32 ms_uart_tx_empty(struct uart_port *p);
static void ms_uart_set_mctrl(struct uart_port *pPort_st, u32 mctrl);
static u32 ms_uart_get_mctrl(struct uart_port *pPort_st);
static void ms_uart_stop_tx(struct uart_port *p);
static void ms_uart_start_tx(struct uart_port *p);
static void ms_uart_stop_rx(struct uart_port *p);
static void ms_uart_enable_ms(struct uart_port *pPort_st);
static void ms_uart_break_ctl(struct uart_port *pPort_st, s32 break_state);
static s32 ms_uart_startup(struct uart_port *p);
static void ms_uart_shutdown(struct uart_port *p);
static void ms_uart_set_termios(struct uart_port *p, struct ktermios *pTermios_st, struct ktermios *pOld_st);
static const char * ms_uart_type(struct uart_port *pPort_st);
static void ms_uart_release_port(struct uart_port *pPort_st);
static void ms_uart_release_port(struct uart_port *pPort_st);
static s32 ms_uart_request_port(struct uart_port *pPort_st);
static void ms_uart_config_port(struct uart_port *pPort_st, s32 flags);
static s32 ms_uart_verify_port(struct uart_port *pPort_st, struct serial_struct *ser);
/* UART Operations */
static struct uart_ops ms_uart_ops =
{
.tx_empty = ms_uart_tx_empty,
.set_mctrl = ms_uart_set_mctrl, /* Not supported in MSB2501 */
.get_mctrl = ms_uart_get_mctrl, /* Not supported in MSB2501 */
.stop_tx = ms_uart_stop_tx,
.start_tx = ms_uart_start_tx,
.stop_rx = ms_uart_stop_rx,
.enable_ms = ms_uart_enable_ms, /* Not supported in MSB2501 */
.break_ctl = ms_uart_break_ctl, /* Not supported in MSB2501 */
.startup = ms_uart_startup,
.shutdown = ms_uart_shutdown,
.set_termios = ms_uart_set_termios,
.type = ms_uart_type, /* Not supported in MSB2501 */
.release_port = ms_uart_release_port, /* Not supported in MSB2501 */
.request_port = ms_uart_request_port, /* Not supported in MSB2501 */
.config_port = ms_uart_config_port, /* Not supported in MSB2501 */
.verify_port = ms_uart_verify_port, /* Not supported in MSB2501 */
};
static void ms_uart_console_write(struct console *co, const char *str, u32 count);
static s32 ms_uart_console_setup(struct console *co, char *options);
#if CONSOLE_DMA
static s32 _ms_uart_console_prepare(int idx);
static int ms_uart_console_match(struct console *co, char *name, int idx, char *options);
static void *urdma_tx_thread(void * arg);
static int _urdma_tx(struct ms_uart_port *mp, unsigned char* buf, int buf_size);
static DEFINE_SPINLOCK(mutex_console_2_dma);
#else
static void ms_uart_add_console_port(struct ms_uart_port *ur);
static struct ms_uart_port *console_ports[NR_CONSOLE_PORTS];
#endif
static struct ms_uart_port console_port;
static struct uart_driver ms_uart_driver;
/* Serial Console Structure Definition */
static struct console ms_uart_console =
{
.name = MS_CONSOLE_DEV,
.write = ms_uart_console_write,
.setup = ms_uart_console_setup,
.flags = CON_PRINTBUFFER,
.device = uart_console_device,
.data = &ms_uart_driver,
.index = -1,
#if CONSOLE_DMA
.match = ms_uart_console_match,
#endif
};
static struct uart_driver ms_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "ms_uart",
.dev_name = "ttyS",
.nr = 8,
.cons = &ms_uart_console,
};
static DECLARE_WAIT_QUEUE_HEAD(urdma_wait);
static volatile int urdma_conditions = 0;
// extern void Chip_UART_Disable_Line(int line);
// extern void Chip_UART_Enable_Line(int line);
static void URDMA_Reset(struct uart_port *p);
static void URDMA_Activate(struct uart_port *p,BOOL bEnable);
static void URDMA_TxEnable(struct uart_port *p,BOOL bEnable);
static void URDMA_RxEnable(struct uart_port *p,BOOL bEnable);
static U8 URDMA_GetInterruptStatus(struct uart_port *p);
static void URDMA_TxInit(struct uart_port *p);
static void URDMA_RxInit(struct uart_port *p);
static void URDMA_TxSetupTimeoutInterrupt(struct uart_port *p,BOOL bEnable);
static void URDMA_TxClearInterrupt(struct uart_port *p);
static void URDMA_RxSetupTimeoutInterrupt(struct uart_port *p,BOOL bEnableTimeout);
static void URDMA_RxSetupThresholdInterrupt(struct uart_port *p,BOOL bEnableThreshold);
static U8 URDMA_RxGetInterrupt(struct uart_port *p);
static void URDMA_RxClearInterrupt(struct uart_port *p);
static void URDMA_StartTx(struct uart_port *p);
static void URDMA_StartRx(struct uart_port *p);
static int ms_uart_rs485_gpio(struct serial_rs485 *rs485,bool send)
{
UART_ERR("delay:%d,%d\n",rs485->delay_rts_before_send,rs485->delay_rts_before_send);
if ((rs485->flags & SER_RS485_ENABLED) && send)
{
if(rs485->delay_rts_before_send)udelay(rs485->delay_rts_before_send);
if(rs485->flags&SER_RS485_RTS_ON_SEND)
{
gpio_set_value(rs485->padding[0],1);UART_ERR("wend set higt\n");
}
else
{
gpio_set_value(rs485->padding[0],0);UART_ERR("send set low\n");
}
}
else if ((rs485->flags & SER_RS485_ENABLED) && !send)
{
if(rs485->delay_rts_after_send)udelay(rs485->delay_rts_after_send);
if(rs485->flags&SER_RS485_RTS_AFTER_SEND)
{
gpio_set_value(rs485->padding[0],1);UART_ERR("after set higt\n");
}
else
{
gpio_set_value(rs485->padding[0],0);UART_ERR("after set low\n");
}
}
return 0;
}
static int ms_uart_rs485_config(struct uart_port *port,
struct serial_rs485 *rs485)
{
if (rs485->flags & SER_RS485_ENABLED) {
UART_ERR("uart %d set 485 on,pin RTS:%d\n", port->line,rs485->padding[0]);
if(gpio_request(rs485->padding[0], "gpio_irq_test") < 0)
{
UART_ERR("request gpio[%d] failed...\n", rs485->padding[0]);
return -EFAULT;
}
if(rs485->flags&SER_RS485_RTS_ON_SEND)
{
if(gpio_direction_output(rs485->padding[0],0) < 0)
{
UART_ERR("gpio_direction_output low[%d] failed...\n", rs485->padding[0]);
return -EFAULT;
}
}
else
{
if(gpio_direction_output(rs485->padding[0],1) < 0)
{
UART_ERR("gpio_direction_output high[%d] failed...\n", rs485->padding[0]);
return -EFAULT;
}
}
UART_ERR("rs485->flags=0x%x\n",rs485->flags);
}
else {
UART_ERR("uart %d set 485 off\n", port->line);
gpio_free(rs485->padding[0]);
}
memcpy(&port->rs485, rs485, sizeof(*rs485));
return 0;
}
void inline ms_uart_clear_fifos(struct uart_port *p)
{
unsigned int timeout=0;
while( ((INREG8(REG_USR(p)) & UART_USR_BUSY)) && timeout < 2000)
timeout++;
OUTREG8(REG_IIR_FCR(p), UART_FCR_FIFO_ENABLE);
OUTREG8(REG_IIR_FCR(p), UART_FCR_FIFO_ENABLE | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
OUTREG8(REG_IIR_FCR(p), 0);
}
static u8 u8SelectPad[] = { 2, 3, 1, 4 };
void ms_select_pad(struct uart_port *p, u8 padmux, u8 pad_mode)
{
#if 0
u8 select=0;
if(p->line == 0)
select=2;
else if(p->line == 1)
select=3;
else if(p->line == 2)
select=1;
else if(p->line == 3)
select=4;
else
{
// UART_ERR("[%s] port line %d is not supported\n", __func__, p->line);
return;
}
ms_uart_select_pad(select, padmux, pad_mode);
#else
ms_uart_select_pad(u8SelectPad[p->line], padmux, pad_mode);
#endif
}
void ms_force_rx_disable(u8 padmux, BOOL status)
{
switch(padmux) //set 0 for disable, 1 for enable
{
case MUX_PM_UART:
OUTREGMSK16(REG_FORCE_RX_DISABLE, (~status) << 2, 1 << 2);
break;
case MUX_FUART:
OUTREGMSK16(REG_FORCE_RX_DISABLE, (~status) << 0, 1 << 0);
break;
case MUX_UART0:
OUTREGMSK16(REG_FORCE_RX_DISABLE, (~status) << 1, 1 << 1);
break;
case MUX_UART1:
OUTREGMSK16(REG_FORCE_RX_DISABLE, (~status) << 3, 1 << 3);
break;
#ifdef CONFIG_MS_SUPPORT_UART2
case MUX_UART2:
OUTREGMSK16(REG_FORCE_RX_DISABLE, (~status) << 4, 1 << 4);
break;
#endif
default:
// UART_ERR("[%s] Padmux %d not defined\n", __func__, padmux);
break;
}
}
U16 ms_uart_set_clk(struct uart_port *p, u32 request_baud)
{
//struct clk *clk;
unsigned int num_parents;
struct clk **clk_parents;
unsigned int tolerance, rate, divisor, real_baud;
struct ms_uart_port *mp;
int i;
if(!p->dev)
{
//do nothing because clk and device node not ready
return 0;
}
else
{
mp = (struct ms_uart_port*)(p->dev->driver_data);
if (IS_ERR(mp->clk)) {
// UART_ERR("%s: of_clk_get failed\n", p->dev->of_node->full_name);
p->uartclk=172800000;
return 0;
}
if(of_property_read_u32(p->dev->of_node, "tolerance", &tolerance))
{
UART_DBG("%s: use default tolerance 3%%\n", __func__);
tolerance = 3;
}
num_parents = clk_hw_get_num_parents(__clk_get_hw(mp->clk));
if(!num_parents)
{
rate=clk_get_rate(mp->clk);
divisor = (rate + (8*request_baud)) / (16*request_baud);
real_baud = rate / (16 * divisor);
// UART_ERR("[uart%d]divisor=0x%02X, real_baud=%d,uart_clk=%d\n", p->line, divisor, real_baud,rate);
if( (abs(real_baud - request_baud)*100/request_baud) < tolerance)
{
p->uartclk=rate;
// UART_ERR("[uart%d] uartclk=%d, request_baud=%d, real_baud=%d, divisor=0x%X\n", p->line, p->uartclk, request_baud, real_baud, divisor);
}
return divisor;
}
else
{
clk_parents = kzalloc((sizeof(*clk_parents) * num_parents), GFP_KERNEL);
if(!clk_parents)
{
// UART_ERR("%s: failed to allocate memory\n", __func__);
kfree(clk_parents);
p->uartclk=clk_get_rate(mp->clk);
return 0;
}
for(i = 0; i < num_parents; i++)
{
clk_parents[i] = clk_hw_get_parent_by_index(__clk_get_hw(mp->clk), i)->clk;
rate = clk_get_rate(clk_parents[i]);
divisor = (rate + (8*request_baud)) / (16*request_baud);
real_baud = rate / (16 * divisor);
UART_DBG("[uart%d]foreach parent divisor=0x%02X, real_baud=%d,uart_clk=%d\n", p->line, divisor, real_baud,rate);
if( (abs(real_baud - request_baud)*100/request_baud) < tolerance)
{
clk_set_parent(mp->clk, clk_parents[i]);
p->uartclk=rate;
UART_DBG("[uart%d] uartclk=%d, request_baud=%d, real_baud=%d, divisor=0x%X\n", p->line, p->uartclk, request_baud, real_baud, divisor);
break;
}
}
if(i >= num_parents)
{
// UART_ERR("[uart%d] can't find suitable clk for baud=%d tolerance=%d%%, will not changed\n", p->line, request_baud, tolerance);
divisor = 0;
}
kfree(clk_parents);
return divisor;
}
}
}
void ms_uart_set_divisor(struct uart_port *p, u16 divisor)
{
ms_uart_clear_fifos(p);
// enable Divisor Latch Access, so Divisor Latch register can be accessed
OUTREG8(REG_LCR(p), INREG8(REG_LCR(p)) | UART_LCR_DLAB);
OUTREG8(REG_DLH_IER(p), (u8 )((divisor >> 8) & 0xff));
OUTREG8(REG_DLL_THR_RBR(p), (u8 )(divisor & 0xff));
// disable Divisor Latch Access
OUTREG8(REG_LCR(p), INREG8(REG_LCR(p)) & ~UART_LCR_DLAB);
}
static void ms_uart_console_putchar(struct uart_port *p, s32 ch)
{
u8 lsr_u8 = 0; /* Line Status Register (LSR) */
/* Check if Transmit FIFO full */
/* we can not modify the Tx FIFO size, default is 1 byte size*/
lsr_u8 = INREG8(REG_LSR(p));
while(!(lsr_u8 & UART_LSR_THRE))
{
lsr_u8 = INREG8(REG_LSR(p));
}
OUTREG8(REG_DLL_THR_RBR(p),ch);
/* Check if both TX FIFO buffer & shift register are empty */
//lsr_u8 = INREG8(REG_LSR(p));
//while((lsr_u8 & (UART_LSR_TEMT | UART_LSR_THRE)) != (UART_LSR_TEMT | UART_LSR_THRE))
//{
// lsr_u8 = INREG8(REG_LSR(p));
//}
}
#if CONSOLE_DMA
static void __maybe_unused ms_uart_console_putchar_dma(struct uart_port *p, s32 ch)
{
unsigned char c = (unsigned char)(ch & 0xFF);
while (1 != _urdma_tx(&console_port, &c, 1));
}
static int ms_uart_console_match(struct console *co, char *name, int idx, char *options)
{
co->index = idx;
return -ENODEV;
}
#endif
#if CONSOLE_DMA
static s32 _ms_uart_console_prepare(int idx)
{
struct device_node *console_np;
struct resource res;
char* uart_name[] =
{
"/soc/uart0@1F221000",
"/soc/uart1@1F221200",
"/soc/uart2@1F220400",
"/soc/uart2@1F221400",
};
if ((0 > idx) || (NR_CONSOLE_PORTS <= idx))
return -ENODEV;
console_np=of_find_node_by_path(uart_name[idx]);
if(!console_np)
{
console_np =of_find_node_by_path("console");
idx = 0;
}
if(!console_np)
return -ENODEV;
BUG_ON( of_address_to_resource(console_np,0,&res) );
console_port.port.membase = (void *)res.start;
console_port.port.type = PORT_8250;
console_port.port.ops=&ms_uart_ops;
console_port.port.regshift = 0;
console_port.port.fifosize = 16;
console_port.port.cons=&ms_uart_console;
console_port.port.line= idx;
ms_uart_console.index = console_port.port.line;
return 0;
}
#define UART_RX (0 * 2) // In: Receive buffer (DLAB=0)
#define UART_TX (0 * 2) // Out: Transmit buffer (DLAB=0)
#define UART_DLL (0 * 2) // Out: Divisor Latch Low (DLAB=1)
#define UART_DLM (1 * 2) // Out: Divisor Latch High (DLAB=1)
#define UART_IER (1 * 2) // Out: Interrupt Enable Register
#define UART_IIR (2 * 2) // In: Interrupt ID Register
#define UART_FCR (2 * 2) // Out: FIFO Control Register
#define UART_LCR (3 * 2) // Out: Line Control Register
#define UART_MCR (4 * 2) // Out: Modem Control Register
#define UART_LSR (5 * 2) // In: Line Status Register
#define UART_MSR (6 * 2) // In: Modem Status Register
#define UART_USR (7 * 2) // Out: USER Status Register
#define UART_RST (14 * 2) // Out: SW rstz
// #define REG_ADDR_BASE_UART1 0xFD221200
// #define REG_ADDR_BASE_FUART 0xFD220400
#define UART_BASE IO_ADDRESS(console_port.port.membase)
#define UART_REG8(_x_) ((U8 volatile *)(UART_BASE))[((_x_) * 4) - ((_x_) & 1)]
#define UART_BAUDRATE 115200
#define UART_CLK 172800000
#define UART_LCR_PARITY 0x08
#define UART_FCR_ENABLE_FIFO 0x01
// #define REG_UART_SEL3210 GET_REG16_ADDR(REG_ADDR_BASE_CHIPTOP, 0x53)
// #define REG_RX_ENABLE GET_REG16_ADDR(REG_ADDR_BASE_PM_SLEEP, 0x09)
// #define REG_UART0_CLK GET_REG16_ADDR(REG_ADDR_BASE_CLKGEN, 0x31)
void uart_init(void)
{
U8 count=0;
#if 0
// Enable uart0 clock
OUTREG8(REG_UART0_CLK, 0x09); //clk_xtal and gating
CLRREG8(REG_UART0_CLK, 0x01); //clear gating
// Reset RX_enable
CLRREG16(REG_RX_ENABLE, BIT11);
// Reset PM uart pad digmux
OUTREG16(REG_UART_SEL3210, 0x3210);
#endif
// Toggle SW reset
UART_REG8(UART_RST) &= ~0x01;
UART_REG8(UART_RST) |= 0x01;
// Disable all interrupts
UART_REG8(UART_IER) = 0x00;
// Set "reg_mcr_loopback";
UART_REG8(UART_MCR) |= 0x10;
// Poll "reg_usr_busy" till 0; (10 times)
while(UART_REG8(UART_USR) & 0x01 && count++ < 10)
;
if(count == 10)
{
// SetDebugFlag(FLAG_INIT_UART_BUSY); /* 0x0BF1 */
}
else // Set divisor
{
U16 DLR = ((UART_CLK+(8*UART_BAUDRATE)) / (16 * UART_BAUDRATE));
UART_REG8(UART_LCR) |= UART_LCR_DLAB;
UART_REG8(UART_DLL) = DLR & 0xFF;
UART_REG8(UART_DLM) = (DLR >> 8) & 0xFF;
UART_REG8(UART_LCR) &= ~(UART_LCR_DLAB);
}
// Set 8 bit char, 1 stop bit, no parity
UART_REG8(UART_LCR) = UART_LCR_WLEN8 & ~(UART_LCR_STOP2 | UART_LCR_PARITY);
// Unset loopback
UART_REG8(UART_MCR) &= ~0x10;
// Enable TX/RX fifo
UART_REG8(UART_FCR) = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;;
#if 0
// Set PM uart pad digmux to UART0
OUTREG16(REG_UART_SEL3210, 0x3012);
// Set RX_enable
SETREG16(REG_RX_ENABLE, BIT11);
#endif
}
#endif
#if CONSOLE_DMA
static void ms_uart_console_write(struct console *co, const char *str, u32 count)
{
struct ms_uart_port *mp;
struct uart_port *p;
unsigned long flags;
int locked = 1;
if( (!str )|| co->index>=NR_CONSOLE_PORTS || co->index < 0)
{
return;
}
mp = &console_port;
p = &(mp->port);
if (p->sysrq || oops_in_progress)
locked = spin_trylock_irqsave(&p->lock, flags);
else
spin_lock_irqsave(&p->lock, flags);
if(!mp->use_dma)
uart_console_write(p, str, count, ms_uart_console_putchar);
else if (mp->urdma)
{
uart_console_write(p, str, count, ms_uart_console_putchar_dma);
}
if (locked)
spin_unlock_irqrestore(&p->lock, flags);
return;
}
static s32 __init ms_uart_console_setup(struct console *co, char *options)
{
/* Define Local Variables */
s32 baud =115200;
s32 bits = 8;
s32 parity = 'n';
s32 flow = 'n';
if(!options)
{
options = "115200n8r"; /* Set default baudrate for console*/
}
/* parsing the command line arguments */
uart_parse_options(options, &baud, &parity, &bits, &flow);
_ms_uart_console_prepare(co->index);
if (co->index)
uart_init();
#if DEBUG_PAD_MUX
{
*((volatile int*)(0xFD203D4C)) &= 0xFFF0;
// *((volatile int*)(0xFD203D4C)) |= 0x0003;
*((volatile int*)(0xFD203D4C)) |= (u8SelectPad[co->index] & 0xF);
}
#endif
return uart_set_options(&(console_port.port), co, baud, parity, bits, flow);
}
#else // #if CONSOLE_DMA
static void ms_uart_console_write(struct console *co, const char *str, u32 count)
{
struct ms_uart_port *mp;
struct uart_port *p;
unsigned long flags;
int locked = 1;
if( (!str )|| co->index>=NR_CONSOLE_PORTS || co->index < 0)
{
return;
}
mp = console_ports[co->index];
p = &(mp->port);
if (p->sysrq || oops_in_progress)
locked = spin_trylock_irqsave(&p->lock, flags);
else
spin_lock_irqsave(&p->lock, flags);
if(!mp->use_dma)
uart_console_write(p, str, count, ms_uart_console_putchar);
if (locked)
spin_unlock_irqrestore(&p->lock, flags);
return;
}
static s32 __init ms_uart_console_setup(struct console *co, char *options)
{
/* Define Local Variables */
s32 baud =115200;
s32 bits = 8;
s32 parity = 'n';
s32 flow = 'n';
if(!options)
{
options = "115200n8r"; /* Set default baudrate for console*/
}
/* validate console port index */
if(co->index == -1 || co->index >= NR_CONSOLE_PORTS)
{
co->index = 0;
}
/* parsing the command line arguments */
uart_parse_options(options, &baud, &parity, &bits, &flow);
if(console_ports[co->index]==NULL){
return -ENODEV;
}
return uart_set_options(&(console_ports[co->index]->port), co, baud, parity, bits, flow);
}
static void ms_uart_add_console_port(struct ms_uart_port *ur)
{
if(ur->port.line < NR_CONSOLE_PORTS )
{
console_ports[ur->port.line] = ur;
}
}
#endif // #if CONSOLE_DMA
static const char * ms_uart_type(struct uart_port *pPort_st)
{
return NULL;
}
static void ms_uart_release_port(struct uart_port *pPort_st)
{
}
static s32 ms_uart_request_port(struct uart_port *pPort_st)
{
int ret=0;
return ret;
}
static void ms_uart_config_port(struct uart_port *pPort_st, s32 flags)
{
}
static s32 ms_uart_verify_port(struct uart_port *pPort_st, struct serial_struct *ser)
{
int ret=0;
return ret;
}
static void ms_uart_enable_ms(struct uart_port *pPort_st)
{
}
static u32 ms_uart_get_mctrl(struct uart_port *pPort_st)
{
return (TIOCM_CAR|TIOCM_CTS|TIOCM_DSR);
}
static void ms_uart_set_mctrl(struct uart_port *pPort_st, u32 mctrl)
{
}
static void ms_uart_break_ctl(struct uart_port *pPort_st, s32 break_state)
{
}
static void ms_uart_stop_tx(struct uart_port *p)
{
struct ms_uart_port *mp = (struct ms_uart_port*)(p->dev->driver_data);
if(!mp->use_dma)
{
#if UART_USE_SPINLOCK
unsigned long flags;
spin_lock_irqsave(&p->lock, flags);
CLRREG8(REG_DLH_IER(p), UART_IER_THRI);
spin_unlock_irqrestore(&p->lock, flags);
#else
CLRREG8(REG_DLH_IER(p), UART_IER_THRI);
//NOTE: Read IIR to clear dummy THRI after disable THRI occurred at ZEBU -- Spade
INREG8(REG_IIR_FCR(p));
#endif
}
else
{
// printk(KERN_ERR "DMA_TX_STOP\n");
URDMA_TxSetupTimeoutInterrupt(p, FALSE);
}
if ((p->rs485.flags & SER_RS485_ENABLED) && mp->rs485_gpio_flag)
{
while( !( INREG8(REG_USR(p)) & UART_USR_TXFIFO_EMPTY ) );
ms_uart_rs485_gpio(&p->rs485,false);
mp->rs485_gpio_flag = 0;
}
}
static void ms_uart_stop_rx(struct uart_port *p)
{
struct ms_uart_port *mp = (struct ms_uart_port*)p->dev->driver_data;
if(!mp->use_dma)
{
#if UART_USE_SPINLOCK
unsigned long flags;
spin_lock_irqsave(&p->lock, flags);
CLRREG8(REG_DLH_IER(p), UART_IER_RDI | UART_IER_RLSI);
spin_unlock_irqrestore(&p->lock, flags);
#else
CLRREG8(REG_DLH_IER(p), UART_IER_RDI | UART_IER_RLSI);
#endif
}
}
static void ms_uart_start_tx(struct uart_port *p)
{
struct ms_uart_port *mp = (struct ms_uart_port*)p->dev->driver_data;
struct circ_buf *xmit = &p->state->xmit;
if(CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE) && (p->rs485.flags & SER_RS485_ENABLED))
{
mp->rs485_gpio_flag = 1;
}
if ((p->rs485.flags & SER_RS485_ENABLED) && mp->rs485_gpio_flag) {
ms_uart_rs485_gpio(&p->rs485,true);
}
if(!mp->use_dma)
{
#if UART_USE_SPINLOCK
unsigned long flags;
spin_lock_irqsave(&p->lock, flags);
SETREG8(REG_DLH_IER(p), UART_IER_THRI);
spin_unlock_irqrestore(&p->lock, flags);
#else
SETREG8(REG_DLH_IER(p), UART_IER_THRI);
#endif
}
else
{
// pr_err("DMA_TX_START\n");
URDMA_TxSetupTimeoutInterrupt(p, TRUE);
URDMA_StartTx(p);
}
}
static u32 ms_uart_tx_empty(struct uart_port *p)
{
int ret=0;
struct ms_uart_port *mp = (struct ms_uart_port*)p->dev->driver_data;
if(!mp->use_dma)
{
/* Check if both TX FIFO buffer & shift register are empty */
if((INREG8(REG_LSR(p)) & (UART_LSR_TEMT | UART_LSR_THRE)) == (UART_LSR_TEMT | UART_LSR_THRE))
{
ret=TIOCSER_TEMT; /*if no support, also return this */
}
}
else
{
if(mp->urdma->reg_base->tx_buf_rptr == mp->urdma->reg_base->tx_buf_wptr)
{
return TIOCSER_TEMT; /*if no support, also return this */
}
else
{
return 0;
}
}
return ret;
}
#if UART_TX_TASK
static void ms_do_xmit_task(unsigned long port_address)
{
/* Define Local Variables */
s32 count = 0;
struct circ_buf *xmit;
u8 u8USR = 0;
struct uart_port* p = (struct uart_port*) ((void *) port_address);
/* Parameter out-of-bound check */
if (!p)
{
// UART_ERR("ms_do_xmit_task: port is NULL\n");
return;
}
xmit = &p->state->xmit;
if (p->x_char)
{
while( !( INREG8(REG_LSR(p)) & UART_LSR_THRE ) )
{
//nothing to do
}
OUTREG8(REG_DLL_THR_RBR(p), xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
p->icount.tx++;
p->x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(p))
{
ms_uart_stop_tx(p);
return;
}
u8USR = INREG8(REG_USR(p));
if (UART_USR_TXFIFO_EMPTY == (u8USR & (UART_USR_TXFIFO_EMPTY))) // Tx FIFO Empty
{
count = p->fifosize;
}
else if (UART_USR_TXFIFO_NOT_FULL == (u8USR & (UART_USR_TXFIFO_NOT_FULL))) // not empty, but not full
{
count = 1;
}
do {
OUTREG8(REG_DLL_THR_RBR(p), xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
p->icount.tx++;
if (uart_circ_empty(xmit))
{
break;
}
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(p);
if (uart_circ_empty(xmit))
{
ms_uart_stop_tx(p);
return;
}
#if UART_USE_SPINLOCK
{
unsigned long flags;
spin_lock_irqsave(&p->lock, flags);
SETREG8(REG_DLH_IER(p), UART_IER_THRI);
spin_unlock_irqrestore(&p->lock, flags);
}
#else
SETREG8(REG_DLH_IER(p), UART_IER_THRI);
#endif
return;
}
#else // UART_TX_TASK = 0
static void ms_putchar(struct uart_port *p)
{
/* Define Local Variables */
int count;
struct circ_buf *xmit;
u8 u8USR = 0;
/* Parameter out-of-bound check */
if (!p)
{
// UART_ERR("ms_putchar: port is NULL\n");
return;
}
xmit = &p->state->xmit;
if (p->x_char)
{
while( !( INREG8(REG_USR(p)) & UART_USR_TXFIFO_NOT_FULL ) )
{
//nothing to do
}
OUTREG8(REG_DLL_THR_RBR(p), p->x_char);
p->icount.tx++;
p->x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(p))
{
ms_uart_stop_tx(p);
return;
}
u8USR = INREG8(REG_USR(p));
if (UART_USR_TXFIFO_EMPTY == (u8USR & (UART_USR_TXFIFO_EMPTY))) // Tx FIFO Empty
{
count = p->fifosize;
}
else if (UART_USR_TXFIFO_NOT_FULL == (u8USR & (UART_USR_TXFIFO_NOT_FULL))) // not empty, but not full
{
count = 1;
}else
{
count = 1;
}
do {
OUTREG8(REG_DLL_THR_RBR(p), xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
p->icount.tx++;
if (uart_circ_empty(xmit))
{
break;
}
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(p);
if (uart_circ_empty(xmit))
{
ms_uart_stop_tx(p);
}
}
#endif
static int silent_state = 0;
static void ms_getchar(struct uart_port *p)
{
u8 lsr = 0; /* Line Status Register (LSR) */
u32 flag = 0;
u32 ch = 0; /* Character read from UART port */
int max_count = p->fifosize;
struct ms_uart_port *mp = (struct ms_uart_port*)p->dev->driver_data;
static u8 rx_disable = 0, pad_disable = 0;
unsigned long flags;
char isConsole = (console_port.port.line == mp->port.line) ? 1 : 0;
spin_lock_irqsave(&p->lock, flags);
/* Read Line Status Register */
lsr = INREG8(REG_LSR(p));
/* check if Receiver Data Ready */
if((lsr & UART_LSR_DR) != UART_LSR_DR)
{
mp->rx_guard++;
if(unlikely(mp->rx_guard>2000))
{
ch=INREG8(REG_DLL_THR_RBR(p));
UART_ERR("rx interrupts error!!!!!");
mp->rx_guard=0;
}
/* Data NOT Ready */
spin_unlock_irqrestore(&p->lock, flags);
return ;
}
mp->rx_guard=0;
/* while data ready, start to read data from UART FIFO */
do{
flag = TTY_NORMAL;
/* read data from UART IP */
ch = INREG8(REG_DLL_THR_RBR(p));
p->icount.rx++;
if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE)))
{
if (lsr & UART_LSR_BI) {
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
p->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(p))
goto IGNORE_CHAR;
}
else if (lsr & UART_LSR_PE)
{
p->icount.parity++;
}
else if (lsr & UART_LSR_FE)
{
p->icount.frame++;
}
else if (lsr & UART_LSR_OE)
{
p->icount.overrun++;
}
/*
* Mask off conditions which should be ingored.
*/
lsr &= p->read_status_mask;
if (lsr & UART_LSR_BI) {
UART_DBG("handling break....\n");
flag = TTY_BREAK;
}
else if (lsr & UART_LSR_PE)
{
flag = TTY_PARITY;
}
else if (lsr & UART_LSR_FE)
{
flag = TTY_FRAME;
}
}
if (silent_state==0)
{
#ifdef SUPPORT_SYSRQ
if (uart_handle_sysrq_char(p, ch))
{
goto IGNORE_CHAR;
}
#endif
uart_insert_char(p, lsr, UART_LSR_OE, ch, flag);
}
if (isConsole)
{
//when receive '11111', disable UART RX to use TV tool
if(ch == '1')
rx_disable++;
else
rx_disable=0;
if(rx_disable == 5)
{
silent_state = 1 - silent_state;
if (silent_state)
{
printk("disable uart\n");
ch = 0;
}
else
{
printk("enable uart\n");
}
//CLRREG16(0x1F001C24, 0x1<<11);
rx_disable=0;
}
//when receive '22222', disable UART PAD to use TV tool
if(ch == '2')
pad_disable++;
else
pad_disable=0;
if(pad_disable == 5)
{
#if defined(CONFIG_ARCH_INFINITY2)
CLRREG16(0x1F001C24, 0x1<<11);
#else
CLRREG16(0x1F203D4C, 0x000F);
#endif
pad_disable=0;
}
}
IGNORE_CHAR:
lsr = INREG8(REG_LSR(p));
}while((max_count-- >0)&&(lsr & UART_LSR_DR));
spin_unlock_irqrestore(&p->lock, flags);
tty_flip_buffer_push(&p->state->port);
}
static irqreturn_t ms_uart_interrupt(s32 irq, void *dev_id)
{
/* Define Local Variables */
u8 iir_fcr = 0; /* Interrupt Identification Register (IIR) */
struct uart_port *p = dev_id;
struct ms_uart_port *mp = (struct ms_uart_port*)p->dev->driver_data;
u8 count=0, retry=100;
local_fiq_disable();
if(mp->use_dma)
{
u8 status = URDMA_GetInterruptStatus(p);
if(status & URDMA_INTR_STATUS_RX)
{
u8 intr_type = URDMA_RxGetInterrupt(p);
if(intr_type & URDMA_RX_INTR_TYPE_TIMEOUT)
{
URDMA_RxSetupTimeoutInterrupt(p,FALSE);
}
if(intr_type & URDMA_RX_INTR_TYPE_THRESHOLD)
{
URDMA_RxSetupThresholdInterrupt(p,FALSE);
}
URDMA_RxClearInterrupt(p);
URDMA_StartRx(p);
URDMA_RxSetupTimeoutInterrupt(p,TRUE);
URDMA_RxSetupThresholdInterrupt(p,TRUE);
}
else if(status & URDMA_INTR_STATUS_TX)
{
#if CONSOLE_DMA
URDMA_TxClearInterrupt(p);
urdma_conditions = 1;
wake_up_interruptible(&urdma_wait);
#else
URDMA_TxClearInterrupt(p);
//do nothing
#endif
}
else
UART_ERR("URDMA dummy interrupt!\n");
}
else
{
/* Read Interrupt Identification Register */
iir_fcr = INREG8(REG_IIR_FCR(p)) & UART_IIR_ID_MASK;
if( (iir_fcr == UART_IIR_RDI || iir_fcr == UART_IIR_RX_TIMEOUT) ) /* Receive Data Available or Character timeout */
{
ms_getchar(p);
}
else if( iir_fcr == UART_IIR_THRI ) /* Transmitter Holding Register Empty */
{
#if UART_TX_TASK
#if UART_USE_SPINLOCK
unsigned long flags=0;
spin_lock_irqsave(&p->lock, flags);
CLRREG8(REG_DLH_IER(p), UART_IER_THRI);
spin_unlock_irqrestore(&p->lock, flags);
#else
CLRREG8(REG_DLH_IER(p), UART_IER_THRI);
#endif
tasklet_schedule(&mp->xmit_tasklet);
#else
if (silent_state==0)
ms_putchar(p);
#endif
}
else if( iir_fcr == UART_IIR_MSI ) /* Modem Status */
{
// UART_ERR("UART Interrupt: Modem status\n");
// Read MSR to clear
INREG8(REG_MSR(p));
}
else if( iir_fcr == UART_IIR_BUSY ) /* Busy detect indication */
{
// Read USR to clear
INREG8(REG_USR(p));
while( ((INREG8(REG_IIR_FCR(p)) & UART_IIR_ID_MASK) == UART_IIR_BUSY) && (count < retry))
{
// Read USR to clear
INREG8(REG_USR(p));
count++;
}
if (count == retry)
UART_ERR("UART Interrupt: UART_IIR_BUSY\n");
}
else if( iir_fcr == UART_IIR_RLSI ) /* Receiver line status */
{
// Read LSR to clear
INREG8(REG_LSR(p));
}
else if( iir_fcr == UART_IIR_NO_INT ) /* No pending interrupts */
{
while( ((INREG8(REG_USR(p)) & UART_USR_BUSY) == UART_USR_BUSY) && (count < retry))
{
count++;
}
// if (count == retry)
// UART_ERR("UART%d Interrupt: No IRQ rasied by UART, but come in to UART ISR. IIR:0x%02X USR:0x%02X\n", p->line, iir_fcr, INREG8(REG_USR(p)));
}
else /* Unknown Status */
{
UART_ERR("UART Unknown Interrupt, IIR:0x%02X\n", iir_fcr);
}
}
local_fiq_enable();
return IRQ_HANDLED;
}
u32 gu32_console_bug_thre_hits = 0;
module_param(gu32_console_bug_thre_hits, uint, S_IRUGO);
static void serial8250_backup_timeout(unsigned long data)
{
struct ms_uart_port *up = (struct ms_uart_port *)data;
u16 iir, ier = 0, lsr;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
iir = INREG8(REG_IIR_FCR((&up->port)));
ier = INREG8(REG_DLH_IER((&up->port)));
/*
* This should be a safe test for anyone who doesn't trust the
* IIR bits on their UART, but it's specifically designed for
* the "Diva" UART used on the management processor on many HP
* ia64 and parisc boxes.
*/
lsr = INREG8(REG_LSR((&up->port)));;
if ((iir & UART_IIR_NO_INT) && (ier & UART_IER_THRI) &&
(!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) &&
(lsr & UART_LSR_THRE)) {
CLRREG8(REG_DLH_IER((&up->port)), UART_IER_THRI);
SETREG8(REG_DLH_IER((&up->port)), UART_IER_THRI);
gu32_console_bug_thre_hits++;
ms_putchar((&up->port));
}
spin_unlock_irqrestore(&up->port.lock, flags);
/* Standard timer interval plus 0.2s to keep the port running */
mod_timer(&up->timer,
jiffies + uart_poll_timeout(&up->port) + HZ / 5);
}
static s32 ms_uart_startup(struct uart_port *p)
{
/* Define Local Variables */
int rc = 0;
struct ms_uart_port *mp = (struct ms_uart_port*)p->dev->driver_data;
ms_force_rx_disable(mp->padmux, DISABLE);
ms_uart_clear_fifos(p);
/*we do not support CTS now*/
p->flags &= ~ASYNC_CTS_FLOW;
#if UART_TX_TASK
tasklet_init(&mp->xmit_tasklet, ms_do_xmit_task, (unsigned long)p);
#endif
if(mp->use_dma)
{
rc = request_irq(mp->urdma->urdma_irq, ms_uart_interrupt, IRQF_SHARED, "ms_serial_dma",p);
}
else
{
OUTREG8(REG_DLH_IER(p), 0);
INREG8(REG_LSR(p));
INREG8(REG_DLL_THR_RBR(p));
INREG8(REG_IIR_FCR(p));
INREG8(REG_MSR(p));
rc = request_irq(p->irq, ms_uart_interrupt, IRQF_SHARED, "ms_serial", p);
}
/* Print UART interrupt request status */
if (rc) {
/* UART interrupt request failed */
//UART_ERR("ms_startup(): UART%d request_irq()is failed. return code=%d\n", p->line, rc);
} else {
/* UART interrupt request passed */
//UART_ERR("ms_startup(): UART%d request_irq() is passed.\n", p->line);
}
mp->rx_guard=0;
if(mp->use_dma)
{
#if CONSOLE_DMA
unsigned char bConsole = (mp->port.membase == console_port.port.membase) ? 1 : 0;
if (bConsole)
{
console_lock();
}
#endif
URDMA_Reset(p);
URDMA_Activate(p,TRUE);
URDMA_TxInit(p);
URDMA_TxEnable(p,TRUE);
URDMA_RxInit(p);
URDMA_RxSetupTimeoutInterrupt(p,TRUE);
URDMA_RxSetupThresholdInterrupt(p,TRUE);
URDMA_RxEnable(p,TRUE);
#if CONSOLE_DMA
if (bConsole)
{
console_port.use_dma = 1;
console_unlock();
}
#endif
}
else
{
#if UART_USE_SPINLOCK
unsigned long flags;
spin_lock_irqsave(&p->lock, flags);
SETREG8(REG_DLH_IER(p), UART_IER_RDI | UART_IER_RLSI);
spin_unlock_irqrestore(&p->lock, flags);
#else
SETREG8(REG_DLH_IER(p), UART_IER_RDI | UART_IER_RLSI);
#endif
}
ms_force_rx_disable(mp->padmux, ENABLE);
#ifndef CONFIG_MS_SUPPORT_EXT_PADMUX
ms_select_pad(p, mp->padmux, mp->pad_mode);
#endif
#if MS_UART_8250_BUG_THRE
mp->bugs = UART_BUG_THRE;
#endif
/*
* [PATCH] from drivers/tty/serial/8250/8250_core.c
* The above check will only give an accurate result the first time
* the port is opened so this value needs to be preserved.
*/
if (mp->bugs & UART_BUG_THRE) {
UART_ERR("ms_startup(): enable UART_BUG_THRE\n");
init_timer(&mp->timer);
mp->timer.function = serial8250_backup_timeout;
mp->timer.data = (unsigned long)mp;
mod_timer(&mp->timer, jiffies +
uart_poll_timeout(&mp->port) + HZ / 5);
}
return rc;
}
static void ms_uart_shutdown(struct uart_port *p)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
if(p->rs485.flags&SER_RS485_ENABLED)//deinit
{
p->rs485.flags &= ~SER_RS485_ENABLED;
ms_uart_rs485_config(p, &p->rs485);
}
if(mp->use_dma)
{
URDMA_RxEnable(p,FALSE);
URDMA_TxEnable(p,FALSE);
URDMA_TxSetupTimeoutInterrupt(p,FALSE);
URDMA_TxClearInterrupt(p);
URDMA_RxSetupTimeoutInterrupt(p,FALSE);
URDMA_RxSetupThresholdInterrupt(p,FALSE);
URDMA_RxClearInterrupt(p);
URDMA_Activate(p,FALSE);
disable_irq(mp->urdma->urdma_irq);
free_irq(mp->urdma->urdma_irq, p);
}
else
{
#if UART_USE_LOOPBACK
//set loopback mode
SETREG8(REG_MCR(p), 0x10);
#endif
CLRREG8(REG_LCR(p), UART_LCR_SBC);
ms_uart_clear_fifos(p);
INREG8(REG_DLL_THR_RBR(p));
#if UART_USE_LOOPBACK
//clear loopback mode
CLRREG8(REG_MCR(p), 0x10);
#endif
//OUTREG8(REG_DLH_IER(p), 0);
disable_irq(p->irq);
free_irq(p->irq, p);
}
if (mp->bugs & UART_BUG_THRE) {
del_timer_sync(&mp->timer);
}
ms_force_rx_disable(mp->padmux, DISABLE);
}
static void ms_uart_set_termios(struct uart_port *p, struct ktermios *pTermios_st, struct ktermios *pOld_st)
{
/* Define Local Variables */
struct ms_uart_port *mp = NULL;
u8 uartflag = 0;
u16 divisor = 0;
u32 baudrate = 0;
u32 sctp_enable = 0;
//OUTREG8(REG_DLH_IER(p), 0);
if(p->dev)
{
mp=(struct ms_uart_port*)p->dev->driver_data;
if(mp->use_dma)
URDMA_Activate(p,FALSE);
ms_force_rx_disable(mp->padmux, DISABLE);
}
/*------- Configure Chararacter Size --------*/
switch (pTermios_st->c_cflag & CSIZE)
{
case CS5: /* Word length is 5 bits */
uartflag |= UART_LCR_WLEN5;
break;
case CS6: /* Word length is 6 bits */
uartflag |= UART_LCR_WLEN6;
break;
case CS7: /* Word length is 7 bits */
uartflag |= UART_LCR_WLEN7;
break;
case CS8: /* Word length is 8 bits */
uartflag |= UART_LCR_WLEN8;
break;
default:
UART_ERR("%s unsupported bits:%d\n", __FUNCTION__, pTermios_st->c_cflag & CSIZE);
break;
}
/*------------ Configure Stop bit -----------*/
if (pTermios_st->c_cflag & CSTOPB)
{
/* Stop length is 1.5 bits (5-bit char), 2 bits (otherwise) */
uartflag |= UART_LCR_STOP2;
}
else
{
/* Stop length is 1 bit */
uartflag |= UART_LCR_STOP1;
}
/*----------- Configure Parity --------------*/
if (pTermios_st->c_cflag & PARENB)
{
/* Parity Enable */
uartflag |= UART_LCR_PARITY_EN;
/* Set Odd/Even Parity */
if (pTermios_st->c_cflag & PARODD)
{
/* Odd Parity */
uartflag &= (~UART_LCR_PARITY_SEL);
}
else
{
/* Even Parity */
uartflag |= UART_LCR_PARITY_SEL;
}
}
else
{
/* Parity Disable */
uartflag &= (~UART_LCR_PARITY_EN);
}
OUTREG8(REG_LCR(p), uartflag);
//NOTE: we are going to set LCR, be carefully here
baudrate = uart_get_baud_rate(p, pTermios_st, pOld_st, 0, 115200 * 14);
divisor = ms_uart_set_clk(p, baudrate);
if(divisor)
ms_uart_set_divisor(p, divisor);
OUTREG8(REG_IIR_FCR(p), UART_FCR_FIFO_ENABLE | UART_FCR_TRIGGER_TX_L0 | UART_FCR_TRIGGER_RX_L0);
INREG8(REG_DLL_THR_RBR(p));
if(p->dev != NULL){
of_property_read_u32(p->dev->of_node, "sctp_enable", &sctp_enable);
if(sctp_enable == 1)
{
if(pTermios_st->c_cflag & CRTSCTS)
{
//rts cts enable
OUTREG8(REG_MCR(p), UART_MCR_AFCE | UART_MCR_RTS);
OUTREG8(REG_IIR_FCR(p), UART_FCR_FIFO_ENABLE | UART_FCR_TRIGGER_TX_L0 | UART_FCR_TRIGGER_RX_L3);
}
else
{
//rts cts disable
OUTREG8(REG_MCR(p), INREG8(REG_MCR(p)) & ~ (UART_LCR_DLAB| UART_MCR_RTS ) );
}
}
}
if(p->dev)
{
if(mp->use_dma)
{
URDMA_Reset(p);
URDMA_Activate(p,TRUE);
URDMA_TxInit(p);
URDMA_TxEnable(p,TRUE);
URDMA_RxInit(p);
URDMA_RxSetupTimeoutInterrupt(p,TRUE);
URDMA_RxSetupThresholdInterrupt(p,TRUE);
URDMA_RxEnable(p,TRUE);
}
ms_force_rx_disable(mp->padmux, ENABLE);
}
}
#ifdef CONFIG_PM
static s32 ms_uart_suspend(struct platform_device *pdev, pm_message_t state)
{
struct ms_uart_port *mp=platform_get_drvdata(pdev);
struct uart_port *p=&mp->port;
UART_DBG("[%s] uart%d\n", __func__, p->line);
if(mp->use_dma)
{
URDMA_RxEnable(p,FALSE);
URDMA_TxEnable(p,FALSE);
URDMA_TxSetupTimeoutInterrupt(p,FALSE);
URDMA_TxClearInterrupt(p);
URDMA_RxSetupTimeoutInterrupt(p,FALSE);
URDMA_RxSetupThresholdInterrupt(p,FALSE);
URDMA_RxClearInterrupt(p);
URDMA_Activate(p,FALSE);
}
uart_suspend_port(&ms_uart_driver, p);
mp->backupIER = INREG8(REG_DLH_IER(p));
mp->backupLCR = INREG8(REG_LCR(p));
mp->backupMCR = INREG8(REG_MCR(p));
OUTREG8(REG_DLH_IER(p), 0);
ms_uart_clear_fifos(p);
OUTREG8(REG_LCR(p) , mp->backupLCR | UART_LCR_DLAB);
mp->backupDivisor = (INREG8(REG_DLH_IER(p)) << 8);
mp->backupDivisor |= (INREG8(REG_DLL_THR_RBR(p)) & 0xFF);
if (!IS_ERR(mp->clk))
{
clk_disable_unprepare(mp->clk);
#if 0
clk_put(mp->clk);
#endif
}
return 0;
}
static s32 ms_uart_resume(struct platform_device *pdev)
{
struct ms_uart_port *mp=platform_get_drvdata(pdev);
struct uart_port *p=&mp->port;
if (!IS_ERR(mp->clk))
clk_prepare_enable(mp->clk);
ms_uart_set_divisor(p, mp->backupDivisor);
OUTREG8(REG_MCR(p), mp->backupMCR);
OUTREG8(REG_LCR(p), mp->backupLCR);
OUTREG8(REG_DLH_IER(p), mp->backupIER);
if(mp->use_dma)
{
URDMA_Reset(p);
URDMA_Activate(p,TRUE);
URDMA_TxInit(p);
URDMA_TxEnable(p,TRUE);
URDMA_RxInit(p);
URDMA_RxSetupTimeoutInterrupt(p,TRUE);
URDMA_RxSetupThresholdInterrupt(p,TRUE);
URDMA_RxEnable(p,TRUE);
}
uart_resume_port(&ms_uart_driver, &mp->port);
UART_DBG("[%s] uart%d\n", __func__, p->line);
return 0;
}
#endif
static s32 ms_uart_remove(struct platform_device *pdev)
{
struct ms_uart_port *mp=platform_get_drvdata(pdev);
uart_remove_one_port(&ms_uart_driver,&mp->port);
if(mp->use_dma)
{
dma_free_coherent(&pdev->dev, PAGE_ALIGN(UR2DMA_RX_BUF_LENGTH), &mp->urdma->rx_urdma_base, GFP_KERNEL);
dma_free_coherent(&pdev->dev, PAGE_ALIGN(UR2DMA_TX_BUF_LENGTH), &mp->urdma->tx_urdma_base, GFP_KERNEL);
CamOsThreadStop(mp->urdma_task);
}
if (!IS_ERR(mp->clk))
{
clk_disable_unprepare(mp->clk);
clk_put(mp->clk);
}
return 0;
}
static s32 ms_uart_probe(struct platform_device *pdev)
{
int ret = 0;
struct ms_uart_port *mp;
struct ms_urdma *urdma;
struct resource *res;
int tx_pad;
CamOsThreadAttrb_t tAttr = {0};
if(!pdev)
{
// UART_ERR("ms_uart_probe() parameter pdev is NULL\n");
return -ENOMEM;
}
mp = devm_kzalloc(&pdev->dev, sizeof(*mp), GFP_KERNEL);
if (!mp)
return -ENOMEM;
spin_lock_init(&mp->port.lock);
mp->port.line = of_alias_get_id(pdev->dev.of_node, "serial");
/*if (mp->port.line < 0) {
UART_ERR("[%s] failed to get alias/pdev id = %d\n", __func__, mp->port.line);
return -EINVAL;
}*/
pdev->id=mp->port.line;
mp->clk = of_clk_get(pdev->dev.of_node, 0);
if(IS_ERR(mp->clk))
{
//UART_ERR("[%s] of_clk_get failed\n", __func__);
return -EINVAL;
}
//enable clk in probe, because if UART no clk, it can not be accessed.
clk_prepare_enable(mp->clk);
mp->port.uartclk = clk_get_rate(mp->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
// UART_ERR("no memory resource defined\n");
ret = -ENODEV;
goto out;
}
mp->port.membase = (void *)res->start;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
// UART_ERR("no irq resource defined\n");
ret = -ENODEV;
goto out;
}
mp->port.irq = res->start;
of_property_read_u32(pdev->dev.of_node, "dma", &mp->use_dma);
if (mp->use_dma)
{
#if CONSOLE_DMA
unsigned char bConsole = (mp->port.membase == console_port.port.membase) ? 1 : 0;
if (bConsole)
{
console_lock();
}
#endif
mp->urdma = devm_kzalloc(&pdev->dev, sizeof(*urdma), GFP_KERNEL);
if (!mp->urdma)
{
mp->use_dma=0;
goto dma_err;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res == NULL)
{
mp->use_dma = 0;
// UART_ERR("no urdma memory resource defined...\n");
goto dma_err;
}
mp->urdma->reg_base = (reg_urdma *)IO_ADDRESS(res->start);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (res == NULL)
{
mp->use_dma = 0;
// UART_ERR("no urdma irq resource defined\n");
goto dma_err;
}
mp->urdma->urdma_irq = res->start;
mp->port.irq = res->start;
mp->urdma->rx_buf = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(UR2DMA_RX_BUF_LENGTH),
&mp->urdma->rx_urdma_base, GFP_KERNEL);
mp->urdma->tx_buf = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(UR2DMA_TX_BUF_LENGTH),
&mp->urdma->tx_urdma_base, GFP_KERNEL);
if(!mp->urdma->rx_buf || !mp->urdma->tx_buf)
{
mp->use_dma = 0;
// UART_ERR("Allocate urdma rx_buffer/tx_buffer failed, use UART mode\n");
goto dma_err;
}
mp->urdma->rx_urdma_size = PAGE_ALIGN(UR2DMA_RX_BUF_LENGTH);
mp->urdma->tx_urdma_size = PAGE_ALIGN(UR2DMA_TX_BUF_LENGTH);
UART_DBG("[%s] URDMA mode enable, reg_base=0x%08X, irq=%d\n", __func__, (unsigned int)(mp->urdma->reg_base), mp->urdma->urdma_irq);
// UART_ERR("URDMA rx_buf=0x%08X(phy:0x%08X) tx_buf=0x%08X(phy:0x%08X) size=0x%X\n", (unsigned int)mp->urdma->rx_buf, (unsigned int)mp->urdma->rx_urdma_base, (unsigned int)mp->urdma->tx_buf, (unsigned int)mp->urdma->tx_urdma_base, mp->urdma->rx_urdma_size);
#if CONSOLE_DMA
dma_err:
if (bConsole)
{
// console_port.use_dma = mp->use_dma;
if ((!mp->use_dma) && (mp->urdma))
{
devm_kfree(&pdev->dev, mp->urdma);
mp->urdma = NULL;
}
console_port.urdma= mp->urdma;
console_unlock();
}
#endif
tAttr.nPriority = 50;
tAttr.nStackSize = 0;
tAttr.szName = "urdma_tx_thread";
CamOsThreadCreate(&mp->urdma_task, &tAttr, (void *)urdma_tx_thread, (void *)&mp->port);
}
#if !CONSOLE_DMA
dma_err:
#endif
mp->port.type = PORT_8250;
mp->port.dev = &pdev->dev;
mp->port.ops=&ms_uart_ops;
mp->port.regshift = 0;
mp->port.fifosize = 16;
mp->port.timeout =HZ;
mp->port.iotype=UPIO_MEM;
mp->port.rs485_config = ms_uart_rs485_config;
UART_DBG("[%s] line=%d name=%s\n", __func__, mp->port.line, pdev->name);
//[2016.11.15] Add padmux select from DTB by Spade
if(of_property_read_u32(pdev->dev.of_node, "pad", &tx_pad)) //read property failed
{
//set default pad
if(mp->port.line==0)
{
// improve boot-up speed, remove print log
UART_DBG("[%s] uart port %d use %s\n", __func__, mp->port.line, "MUX_PM_UART");
//UART_ERR("[%s] uart port %d use %s\n", __func__, mp->port.line, "MUX_PM_UART");
mp->padmux=MUX_PM_UART;
}
else if(mp->port.line==1)
{
// improve boot-up speed, remove print log
UART_DBG("[%s] uart port %d use %s\n", __func__, mp->port.line, "MUX_UART1");
//UART_ERR("[%s] uart port %d use %s\n", __func__, mp->port.line, "MUX_UART1");
mp->padmux=MUX_UART1;
}
else if(mp->port.line==2)
{
// improve boot-up speed, remove print log
UART_DBG("[%s] uart port %d use %s\n", __func__, mp->port.line, "MUX_FUART");
//UART_ERR("[%s] uart port %d use %s\n", __func__, mp->port.line, "MUX_FUART");
mp->padmux=MUX_FUART;
}
#ifdef CONFIG_MS_SUPPORT_UART2
else if(mp->port.line==3)
{
// improve boot-up speed, remove print log
UART_DBG("[%s] uart port %d use %s\n", __func__, mp->port.line, "MUX_UART2");
//UART_ERR("[%s] uart port %d use %s\n", __func__, mp->port.line, "MUX_UART2");
mp->padmux=MUX_UART2;
}
#endif
else
{
// improve boot-up speed, remove print log
UART_DBG("[%s] port line %d is not supported\n", __func__, mp->port.line);
//UART_ERR("[%s] port line %d is not supported\n", __func__, mp->port.line);
ret = -EINVAL;
goto out;
}
}
else //read property successfully
{
if (ms_uart_get_padmux(tx_pad, &(mp->padmux), &(mp->pad_mode)) != 0)
{
// UART_ERR("[%s] Use undefined pad number %d\n", __func__, tx_pad);
ret = -EINVAL;
goto out;
}
// improve boot-up speed, remove print log
#ifdef CONFIG_MS_SUPPORT_UART2
UART_DBG("[%s] uart port %d use %s\n", __func__, mp->port.line, (mp->padmux==MUX_FUART)?"MUX_FUART":(mp->padmux==MUX_UART0)?"MUX_UART0":(mp->padmux==MUX_UART1)?"MUX_UART1":"MUX_UART2");
//UART_ERR("[%s] uart port %d use %s\n", __func__, mp->port.line, (mp->padmux==MUX_FUART)?"MUX_FUART":(mp->padmux==MUX_UART0)?"MUX_UART0":(mp->padmux==MUX_UART1)?"MUX_UART1":"MUX_UART2");
#else
UART_DBG("[%s] uart port %d use %s\n", __func__, mp->port.line, (mp->padmux==MUX_FUART)?"MUX_FUART":(mp->padmux==MUX_UART0)?"MUX_UART0":"MUX_UART1");
//UART_ERR("[%s] uart port %d use %s\n", __func__, mp->port.line, (mp->padmux==MUX_FUART)?"MUX_FUART":(mp->padmux==MUX_UART0)?"MUX_UART0":"MUX_UART1");
#endif
}
platform_set_drvdata(pdev, mp);
ret = uart_add_one_port(&ms_uart_driver, &mp->port);
if (ret != 0)
goto out;
return 0;
out:
// UART_ERR("[UART%d]: failure [%s]: %d\n", mp->port.line, __func__, ret);
clk_disable_unprepare(mp->clk);
clk_put(mp->clk);
if(mp->use_dma)
{
CamOsThreadStop(mp->urdma_task);
}
return ret;
}
static const struct of_device_id ms_uart_of_match_table[] = {
{ .compatible = "sstar,uart" },
{}
};
MODULE_DEVICE_TABLE(of, ms_uart_of_match_table);
static struct platform_driver ms_uart_platform_driver = {
.remove = ms_uart_remove,
.probe = ms_uart_probe,
#ifdef CONFIG_PM
.suspend = ms_uart_suspend,
.resume = ms_uart_resume,
#endif
.driver = {
.name = "ms_uart",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ms_uart_of_match_table),
},
};
static s32 __init ms_uart_module_init(void)
{
int ret;
ret = uart_register_driver(&ms_uart_driver);
if (ret != 0)
return ret;
ret = platform_driver_register(&ms_uart_platform_driver);
if (ret != 0)
{
// UART_ERR("[ms_uart]platform_driver_register failed!!\n");
uart_unregister_driver(&ms_uart_driver);
}
return ret;
}
static void __exit ms_uart_module_exit(void)
{
platform_driver_unregister(&ms_uart_platform_driver);
uart_unregister_driver(&ms_uart_driver);
}
module_init(ms_uart_module_init);
module_exit(ms_uart_module_exit);
static int __init ms_early_console_init(void)
{
#if !CONSOLE_DMA
struct device_node *console_np;
struct resource res;
console_np=of_find_node_by_path("console");
if(!console_np)
return -ENODEV;
BUG_ON( of_address_to_resource(console_np,0,&res) );
console_port.port.membase = (void *)res.start;
console_port.port.type = PORT_8250;
console_port.port.ops=&ms_uart_ops;
console_port.port.regshift = 0;
console_port.port.fifosize = 16;
console_port.port.line=0;
console_port.port.cons=&ms_uart_console;
ms_uart_add_console_port(&console_port);
#endif
register_console(&ms_uart_console);
return 0;
}
console_initcall(ms_early_console_init);
void URDMA_Reset(struct uart_port *p)
{
unsigned int i=0;
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->urdma_mode=1;
mp->urdma->reg_base->tx_urdma_en = 0;
mp->urdma->reg_base->rx_urdma_en = 0;
mp->urdma->reg_base->tx_intr_en = 0;
mp->urdma->reg_base->rx_intr1_en = 0;
mp->urdma->reg_base->rx_intr2_en = 0;
/* clear interrupt status */
mp->urdma->reg_base->tx_intr_clr = 1;
mp->urdma->reg_base->rx_intr_clr = 1;
/* software reset */
mp->urdma->reg_base->sw_rst = 1;
/* make sure rx_busy is off */
for(i=0; (mp->urdma->reg_base->rx_busy || mp->urdma->reg_base->tx_busy); i++)
{
if(0xFFFF == i)
{
break;
}
}
#if !CONSOLE_DMA
mdelay(10);
#endif
mp->urdma->reg_base->sw_rst = 0;
mp->urdma->reg_base->urdma_mode=0;
}
void URDMA_Activate(struct uart_port *p,BOOL bEnable)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->urdma_mode=bEnable?TRUE:FALSE;
UART_DBG("URDMA_Activate: %d\n", bEnable);
}
void URDMA_TxEnable(struct uart_port *p,BOOL bEnable)
{
unsigned int i=0;
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
if(bEnable)
{
mp->urdma->reg_base->tx_urdma_en = 1;
}
else
{
mp->urdma->reg_base->tx_urdma_en = 0;
for(i=0; (mp->urdma->reg_base->tx_busy); i++)
{
if(0xFFFF == i)
{
return;
}
}
}
}
void URDMA_RxEnable(struct uart_port *p,BOOL bEnable)
{
unsigned int i=0;
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
if(bEnable)
{
mp->urdma->reg_base->rx_urdma_en = 1;
}
else
{
mp->urdma->reg_base->rx_urdma_en = 0;
for(i=0; (mp->urdma->reg_base->rx_busy); i++)
{
if(0xFFFF == i)
{
return;
}
}
}
}
U8 URDMA_GetInterruptStatus(struct uart_port *p)
{
U8 stat=0;
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
if(mp->urdma->reg_base->rx_mcu_intr)
{
stat|=URDMA_INTR_STATUS_RX;
}
if(mp->urdma->reg_base->tx_mcu_intr)
{
stat|=URDMA_INTR_STATUS_TX;
}
return stat;
}
void URDMA_TxInit(struct uart_port *p)
{
unsigned int i=0;
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->tx_urdma_en = 0; /* disable dma */
mp->urdma->reg_base->tx_sw_rst = 1;
for(i=0; (mp->urdma->reg_base->tx_busy); i++)
{
if(0xFFFF == i)
{
return;
}
}
mp->urdma->reg_base->tx_sw_rst = 0;
mp->urdma->reg_base->tx_buf_base_h = URDMA_HIU16((U32)mp->urdma->tx_urdma_base);
mp->urdma->reg_base->tx_buf_base_l = URDMA_LOU16((U32)mp->urdma->tx_urdma_base);
mp->urdma->reg_base->tx_buf_size = (mp->urdma->tx_urdma_size/8);
mp->urdma->reg_base->tx_timeout = URDMA_TX_TIMEOUT;
mp->urdma->reg_base->tx_buf_wptr = 0x0;
//2015.10.21 Refine: initialize tx_buf_wptr to 1 because HW behavior is not correct from 0 to 1
//2016.11.27 Refine: remove initial value for tx_buf_wptr
//mp->urdma->reg_base->tx_buf_wptr = 0x1;
}
void URDMA_RxInit(struct uart_port *p)
{
unsigned int i=0;
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->rx_urdma_en = 0; /* disable dma */
mp->urdma->reg_base->rx_sw_rst = 1;
for(i=0; (mp->urdma->reg_base->rx_busy); i++)
{
if(0xFFFF == i)
{
return;
}
}
mp->urdma->reg_base->rx_sw_rst=0;
mp->urdma->reg_base->rx_buf_base_h = URDMA_HIU16((U32)mp->urdma->rx_urdma_base);
mp->urdma->reg_base->rx_buf_base_l = URDMA_LOU16((U32)mp->urdma->rx_urdma_base);
mp->urdma->reg_base->rx_buf_size = (mp->urdma->rx_urdma_size/8);
mp->urdma->reg_base->intr_threshold = URDMA_RX_INTR_LEVEL;
mp->urdma->reg_base->rx_timeout = URDMA_RX_TIMEOUT; /* receive timeout. */
//need to clear buffer?
//memset(dma_rx_buf[fuartNum].Buffer,0,dma_rx_buf[fuartNum].Length);
//2016.11.27 Refine: sw_rx_rptr is the index of rx_buf we have read
//Give initial value (size-1) according to HW behavior
mp->urdma->sw_rx_rptr = mp->urdma->rx_urdma_size - 1;
}
void URDMA_TxSetupTimeoutInterrupt(struct uart_port *p,BOOL bEnable)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->tx_intr_en = bEnable?1:0;
}
void URDMA_TxClearInterrupt(struct uart_port *p)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->tx_intr_clr = 1; /* clear int status */
}
void URDMA_RxSetupTimeoutInterrupt(struct uart_port *p,BOOL bEnableTimeout)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->rx_intr1_en=bEnableTimeout?1:0;
}
void URDMA_RxSetupThresholdInterrupt(struct uart_port *p,BOOL bEnableThreshold)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->rx_intr2_en=bEnableThreshold?1:0;
}
U8 URDMA_RxGetInterrupt(struct uart_port *p)
{
U8 intr=0;
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
if(mp->urdma->reg_base->rx_intr1)
{
intr |= URDMA_RX_INTR_TYPE_TIMEOUT;
}
if(mp->urdma->reg_base->rx_intr2)
{
intr |= URDMA_RX_INTR_TYPE_THRESHOLD;
}
return intr;
}
void URDMA_RxClearInterrupt(struct uart_port *p)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
mp->urdma->reg_base->rx_intr_clr = 1; /* clear int status */
}
/*
U32 URDMA_GetWritableSize(struct uart_port *p)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
return (U32)((mp->urdma->reg_base->tx_buf_rptr - mp->urdma->reg_base->tx_buf_wptr - 1) & (mp->urdma->rx_urdma_size - 1));
}
*/
#if CONSOLE_DMA
static void *urdma_tx_thread(void *arg)
{
struct uart_port *p = (struct uart_port *)arg;
struct circ_buf *xmit;
while(1){
wait_event_interruptible(urdma_wait, urdma_conditions);
urdma_conditions = 0;
xmit = &p->state->xmit;
if (uart_circ_empty(xmit) || uart_tx_stopped(p))
{
ms_uart_stop_tx(p);
}
if (uart_circ_chars_pending(xmit))
{
URDMA_StartTx(p);
}else
{
uart_write_wakeup(p);
}
if (CAM_OS_OK == CamOsThreadShouldStop())
break;
}
return 0;
}
static int _urdma_tx(struct ms_uart_port *mp, unsigned char* buf, int buf_size)
{
int complete_size = 0;
int size_cp;
U16 sw_tx_wptr = mp->urdma->reg_base->tx_buf_wptr;
U16 sw_tx_rptr = mp->urdma->reg_base->tx_buf_rptr;
unsigned long flags1;
spin_lock_irqsave(&mutex_console_2_dma, flags1);
if(mp->urdma->reg_base->tx_buf_wptr == 0 && mp->urdma->reg_base->tx_buf_rptr == 0){
sw_tx_wptr = (sw_tx_wptr) & UR2DMA_TX_BUF_MASK;
}
else
{
sw_tx_wptr = (sw_tx_wptr +1) & UR2DMA_TX_BUF_MASK;
}
if (sw_tx_wptr >= sw_tx_rptr)
{
size_cp = min_t(int, (mp->urdma->tx_urdma_size - sw_tx_wptr), buf_size);
if (size_cp)
{
memcpy((void *)(&mp->urdma->tx_buf[sw_tx_wptr]), buf, size_cp);
buf += size_cp;
buf_size -= size_cp;
complete_size += size_cp;
sw_tx_wptr = (sw_tx_wptr + size_cp) & UR2DMA_TX_BUF_MASK;
}
}
if (buf_size)
{
size_cp = min_t(int, (sw_tx_rptr - sw_tx_wptr), buf_size);
if (size_cp)
{
memcpy((void *)(&mp->urdma->tx_buf[sw_tx_wptr]), buf, size_cp);
complete_size += size_cp;
}
}
if (complete_size)
{
Chip_Flush_MIU_Pipe();
if(mp->urdma->reg_base->tx_buf_wptr == 0 && mp->urdma->reg_base->tx_buf_rptr == 0)
{
mp->urdma->reg_base->tx_buf_wptr = ((mp->urdma->reg_base->tx_buf_wptr + complete_size -1 ) & UR2DMA_TX_BUF_MASK);
}
else
{
mp->urdma->reg_base->tx_buf_wptr = ((mp->urdma->reg_base->tx_buf_wptr + complete_size) & UR2DMA_TX_BUF_MASK);
}
}
spin_unlock_irqrestore(&mutex_console_2_dma, flags1);
return complete_size;
}
void URDMA_StartTx(struct uart_port *p)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
struct circ_buf *xmit = &p->state->xmit;
U16 circ_buf_out_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
int complete_size = 0;
U16 sw_tx_wptr = mp->urdma->reg_base->tx_buf_wptr;
U16 sw_tx_rptr = mp->urdma->reg_base->tx_buf_rptr;
int space = (sw_tx_rptr > sw_tx_wptr) ? (sw_tx_rptr - sw_tx_wptr) : (mp->urdma->tx_urdma_size - sw_tx_wptr + sw_tx_rptr);
if (circ_buf_out_size && (space > circ_buf_out_size))
{
complete_size = _urdma_tx(mp, &xmit->buf[xmit->tail], circ_buf_out_size);
p->icount.tx += complete_size;
xmit->tail = (xmit->tail + complete_size) & (UART_XMIT_SIZE - 1);
}
return;
}
#else // #if CONSOLE_DMA
void URDMA_StartTx(struct uart_port *p)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
struct circ_buf *xmit = &p->state->xmit;
U16 circ_buf_out_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
U16 tx_buf_space, tx_end;
/* tx_buf_wptr point to the byte have been written to buffer */
/* tx_buf_rptr point to the byte have been sent */
/* if tx_buf_rptr meets tx_buf_wptr, it means all written bytes in buffer have been sent */
/* In this case, the space equal to buffer size */
U16 sw_tx_wptr = mp->urdma->reg_base->tx_buf_wptr;
U16 sw_tx_rptr = mp->urdma->reg_base->tx_buf_rptr;
if(sw_tx_wptr == (mp->urdma->tx_urdma_size-1)) //wptr meet the end of buffer, start from (0), end at (rptr)
{
sw_tx_wptr = 0;
tx_buf_space = sw_tx_rptr;
}
else if(sw_tx_wptr >= sw_tx_rptr) //start from (wptr+1), end at (buffer_size-1)
{
sw_tx_wptr+=1;
tx_buf_space = mp->urdma->tx_urdma_size - sw_tx_wptr;
}
else //start from (wptr+1), end at (rptr)
{
sw_tx_wptr+=1;
tx_buf_space = sw_tx_rptr - sw_tx_wptr;
}
//pr_debug("sw_wp=%4x, sw_rp=%4x, tx_space=%4x, circ_buf_out_size=%4x, head=%4x, tail=%4x\n", sw_tx_wptr, sw_tx_rptr, tx_space, circ_buf_out_size, xmit->head, xmit->tail);
if(circ_buf_out_size > tx_buf_space) //tx_cnt > tx_space
{
memcpy((void *)(&mp->urdma->tx_buf[sw_tx_wptr]), &xmit->buf[xmit->tail], tx_buf_space);
p->icount.tx += tx_buf_space;
xmit->tail += tx_buf_space;
circ_buf_out_size -= tx_buf_space;
//now we can start write from (0), end at (min[sw_tx_rptr,circ_buf_out_size])
tx_end = circ_buf_out_size >= sw_tx_rptr ? sw_tx_rptr : circ_buf_out_size;
memcpy((void *)(&mp->urdma->tx_buf[0]), &xmit->buf[xmit->tail], tx_end);
Chip_Flush_MIU_Pipe();
p->icount.tx += tx_end;
xmit->tail = (xmit->tail + tx_end) & (UART_XMIT_SIZE - 1);
mp->urdma->reg_base->tx_buf_wptr = tx_end;
}
else //tx_cnt <= tx_space
{
memcpy((void *)(&mp->urdma->tx_buf[sw_tx_wptr]),&xmit->buf[xmit->tail], circ_buf_out_size);
Chip_Flush_MIU_Pipe();
p->icount.tx += circ_buf_out_size;
xmit->tail = (xmit->tail + circ_buf_out_size) & (UART_XMIT_SIZE - 1);
//mp->urdma->reg_base->tx_buf_wptr += circ_buf_out_size;
if(mp->urdma->reg_base->tx_buf_wptr == mp->urdma->tx_urdma_size-1)
mp->urdma->reg_base->tx_buf_wptr = circ_buf_out_size -1;
else
mp->urdma->reg_base->tx_buf_wptr += circ_buf_out_size;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(p);
if (uart_circ_empty(xmit) || uart_tx_stopped(p))
{
ms_uart_stop_tx(p);
}
return;
}
#endif
void URDMA_StartRx(struct uart_port *p)
{
struct ms_uart_port *mp=(struct ms_uart_port*)p->dev->driver_data;
U16 sw_rx_wptr = mp->urdma->reg_base->rx_buf_wptr;
U16 sw_rx_rptr = mp->urdma->sw_rx_rptr;
U16 in_size = (sw_rx_wptr - sw_rx_rptr) & (mp->urdma->rx_urdma_size - 1); //read from (sw_rx_rptr+1), end at (sw_rx_wptr)
U16 rptr_to_end = mp->urdma->rx_urdma_size - sw_rx_rptr - 1;
/* sw_rx_wptr point to the byte already read from UART */
/* sw_rx_rptr point to the byte have been read */
/* if sw_rx_rptr equal to sw_rx_wptr, it means all bytes in buffer have been read */
if(sw_rx_rptr==mp->urdma->rx_urdma_size - 1) //initial case
{
tty_insert_flip_string(&p->state->port, &mp->urdma->rx_buf[0], in_size - rptr_to_end);
p->icount.rx += in_size - rptr_to_end;
//update global sw_rx_rptr
mp->urdma->sw_rx_rptr = in_size - rptr_to_end -1;
tty_flip_buffer_push(&p->state->port);
pr_debug("(0) sw_rx_rptr=0x%4x, in_size=0x%4x\n", mp->urdma->sw_rx_rptr, in_size);
}
else if(in_size > rptr_to_end)
{
tty_insert_flip_string(&p->state->port, &mp->urdma->rx_buf[sw_rx_rptr+1], rptr_to_end);
tty_insert_flip_string(&p->state->port, &mp->urdma->rx_buf[0], in_size - rptr_to_end);
p->icount.rx += in_size;
//update global sw_rx_rptr
mp->urdma->sw_rx_rptr = (in_size - rptr_to_end - 1);
tty_flip_buffer_push(&p->state->port);
pr_debug("(1) sw_rx_rptr=0x%4x, in_size=0x%4x\n", mp->urdma->sw_rx_rptr, in_size);
}
else
{
tty_insert_flip_string(&p->state->port, &mp->urdma->rx_buf[(sw_rx_rptr+1)&(mp->urdma->rx_urdma_size-1)], in_size);
p->icount.rx += in_size;
//update global sw_rx_rptr
mp->urdma->sw_rx_rptr += in_size & (mp->urdma->rx_urdma_size - 1); //avoid sw_rx_rptr overflow
tty_flip_buffer_push(&p->state->port);
pr_debug("(2) sw_rx_rptr=0x%4x, in_size=0x%4x\n", mp->urdma->sw_rx_rptr, in_size);
}
return;
}