代码拉取完成,页面将自动刷新
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <mach/platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <mach/sys_config.h>
#include <mach/gpio.h>
#include <mach/irqs.h>
#include <asm/io.h>
#include "ch438.h"
#define CH438_UART_NAME "usart"
#define CH438_DEV_NAME "ttySC"
#define CH438_DEV_NUM 8
#define CH438_CLOCK_RATE (22118400/12)
/* debug control */
enum {
DBG_ERR = 1U << 0,
DBG_DBG = 1U << 1,
};
static u32 ch438_debug_mask = 0;
#define dprintk(level, fmt, arg...) \
do { \
if (unlikely(ch438_debug_mask & level)) { \
printk("%s()%d - ", __func__, __LINE__); \
printk(fmt, ##arg); \
} \
} while (0)
#define SERIAL_ERR(fmt, arg...) dprintk(DBG_ERR, fmt, ##arg)
#define SERIAL_DBG(fmt, arg...) dprintk(DBG_DBG, fmt, ##arg)
#define UART_TO_SPORT(port) ((struct ch438_port*)port)
static struct ch438_pin ch438_pin;
static spinlock_t ch438_rwlock;
static unsigned long ch438_mapbase=0;
static unsigned char __iomem *ch438_membase=NULL; /*read/write[bwl]*/
static const u8 REG[] = {0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38};
/* NO CONFIG_SERIAL_SUNXI_CONSOLE, this device don't as console! */
static struct platform_device ch438_device[CH438_DEV_NUM];
static struct ch438_port ch438_ports[CH438_DEV_NUM];
/* Access macros for the CH438 UART */
#define UART_GET_CHR(p) RdReg(REG[p->line]|RBR_RO)
#define UART_PUT_CHR(p, c) WrReg(REG[p->line]|THR_WO, c)
#define UART_GET_IER(p) RdReg(REG[p->line]|IER_RW)
#define UART_PUT_IER(p, c) WrReg(REG[p->line]|IER_RW, c)
#define UART_GET_IIR(p) RdReg(REG[p->line]|IIR_RO)
#define UART_GET_FCR(p) RdReg(REG[p->line]|FCR_WO)
#define UART_PUT_FCR(p, c) WrReg(REG[p->line]|FCR_WO, c)
#define UART_GET_MSR(p) RdReg(REG[p->line]|MSR_RO)
#define UART_GET_LSR(p) RdReg(REG[p->line]|LSR_RO)
#define UART_GET_LCR(p) RdReg(REG[p->line]|LCR_RW)
#define UART_PUT_LCR(p, c) WrReg(REG[p->line]|LCR_RW, c)
#define UART_GET_MCR(p) RdReg(REG[p->line]|MCR_RW)
#define UART_PUT_MCR(p, c) WrReg(REG[p->line]|MCR_RW, c)
#define UART_GET_SCR(p) RdReg(REG[p->line]|SCR_RW)
#define UART_PUT_SCR(p, c) WrReg(REG[p->line]|SCR_RW, c)
#define UART_PUT_DLH(p, c) WrReg(REG[p->line]|DLH_RW, c)
#define UART_PUT_DLL(p, c) WrReg(REG[p->line]|DLL_RW, c)
#define UART_GET_SSR() RdReg(SSR_RO)
static void ch438_gpio_init(void)
{
struct ch438_pin *pin = &ch438_pin;
script_item_u val;
char *para = "ch438"; /* see as sys_config.fex */
int ret=0;
int reg = 0;
SERIAL_DBG("ch438 gpio init.\n");
__raw_writel(0x55555555, CH438_PULL0); /* all pull up */
__raw_writel(0x00000005, CH438_PULL1); /* all pull up */
__raw_writel(0x11111111, CH438_CFG0); /* PE7~PE0: output */
__raw_writel(0x11111111, CH438_CFG1); /* WR[15] ADDR[14~8] output */
__raw_writel(0x00000011, CH438_CFG2); /* CS[17], RD[16] */
/* ch438 RST: PF01 */
script_get_item(para, "ch438_rst", &val);
pin->rst = val.gpio.gpio;
ret=gpio_request(pin->rst, NULL);
gpio_direction_output(pin->rst, 1);
/* ch438 INT: PB06 */
script_get_item(para, "ch438_int", &val);
pin->pint = val.gpio.gpio;
ret=gpio_request(pin->pint, NULL);
gpio_direction_input(pin->pint);
/* Allocate the IRQ */
pin->irqnum = gpio_to_irq(pin->pint);
/* hw reset ch438 */
SERIAL_DBG("ch438 chip hw reset!\n");
gpio_set_value(pin->rst, 1);
udelay(10);
gpio_set_value(pin->rst, 0);
udelay(100);
gpio_set_value(pin->rst, 1);
reg = __raw_readl(CH438_DAT);
/* Reset default: CS=1, WR=1, RD=1 */
__raw_writel(reg|(1<<CH438_CS)|(1<<CH438_WR)|(1<<CH438_RD), CH438_DAT);
}
static void ch438_gpio_free(void)
{
struct ch438_pin *pin = &ch438_pin;
gpio_free(pin->rst);
gpio_free(pin->pint);
}
static void WrReg(unsigned char addr,unsigned char dat)
{
unsigned long flags;
int reg;
spin_lock_irqsave(&ch438_rwlock, flags);
/* addr output */
__raw_writel(0x11111111, CH438_CFG0);
ndelay(20);
/* CS=0, RD=1, WR=0, 00... */
reg = 0x00010000;
reg |= (unsigned int)addr<<8;
/* data port */
reg |= dat;
__raw_writel(reg, CH438_DAT);
ndelay(20);
/* set CS,WR as 0 */
reg &= ~((1<<CH438_CS)|(1<<CH438_WR));
__raw_writel(reg, CH438_DAT);
ndelay(20);
//set CS,WR as 1
reg |= (1<<CH438_CS)|(1<<CH438_WR);
__raw_writel(reg, CH438_DAT);
ndelay(20);
spin_unlock_irqrestore(&ch438_rwlock, flags);
}
static unsigned char RdReg(unsigned char addr)
{
unsigned long flags;
unsigned char value=0;
int reg;
spin_lock_irqsave(&ch438_rwlock, flags);
/* addr input */
__raw_writel(0x00000000, CH438_CFG0);
ndelay(20);
/* CS=1,WR=1,RD=1, addr */
reg = 0x00038000 | (unsigned int)addr<<8;
__raw_writel(reg, CH438_DAT);
ndelay(20);
/* CS=0,RD=0 */
reg &= ~((1<<CH438_CS)|(1<<CH438_RD));
__raw_writel(reg, CH438_DAT);
ndelay(20);
/* read value */
value = __raw_readl(CH438_DAT)&0xFF;
/* RD=1,CS=1 */
reg |= (1<<CH438_CS)|(1<<CH438_RD);
__raw_writel(reg, CH438_DAT);
ndelay(20);
spin_unlock_irqrestore(&ch438_rwlock, flags);
return value;
}
static inline void ch438_reset(struct uart_port *port)
{
UART_PUT_IER(port, BIT_IER_RESET);
}
static unsigned int ch438_handle_rx(struct uart_port *port, unsigned char lsr)
{
struct tty_struct *tty = port->state->port.tty;
unsigned char ch = 0;
int max_count = 256;
char flag;
/* unsigned int cnt=0; */
/* printk("ttySC%d RX: ", port->line); */
/* cnt = port->icount.rx; */
do {
if(likely(lsr & BIT_LSR_DR)) {
ch = UART_GET_CHR(port);
/* printk("%02x ", ch); */
}
flag = TTY_NORMAL;
port->icount.rx++;
if(unlikely(lsr & LSR_BRK_ERROR_BITS)) {
/* For statistics only */
if(lsr & BIT_LSR_BI) {
lsr &= ~(BIT_LSR_FE | BIT_LSR_PE);
port->icount.brk++;
/*
* We do the SysRQ and SAK checking
* here because otherwise the barek
* may be masked by ignore_status_mask
* or read_status_mask.
*/
if(uart_handle_break(port))
goto ignore_char;
} else if(lsr & BIT_LSR_PE)
port->icount.parity++;
else if(lsr & BIT_LSR_FE)
port->icount.frame++;
if(lsr & BIT_LSR_OE)
port->icount.overrun++;
/* Mask off conditions which should be ingored. */
lsr &= port->read_status_mask;
if(lsr & BIT_LSR_BI)
flag = TTY_BREAK;
else if(lsr & BIT_LSR_PE)
flag = TTY_PARITY;
else if(lsr & BIT_LSR_FE)
flag = TTY_FRAME;
}
if(uart_handle_sysrq_char(port, ch))
goto ignore_char;
uart_insert_char(port, lsr, BIT_LSR_OE, ch, flag);
ignore_char:
lsr = UART_GET_LSR(port);
} while ((lsr & (BIT_LSR_DR|BIT_LSR_BI)) && (max_count-- > 0));
/* printk("(%d bytes)\n", port->icount.rx-cnt); */
spin_unlock(&port->lock);
tty_flip_buffer_push(tty);
spin_lock(&port->lock);
return lsr;
}
static void ch438_stop_tx(struct uart_port *port)
{
struct ch438_port *up = UART_TO_SPORT(port);
if(up->ier & BIT_IER_THRI) {
up->ier &= ~BIT_IER_THRI;
SERIAL_DBG("ttySC%d stop tx, ier 0x%02X\n", port->line, up->ier);
UART_PUT_IER(port, up->ier);
}
}
static void ch438_start_tx(struct uart_port *port)
{
struct ch438_port *up = UART_TO_SPORT(port);
/*
* when BIT_IER_THRI(0->1), will be generate "THR empty" interrupt.
*/
if(!(up->ier & BIT_IER_THRI)) {
up->ier |= BIT_IER_THRI;
SERIAL_DBG("ttySC%d start tx, ier 0x%02X\n", port->line, up->ier);
UART_PUT_IER(port, up->ier);
}
}
static void ch438_handle_tx(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
int count;
/* unsigned int cnt; */
if(port->x_char) {
/* Send special char - probebly flow control */
UART_PUT_CHR(port, port->x_char);
port->icount.tx++;
port->x_char = 0;
return;
}
if(uart_circ_empty(xmit) || uart_tx_stopped(port)) {
ch438_stop_tx(port);
return;
}
/* The IRQ is for TX FIFO half-empty */
count = port->fifosize / 2;
/* printk("ttySC%d TX:", port->line); */
/* cnt = port->icount.tx; */
do {
/* printk("%02x ", xmit->buf[xmit->tail]); */
UART_PUT_CHR(port, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail+1) & (UART_XMIT_SIZE-1);
port->icount.tx++;
if (uart_circ_empty(xmit))
break;
} while (--count > 0);
/* printk("(%d bytes)\n", port->icount.tx-cnt); */
if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
spin_unlock(&port->lock);
uart_write_wakeup(port);
spin_lock(&port->lock);
}
if(uart_circ_empty(xmit))
ch438_stop_tx(port);
}
static unsigned char ch438_modem_status(struct uart_port *port)
{
struct ch438_port *up = UART_TO_SPORT(port);
unsigned int status = UART_GET_MSR(port);
status |= up->msr_saved_flags;
up->msr_saved_flags = 0;
if(status & BIT_MSR_ANY_DELTA && up->ier & BIT_IER_MSI &&
port->state != NULL) {
if (status & BIT_MSR_TERI)
port->icount.rng++;
if (status & BIT_MSR_DDSR)
port->icount.dsr++;
if (status & BIT_MSR_DDCD)
uart_handle_dcd_change(port, status & BIT_MSR_DCD);
if (!(up->mcr & BIT_MCR_AFE) && status & BIT_MSR_DCTS)
uart_handle_cts_change(port, status & BIT_MSR_CTS);
wake_up_interruptible(&port->state->port.delta_msr_wait);
}
SERIAL_DBG("ttySC%d modem status: %x\n", port->line, status);
return status;
}
static void irq_todo(struct uart_port *port)
{
unsigned long flags=0;
unsigned char iir = UART_GET_IIR(port) & BIT_IIR_IID_MASK;
unsigned char lsr = UART_GET_LSR(port);
spin_lock_irqsave(&port->lock, flags);
SERIAL_DBG("ttySC%d irq: iir=0x%02x, lsr=0x%02x, ier=0x%02x\n",
port->line, iir, lsr, UART_GET_IER(port));
if(!(lsr & (BIT_LSR_BI | BIT_LSR_FE | BIT_LSR_PE | BIT_LSR_OE))) {
if(lsr & BIT_LSR_DR)
ch438_handle_rx(port, lsr);
} else {
UART_GET_CHR(port); /* clear error */
}
ch438_modem_status(port);
if(lsr & BIT_LSR_THRE) {
ch438_handle_tx(port);
}
spin_unlock_irqrestore(&port->lock, flags);
}
static irqreturn_t ch438_irq(int irq, void *dev_id)
{
unsigned char ssr = 0;
ssr = UART_GET_SSR();
if(ssr & 0x01)
irq_todo(&ch438_ports[0].port);
if(ssr & 0x02)
irq_todo(&ch438_ports[1].port);
if(ssr & 0x04)
irq_todo(&ch438_ports[2].port);
if(ssr & 0x08)
irq_todo(&ch438_ports[3].port);
if(ssr & 0x10)
irq_todo(&ch438_ports[4].port);
if(ssr & 0x20)
irq_todo(&ch438_ports[5].port);
if(ssr & 0x40)
irq_todo(&ch438_ports[6].port);
if(ssr & 0x80)
irq_todo(&ch438_ports[7].port);
return IRQ_HANDLED;
}
static inline void wait_for_xmitr(struct uart_port *port)
{
struct ch438_port *up = UART_TO_SPORT(port);
unsigned int status, tmout = 1000;
unsigned char mask = BIT_LSR_TEMT|BIT_LSR_THRE;
SERIAL_DBG("ttySC%d\n", port->line);
/* Wait up to 1ms for the character to be sent. */
do {
status = UART_GET_LSR(port);
if(status & BIT_LSR_BI)
up->lsr_break_flag = BIT_LSR_BI;
if (--tmout == 0)
break;
udelay(1);
} while ((status & mask) != mask);
/* CTS is unsupported by the 2-line UART, so ignore it. */
if(up->io_num == 2)
return;
/* Wait up to 1s for flow control if necessary */
if (port->flags & UPF_CONS_FLOW) {
tmout = 1000000;
while(--tmout && (UART_GET_MSR(port)&BIT_MSR_CTS)==0) {
udelay(1);
}
}
}
static unsigned int ch438_tx_empty(struct uart_port *port)
{
unsigned long flags = 0;
unsigned int ret = 0;
spin_lock_irqsave(&port->lock, flags);
ret = (UART_GET_LSR(port) & BIT_LSR_TEMT) ? TIOCSER_TEMT : 0;
spin_unlock_irqrestore(&port->lock, flags);
return ret;
}
static void ch438_set_mctrl(struct uart_port *port, u_int mctrl)
{
struct ch438_port *up = UART_TO_SPORT(port);
unsigned int mcr=0;
/* ch438q not used RTS & DTR */
if(mctrl & TIOCM_LOOP)
mcr |= BIT_MCR_LOOP;
if(mctrl & TIOCM_OUT2)
mcr |= BIT_MCR_OUT2;
up->mcr &= ~(BIT_MCR_RTS|BIT_MCR_DTR|BIT_MCR_LOOP|BIT_MCR_OUT2);
up->mcr |= mcr;
SERIAL_DBG("ttySC%d set mcr 0x%02X\n", port->line, mcr);
UART_PUT_MCR(port, up->mcr);
}
static unsigned int ch438_get_mctrl(struct uart_port *port)
{
unsigned int msr;
unsigned int ret = 0;
msr = ch438_modem_status(port);
if (msr & BIT_MSR_DCD)
ret |= TIOCM_CAR;
if (msr & BIT_MSR_RI)
ret |= TIOCM_RNG;
if (msr & BIT_MSR_DSR)
ret |= TIOCM_DSR;
if (msr & BIT_MSR_CTS)
ret |= TIOCM_CTS;
SERIAL_DBG("ttySC%d get msr 0x%02X\n", port->line, msr);
return ret;
}
static void ch438_stop_rx(struct uart_port *port)
{
struct ch438_port *up = UART_TO_SPORT(port);
if (up->ier & BIT_IER_RLSI) {
up->ier &= ~BIT_IER_RLSI;
SERIAL_DBG("ttySC%d stop rx, ier 0x%02X\n", port->line, up->ier);
port->read_status_mask &= ~BIT_LSR_DR;
UART_PUT_IER(port, up->ier);
}
}
static void ch438_enable_ms(struct uart_port *port)
{
struct ch438_port *up = UART_TO_SPORT(port);
if(!(up->ier & BIT_IER_MSI)) {
up->ier |= BIT_IER_MSI;
SERIAL_DBG("ttySC%d en msi, ier 0x%02X\n", port->line, up->ier);
UART_PUT_IER(port, up->ier);
}
}
static void ch438_break_ctl(struct uart_port *port, int break_state)
{
struct ch438_port *up = UART_TO_SPORT(port);
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
if (break_state == -1)
up->lcr |= BIT_LCR_SBC;
else
up->lcr &= ~BIT_LCR_SBC;
UART_PUT_LCR(port, up->lcr);
spin_unlock_irqrestore(&port->lock, flags);
}
static int ch438_startup(struct uart_port *port)
{
struct ch438_port *up = UART_TO_SPORT(port);
SERIAL_DBG("ttySC%d start up ...\n", port->line);
up->msr_saved_flags = 0;
up->ier = BIT_IER_RLSI |BIT_IER_THRI | BIT_IER_RDI;
UART_PUT_IER(port, up->ier); /* Interrupt when starting */
return 0;
}
static void ch438_shutdown(struct uart_port *port)
{
struct ch438_port *up = UART_TO_SPORT(port);
SERIAL_DBG("ttySC%d shut down ...\n", port->line);
up->lcr = 0;
up->mcr = 0;
up->fcr = 0;
up->ier &= ~(BIT_IER_RLSI|BIT_IER_THRI|BIT_IER_RDI);
/* the value of the register must be modified! */
UART_PUT_IER(port, up->ier);
}
static void ch438_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct ch438_port *up = UART_TO_SPORT(port);
unsigned long flags;
unsigned int baud, quot, lcr = 0, dll, dlh;
if(1) {
/* define custom termios settings for NMEA protocol */
termios->c_iflag /* input modes - */
&= ~(IGNBRK /* disable ignore break */
| BRKINT /* disable break causes interrupt */
| PARMRK /* disable mark parity errors */
| ISTRIP /* disable clear high bit of input char */
| INLCR /* disable translate NL to CR */
| IGNCR /* disable ignore CR */
| ICRNL /* disable translate CR to NL */
| IXON); /* disable enable XON/XOFF flow control */
termios->c_oflag /* output modes */
&= ~OPOST; /* disable postprocess output char */
termios->c_lflag /* line discipline modes */
&= ~(ECHO /* disable echo input characters */
| ECHONL /* disable echo new line */
| ICANON /* disable erase, kill, werase, and rprnt
special characters */
| ISIG /* disable interrupt, quit, and suspend
special characters */
| IEXTEN); /* disable non-POSIX special characters */
} /* CT_CYPHIDCOM: Application should handle this for device */
switch (termios->c_cflag & CSIZE) {
case CS5:
lcr = BIT_LCR_WLEN_5;
break;
case CS6:
lcr = BIT_LCR_WLEN_6;
break;
case CS7:
lcr = BIT_LCR_WLEN_7;
break;
case CS8:
default:
lcr = BIT_LCR_WLEN_8;
break;
}
if (termios->c_cflag & CSTOPB)
lcr |= BIT_LCR_STOP;
if (termios->c_cflag & PARENB)
lcr |= BIT_LCR_PARITY;
if (!(termios->c_cflag & PARODD))
lcr |= BIT_LCR_EPAR;
/* set baudrate */
baud = uart_get_baud_rate(port, termios, old,
port->uartclk / 16 / 0xffff,
port->uartclk / 16);
quot = uart_get_divisor(port, baud);
dll = quot & 0xFF;
dlh = quot >> 8;
/* SERIAL_DBG("baud=%d, quot=%d\n", baud, quot); */
spin_lock_irqsave(&port->lock, flags);
/* Update the per-port timeout. */
uart_update_timeout(port, termios->c_cflag, baud);
port->read_status_mask = BIT_LSR_OE | BIT_LSR_THRE | BIT_LSR_DR;
if (termios->c_iflag & INPCK)
port->read_status_mask |= BIT_LSR_FE | BIT_LSR_PE;
if (termios->c_iflag & (BRKINT | PARMRK))
port->read_status_mask |= BIT_LSR_BI;
/* Characters to ignore */
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= BIT_LSR_PE | BIT_LSR_FE;
if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |= BIT_LSR_BI;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= BIT_LSR_OE;
}
/*
* Ignore all characters if CREAD is not set.
*/
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= BIT_LSR_DR;
/*
* if lcr & baud are changed, reset controller to disable transfer
*/
if(lcr != up->lcr || dll != up->dll || dlh != up->dlh) {
/* SERIAL_DBG("ttySC%d reset controller...\n", port->line); */
ch438_reset(port);
}
up->dll = dll;
up->dlh = dlh;
/* flow control */
up->mcr &= ~ BIT_MCR_AFE;
if(termios->c_cflag & CRTSCTS)
up->mcr |= BIT_MCR_AFE;
UART_PUT_MCR(port, up->mcr);
/*
* CTS flow control flag and modem status interrupts
*/
up->ier &= ~BIT_IER_MSI;
if(UART_ENABLE_MS(port, termios->c_cflag))
up->ier |= BIT_IER_MSI;
UART_PUT_IER(port, up->ier);
up->fcr = BIT_FCR_FT_112|BIT_FCR_FIFOEN;
UART_PUT_FCR(port, up->fcr);
up->lcr = lcr;
UART_PUT_LCR(port, up->lcr|BIT_LCR_DLAB); /* set DLAB */
UART_PUT_DLL(port, up->dll);
UART_PUT_DLH(port, up->dlh);
UART_PUT_LCR(port, up->lcr); /* reset DLAB */
/* Don't rewrite B0 */
if(tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
ch438_set_mctrl(port, port->mctrl);
spin_unlock_irqrestore(&port->lock, flags);
}
static const char *ch438_type(struct uart_port *port)
{
return "CH438";
}
static void ch438_release_port(struct uart_port *port)
{
}
/*
* Request the memory region(s) being used by 'port'
*/
static int ch438_request_port(struct uart_port *port)
{
return 0;
}
/*
* Configure/autoconfigure the port.
*/
static void ch438_config_port(struct uart_port *port, int flags)
{
if(flags & UART_CONFIG_TYPE)
{
port->type = PORT_CH438;
ch438_request_port(port);
}
}
/*
* verify the new serial_struct (for TIOCSSERIAL).
*/
static int ch438_verify_port(struct uart_port *port, struct serial_struct *ser)
{
/* We don't want the core code to modify any port params */
return -EINVAL;
}
static void ch438_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct ch438_port *up = UART_TO_SPORT(port);
switch(state) {
case 0: /* Power up */
up->ier &= ~BIT_IER_LOWPWR;
UART_PUT_IER(port, up->ier);
SERIAL_DBG("ttySC%d PM state:%d --ON--\n", port->line, state);
break;
case 3: /* Power down */
up->ier |= BIT_IER_LOWPWR;
UART_PUT_IER(port, up->ier);
SERIAL_DBG("ttySC%d PM state:%d --OFF--\n", port->line, state);
break;
default:
SERIAL_DBG("usart%d, Unknown PM state %d\n", up->id, state);
}
}
static struct uart_ops ch438_uart_ops = {
.tx_empty = ch438_tx_empty,
.set_mctrl = ch438_set_mctrl,
.get_mctrl = ch438_get_mctrl,
.stop_tx = ch438_stop_tx,
.start_tx = ch438_start_tx,
.stop_rx = ch438_stop_rx,
.enable_ms = ch438_enable_ms,
.break_ctl = ch438_break_ctl,
.startup = ch438_startup,
.shutdown = ch438_shutdown,
.set_termios = ch438_set_termios,
.type = ch438_type,
.release_port = ch438_release_port,
.request_port = ch438_request_port,
.config_port = ch438_config_port,
.verify_port = ch438_verify_port,
.pm = ch438_pm,
};
static ssize_t ch438_dev_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uart_port *port = dev_get_drvdata(dev);
struct ch438_port *up = UART_TO_SPORT(port);
return snprintf(buf, PAGE_SIZE,
"id = %d \n"
"name = %s \n"
"irq = %d \n"
"io_num = %d \n"
"port->membase = 0x%08x \n"
"port->iobase = 0x%08x \n",
up->id, up->name, port->irq, 2,
(unsigned int)port->membase,
(unsigned int)port->iobase);
}
static struct device_attribute ch438_dev_info_attr =
__ATTR(dev_info, S_IRUGO, ch438_dev_info_show, NULL);
static ssize_t ch438_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uart_port *port = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE,
"uartclk = %d \n"
"RBR = 0x%02X, IER = 0x%02X, IIR = 0x%02X \n"
"FCR = 0x%02X, LCR = 0x%02X, MCR = 0x%02X \n"
"LSR = 0x%02X, MSR = 0x%02X, SCR = 0x%02X \n",
port->uartclk,
UART_GET_CHR(port),
UART_GET_IER(port),
UART_GET_IIR(port),
UART_GET_FCR(port),
UART_GET_LCR(port),
UART_GET_MCR(port),
UART_GET_LSR(port),
UART_GET_MSR(port),
UART_GET_SCR(port));
}
static struct device_attribute ch438_status_attr =
__ATTR(status, S_IRUGO, ch438_status_show, NULL);
static ssize_t ch438_loopback_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int mcr = 0;
struct uart_port *port = dev_get_drvdata(dev);
mcr = UART_GET_MCR(port);
return snprintf(buf, PAGE_SIZE,
"MCR: 0x%08x, Loopback: %d\n", mcr, mcr&BIT_MCR_LOOP ? 1 : 0);
}
static ssize_t ch438_loopback_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int mcr = 0;
int enable = 0;
struct uart_port *port = dev_get_drvdata(dev);
if (!strncmp(buf, "enable", 6))
enable = 1;
SERIAL_DBG("Set loopback: %d \n", enable);
mcr = UART_GET_MCR(port);
if (enable)
UART_PUT_MCR(port, mcr | BIT_MCR_LOOP);
else
UART_PUT_MCR(port, mcr & ~BIT_MCR_LOOP);
return count;
}
static struct device_attribute ch438_loopback_attr =
__ATTR(loopback, S_IRUGO|S_IWUSR, ch438_loopback_show, ch438_loopback_store);
static ssize_t ch438_ctrl_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uart_port *port = dev_get_drvdata(dev);
struct ch438_port *up = UART_TO_SPORT(port);
u32 dl = (u32)up->dlh << 8 | (u32)up->dll;
if(dl == 0)
dl = 1000;
return snprintf(buf, PAGE_SIZE,
"ier : 0x%02X\n"
"lcr : 0x%02X\n"
"mcr : 0x%02X\n"
"fcr : 0x%02X\n"
"dll : 0x%02X\n"
"dlh : 0x%02X\n"
"baud: %d (dl = %d)\n\n"
"TxRx Statistics:\n"
"tx : %d\n"
"rx : %d\n"
"parity : %d\n"
"frame : %d\n"
"overrun: %d\n",
up->ier, up->lcr, up->mcr,
up->fcr, up->dll, up->dlh,
(port->uartclk>>4)/dl, dl,
port->icount.tx,
port->icount.rx,
port->icount.parity,
port->icount.frame,
port->icount.overrun);
}
static struct device_attribute ch438_ctrl_info_attr =
__ATTR(ctrl_info, S_IRUGO, ch438_ctrl_info_show, NULL);
static void ch438_sysfs(struct platform_device *pdev)
{
device_create_file(&pdev->dev, &ch438_dev_info_attr);
device_create_file(&pdev->dev, &ch438_status_attr);
device_create_file(&pdev->dev, &ch438_loopback_attr);
device_create_file(&pdev->dev, &ch438_ctrl_info_attr);
}
static void __init ch328_device_scan(void)
{
struct uart_port *port;
int i;
memset(ch438_device, 0, sizeof(ch438_device));
memset(ch438_ports, 0, sizeof(ch438_ports));
for(i=0; i<CH438_DEV_NUM; i++) {
port = &ch438_ports[i].port;
ch438_device[i].name = CH438_UART_NAME;
ch438_device[i].id = i;
ch438_device[i].dev.platform_data = port;
port->iotype = UPIO_PORT;
port->ops = &ch438_uart_ops;
port->fifosize = 128; /* max: 128 */
port->line = i;
}
}
static struct uart_driver ch438_uart_driver = {
.owner = THIS_MODULE,
.driver_name = CH438_UART_NAME,
.dev_name = CH438_DEV_NAME,
.nr = CH438_DEV_NUM,
.cons = NULL,
};
struct platform_device *ch438_get_pdev(int uart_id)
{
if(ch438_ports[uart_id].port.dev)
return to_platform_device(ch438_ports[uart_id].port.dev);
else
return NULL;
}
EXPORT_SYMBOL(ch438_get_pdev);
static int __devinit ch438_probe(struct platform_device *pdev)
{
struct uart_port *port;
struct ch438_port *up;
struct ch438_pin *pin = &ch438_pin;
int id = pdev->id;
int ret;
port = &ch438_ports[pdev->id].port;
port->dev = &pdev->dev;
up = UART_TO_SPORT(port);
up->id = id;
up->ier = 0;
up->lcr = 0;
up->mcr = 0;
up->fcr = 0;
up->dll = 0;
up->dlh = 0;
up->io_num = 2;
sprintf(up->name, CH438_UART_NAME"%d", id);
pdev->dev.init_name = up->name;
port->uartclk = CH438_CLOCK_RATE;
port->type = PORT_CH438;
port->flags = UPF_BOOT_AUTOCONF;
port->mapbase = 0; /* it was not used! */
port->irq = pin->irqnum;
port->mctrl = TIOCM_OUT2; /* use BIT_MCR_OUT2 generate an interrupt */
platform_set_drvdata(pdev, port);
ret = request_irq(port->irq, ch438_irq,
IRQF_TRIGGER_FALLING|IRQF_DISABLED|IRQF_SHARED, up->name, port);
if(unlikely(ret)) {
SERIAL_DBG("usart%d cannot get irq%d\n", up->id, port->irq);
}
uart_add_one_port(&ch438_uart_driver, port);
return 0;
}
static int __devexit ch438_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
SERIAL_DBG("release uart%d port\n", port->line);
free_irq(port->irq, port);
return 0;
}
/* UART power management code */
#ifdef CONFIG_PM_SLEEP
static int ch438_suspend(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
if (port) {
SERIAL_DBG("usart%d suspend.\n", port->line);
uart_suspend_port(&ch438_uart_driver, port);
}
return 0;
}
static int ch438_resume(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
if (port) {
uart_resume_port(&ch438_uart_driver, port);
SERIAL_DBG("usart%d resume.\n", port->line);
}
return 0;
}
static const struct dev_pm_ops ch438_pm_ops = {
.suspend = ch438_suspend,
.resume = ch438_resume,
};
#define CH438_PM_OPS (&ch438_pm_ops)
#else /* !CONFIG_PM_SLEEP */
#define CH438_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
static struct platform_driver ch438_platform_driver =
{
.probe = ch438_probe,
.remove = ch438_remove,
.driver = {
.name = CH438_UART_NAME,
.pm = CH438_PM_OPS,
.owner = THIS_MODULE,
},
};
static int __init ch438_uart_init(void)
{
int i;
spin_lock_init(&ch438_rwlock);
SERIAL_DBG("ch438 uart init\n");
ch438_mapbase = CH438_BASE;
ch438_membase = ioremap(ch438_mapbase, GPIO_RANGE);
if(!ch438_membase) {
SERIAL_DBG("ch438, ioremap failed\n");
release_mem_region(ch438_mapbase, GPIO_RANGE);
}
ch438_gpio_init();
ch328_device_scan();
/* SERIAL_DBG("register ch438 driver...\n"); */
uart_register_driver(&ch438_uart_driver);
/* SERIAL_DBG("register platform_device...\n"); */
for(i=0; i<CH438_DEV_NUM; i++) {
platform_device_register(&ch438_device[i]);
ch438_sysfs(&ch438_device[i]);
}
return platform_driver_register(&ch438_platform_driver);
}
static void __exit ch438_uart_exit(void)
{
release_mem_region(ch438_mapbase, GPIO_RANGE);
iounmap(ch438_membase);
ch438_membase = NULL;
platform_driver_unregister(&ch438_platform_driver);
uart_unregister_driver(&ch438_uart_driver);
ch438_gpio_free();
}
module_init(ch438_uart_init);
module_exit(ch438_uart_exit);
MODULE_DESCRIPTION("CH438 serial port driver");
MODULE_AUTHOR("iamyhw@foxmail.com");
MODULE_LICENSE("GPL");
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。