现在的位置: 首页 > 综合 > 正文

基于AT91RM9200的16C554扩展485口驱动

2013年08月27日 ⁄ 综合 ⁄ 共 29149字 ⁄ 字号 评论关闭

 

以前做的一个项目里用到的,用16C554扩展4个485口,我在驱动里添加了485的收发控制,用的还行,没发现什么问题。内核是2.6.13
分别在st16c554_start_tx,st16c554_stop_tx添加了485控制,当然仅对我的系统适用,并且初始化时置成收的状态。
st16c554.c
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/mca.h>
#include <linux/delay.h>
#include <linux/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/serial_8250.h>
#include <linux/interrupt.h>
#define CONFIG_HUB6 1
#include <asm/serial.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/arch/hardware.h>
#include "st16c554.h"
MODULE_LICENSE("Dual BSD/GPL");
/*------------ 接口全局变量------------*/
static unsigned char * ST16C554PortBase[UART_16C554_NUM];
//static struct platform_device *serial16c554_devs;
static struct device *serial16c554_dev;
struct st16c554_uart_port st16c554_ports[UART_16C554_NUM];
static DECLARE_MUTEX(serial16c554_sem);
static char PortIsOpen[UART_16C554_NR];
//static char PortIRQ[UART_16C554_NUM] = {ST16C554_IRQA, ST16C554_IRQB, ST16C554_IRQC, ST16C554_IRQD};
/*------------ 接口操作函数------------*/
static struct uart_ops st16c554_pops = {
.tx_empty = st16c554_tx_empty,
.set_mctrl = st16c554_set_mctrl,
.get_mctrl = st16c554_get_mctrl,
.stop_tx = st16c554_stop_tx,
.start_tx = st16c554_start_tx,
.stop_rx = st16c554_stop_rx,
.enable_ms = st16c554_enable_ms,
.break_ctl = st16c554_break_ctl,
.startup = st16c554_startup,
.shutdown = st16c554_shutdown,
.set_termios = st16c554_set_termios,
.pm = st16c554_pm,
.type = st16c554_type,
.release_port = st16c554_release_port,
.request_port = st16c554_request_port,
.config_port = st16c554_config_port,
.verify_port = st16c554_verify_port,
};
struct uart_driver st16c554_reg = {
.owner = THIS_MODULE,
.driver_name = ST16C554_DRIVER_NAME,
.devfs_name = ST16C554_DEVFS_NAME,
.dev_name = ST16C554_DEV_NAME,
.major = ST16C554_MAJOR,
.minor = ST16C554_MINOR,
.nr = UART_16C554_NR,
.cons = ST16C554_CONSOLE,
};
static unsigned int serial_in(struct uart_port *up, int offset)
{
switch(offset) {
case UART_RX: //0
//case UART_TX: //0
//case UART_DLL: //0
return UART_URXH(up);
case UART_IER: //1
//case UART_DLM: //1
return UART_UIER(up);
case UART_FCR://2
//case UART_IIR: //2
return UART_UFCON(up);
case UART_LCR://3
return UART_ULCON(up);
case UART_MCR: //4
return UART_UMCON(up);
case UART_LSR://5
return UART_ULSTAT(up);
case UART_MSR://6
return UART_UMSTAT(up);
case UART_SCR://7
return UART_URSCRR(up);
default:
return 0;
}
}
/***************************************
**************************************/
unsigned int st16c554_tx_empty(struct uart_port *port)
{
unsigned long flags;
unsigned int ret;
spin_lock_irqsave(&port->lock, flags);
ret = serial_in(port, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
spin_unlock_irqrestore(&port->lock, flags);
return 1;
}
/***************************************
**************************************/
void st16c554_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned char mcr = 0;
//printk("Now Start st16c554_set_mctrl,line= %d\n",port->line);
if (mctrl & TIOCM_RTS)
mcr |= 0x02;
if (mctrl & TIOCM_DTR)
mcr |= 0x01;
if (mctrl & TIOCM_OUT1)
mcr |= 0x04;
if (mctrl & TIOCM_OUT2)
mcr |= 0x80;
if (mctrl & TIOCM_LOOP)
mcr |= 0x10;
/* Enable interrupt */
mcr |= 0x08;
UART_UMCON(port) = mcr;
//printk("st16c554_set_mctrl->set MCR to 0x%x\n",mcr);
return;
}
/***************************************
**************************************/
unsigned int st16c554_get_mctrl(struct uart_port *port)
{
unsigned char status;
unsigned int ret;
//printk("Now Start st16c554_get_mctrl,line= %d\n",port->line);

status = UART_UMSTAT(port);
ret = 0;
if (status & 0x80)
ret |= TIOCM_CAR;
if (status & 0x40)
ret |= TIOCM_RNG;
if (status & 0x20)
ret |= TIOCM_DSR;
if (status & 0x10)
ret |= TIOCM_CTS;
return ret;
}
/***************************************
**************************************/
void st16c554_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
struct st16c554_uart_port *up;
//printk("Now Start st16c554_stop_tx,line= %d\n",port->line);

up=&st16c554_ports[port->line];
up->ier&= ~0x02;
/*485收发控制*/
mdelay(2);
UART_UMCON(port)=0x0b;
UART_UIER(port) = up->ier;
return;
}
/***************************************
**************************************/
void st16c554_start_tx(struct uart_port *port,unsigned int tty_start)
{
struct uart_info *info;
struct st16c554_uart_port *up;
//printk("Now Start st16c554_start_tx,line= %d\n",port->line);

info=port->info;
up=&st16c554_ports[port->line];
/*485收发控制*/
mdelay(2);
UART_UMCON(port)=0x08;
up->ier|= 0x02;
UART_UIER(port) = up->ier;
tx_interrupt(info, port);
return ;
}
/***************************************
**************************************/
void st16c554_stop_rx(struct uart_port *port)
{
struct st16c554_uart_port *up;
//printk("Now Start st16c554_stop_rx,line= %d\n",port->line);

up=&st16c554_ports[port->line];
up->ier&= ~0x01;
UART_UIER(port) = up->ier;
return;
}
/***************************************
开启Modem中断
**************************************/
void st16c554_enable_ms(struct uart_port *port)
{
struct st16c554_uart_port *up;
//printk("Now Start st16c554_enable_ms,line= %d\n",port->line);

up=&st16c554_ports[port->line];
up->ier |= 0x08;
UART_UIER(port) = up->ier;
return;
}
/***************************************
break_state!=0:发送break信号,否则,恢复正常模式
**************************************/
void st16c554_break_ctl(struct uart_port *port, int break_state)
{
//printk("Now Start st16c554_break_ctl,line= %d\n",port->line);
struct st16c554_uart_port *up = &st16c554_ports[port->line];

unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
if (break_state == -1)
up->lcr |= UART_LCR_SBC;
else
up->lcr &= ~UART_LCR_SBC;
UART_ULCON(port)=up->lcr;
spin_unlock_irqrestore(&port->lock, flags);
return;
}
/***************************************
Need debug
注意:
不能直接操作IRQ,因为别的端口与这个端口的IRQ相同
**************************************/
int st16c554_startup(struct uart_port *port)
{
struct st16c554_uart_port *up = &st16c554_ports[port->line];
unsigned long Ret;
unsigned char Data;
printk("Now Start st16c554_startup,line= %d\n",port->line);
//UART_UIER(port) = 0x07;
//UART_UFCON(port) = 0x07;
UART_UFCON(port) = 0xcf;
//udelay(10000);
mdelay(10);
UART_UFCON(port) = 0xc9;
st16c554_set_mctrl(port,port->mctrl);
UART_ULCON(port) = up->lcr=0x83;
//UART_UBRDIVL(port)= (0x0c); //default is 9600 bps
UART_UBRDIVL(port)= (0x24); //default is19200 bps
UART_UBRDIVM(port)= (0x00);
UART_ULCON(port) = up->lcr=0x03;
//空读一次
Data=UART_ULCON(port) ;
//确保收状态
Ret=1;
while(Ret){
Data=UART_ULSTAT(port);
if(Data&0x01){
//printk("st16c554_startup->Receive FIFO is not ready.\n");
Data=UART_URXH(port);
}else{
Ret=0;
}
}
//指示已经打开的端口
PortIsOpen[port->line]=1;
//printk("st16c554_startup->Now Set line %d to 1\n",port->line);
up->ier = UART_IER_RLSI | UART_IER_RDI;
UART_UIER(port) = up->ier;
return 0;
}
/***************************************
Need debug
注意:
不能直接删除IRQ,因为别的端口与这个端口的IRQ相同
**************************************/
void st16c554_shutdown(struct uart_port *port)
{
unsigned long flags;
struct st16c554_uart_port *up = &st16c554_ports[port->line];
//printk("Now Start st16c554_shutdown,line= %d\n",port->line);
up->ier = 0;
UART_UIER(port) = up->ier;
spin_lock_irqsave(&port->lock, flags);
port->mctrl &= ~TIOCM_OUT2;
st16c554_set_mctrl(port,port->mctrl);
spin_unlock_irqrestore(&port->lock, flags);
UART_ULCON(port) = 0x00;
UART_UFCON(port) = 0x00;
flags=(unsigned long )UART_URXH(port);
//指示已经关闭的端口
PortIsOpen[port->line]=0;
//printk("st16c554_shutdown->Now Set line %d to 0\n",port->line);
return;
}
/***************************************
Need Write by myself
**************************************/
void st16c554_set_termios(struct uart_port *port, struct termios *termios,
struct termios *old)
{
struct st16c554_uart_port *up = (struct st16c554_uart_port *)port;
unsigned char cval; //LCR Value
unsigned char fcr = 0;
unsigned long flags;
unsigned int baud, quot;
//printk("Now Start st16c554_set_termios,line= %d\n",port->line);

//set word length
cval=0;
switch (termios->c_cflag & CSIZE) {
case CS5:
cval = UART_LCR_WLEN5;
break;
case CS6:
cval = UART_LCR_WLEN6;
break;
case CS7:
cval = UART_LCR_WLEN7;
break;
default:
case CS8:
cval = UART_LCR_WLEN8;
break;
}
if (termios->c_cflag & CSTOPB)
cval |= UART_LCR_STOP;
if (termios->c_cflag & PARENB)
cval |= UART_LCR_PARITY;
if (!(termios->c_cflag & PARODD))
cval |= UART_LCR_EPAR;
#ifdef CMSPAR
if (termios->c_cflag & CMSPAR)
cval |= UART_LCR_SPAR;
#endif
/*
* Ask the core to calculate the divisor for us.
* baud is the last baud,such as 9600,115200 etc.
*/
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
//printk("set_termios->baud=%d port=%d,CBAUD=0x%x\n",baud,port->uartclk,CBAUD);
quot = st16c554_get_divisor(port, baud);
/*
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
spin_lock_irqsave(&up->port.lock, flags);
/*
* Update the per-port timeout.
*/
uart_update_timeout(port, termios->c_cflag, baud);
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;

if (termios->c_iflag & INPCK)
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
if (termios->c_iflag & (BRKINT | PARMRK))
up->port.read_status_mask |= UART_LSR_BI;
/*
* Characteres to ignore
*/
up->port.ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
if (termios->c_iflag & IGNBRK) {
up->port.ignore_status_mask |= UART_LSR_BI;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
up->port.ignore_status_mask |= UART_LSR_OE;
}
/*
* ignore all characters if CREAD is not set
*/
if ((termios->c_cflag & CREAD) == 0)
up->port.ignore_status_mask |= UART_LSR_DR;
/*
* CTS flow control flag and modem status interrupts
*/
up->ier &= ~UART_IER_MSI;
if (UART_ENABLE_MS(&up->port, termios->c_cflag))
up->ier |= UART_IER_MSI;
up->ier |= 0x05;
UART_UIER(port)=up->ier;
//printk("tx_interrupt->set IER to 0x%\n",up->ier);
UART_ULCON(port)=cval | UART_LCR_DLAB;/* set DLAB;UART_LCR_DLAB=0x80*/

//printk("tx_interrupt->set LCR to 0x%\n",cval | UART_LCR_DLAB);
UART_UBRDIVL(port)=quot & 0xff; /* LS of divisor */
//printk("tx_interrupt->set DLL to 0x%x\n",quot & 0xff);
UART_UBRDIVM(port)=quot >> 8; /* MS of divisor */
//printk("tx_interrupt->set DLM to 0x%x\n",quot >> 8);
cval&=0x7f;
UART_ULCON(port)=cval; /* reset DLAB */
up->lcr = cval; /* Save LCR */
//printk("tx_interrupt->set LCR to 0x%\n",cval);
fcr=0xcf;
UART_UFCON(port)=fcr; /* set fcr */
//udelay(10000);
mdelay(10);
fcr=0xc9;
UART_UFCON(port)=fcr; /* set fcr */
//printk("tx_interrupt->set FCR to 0x%\n",fcr);
st16c554_set_mctrl(&up->port, up->port.mctrl);
spin_unlock_irqrestore(&up->port.lock, flags);
return;
}
/***************************************
Power Management
No Action by wangjm
**************************************/
void st16c554_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
//printk("Now Start st16c554_pm,line= %d\n",port->line);
return;
}
/***************************************
**************************************/
const char * st16c554_type(struct uart_port *port)
{
//printk("Now Start st16c554_type,line= %d\n",port->line);
return (port->type == PORT_16550A) ? "ST16C554" : NULL;
}
/***************************************
OK!
**************************************/
void st16c554_release_port(struct uart_port *port)
{
//printk("Now Start st16c554_release_port,line= %d\n",port->line);
return;
}
/***************************************
No Action!
**************************************/
int st16c554_request_port(struct uart_port *port)
{
int ret = 0;
//printk("Now Start st16c554_request_port,line= %d\n",port->line);

return ret;
}
/***************************************
Need debug
执行串口控制器的自动配置
**************************************/
void st16c554_config_port(struct uart_port *port, int flags)
{
//printk("Now Start st16c554_config_port line=%d\n",port->line);
if((port->mapbase == ST16C554_PHY_ADDRESS)||
(port->mapbase == ST16C554_PHY_ADDRESS +(1<<21))||
(port->mapbase == ST16C554_PHY_ADDRESS +(2<<21))||
(port->mapbase == ST16C554_PHY_ADDRESS +(3<<21)))
{
port->line=((port->mapbase-ST16C554_PHY_ADDRESS)>>21)/8;
spin_lock_init(&(port->lock));
port->ops = &st16c554_pops;
//port->iobase = (unsigned int *)(ST16C554PortBase +port->line*8); //map以后的虚拟地址

//port->membase = (unsigned char *)(ST16C554PortBase +port->line*8);
port->iobase = (unsigned int )(ST16C554PortBase[port->line] ); //map以后的虚拟地址

port->membase = (unsigned char *)(ST16C554PortBase[port->line] );
//port->irq = PortIRQ[port->line]; //CPU 中断号码
port->irq = ST16C554_IRQA;
port->uartclk = 11059200; //=11.0592MHz
//port->uartclk = 1843200;
port->fifosize = 16; //发送的FIFO大小
port->type = PORT_16550A;
port->flags = ASYNC_SKIP_TEST ;//| ASYNC_BOOT_AUTOCONF;

port->iotype = SERIAL_IO_MEM;
}
return;
}
/***************************************
检验端口是否是OK的。
**************************************/
int st16c554_verify_port(struct uart_port *port, struct serial_struct *ser)
{
//printk("Now Start st16c554_verify_port,line= %d\n",port->line);

if (ser->irq !=ST16C554_IRQA||
ser->baud_base < 9600 || ser->type != PORT_16550A){
printk("st16c554_verify_port-> Invalid param.\n");
return -EINVAL;
}
return 0;
}
/***************************************
**************************************/
static void tx_interrupt(struct uart_info *info, struct uart_port *port)
{
struct uart_state *state = info->tty->driver_data;
//printk("Now Start tx_interrupt\n");
if (port->x_char) {
UART_UTXH(port) = port->x_char;
port->icount.tx++;
port->x_char = 0;
//printk("tx_interrupt: x_char.\n");
return;
}
if (info->xmit.head == info->xmit.tail
|| info->tty->stopped || info->tty->hw_stopped) {
st16c554_stop_tx(state->port,0);
//printk("tx_interrupt: head=tail.\n");
return;
}
//do {
 /**/
// while(!(UART_ULSTAT(port)&0x20))
//  {
  //确保TXH为空
  //printk("tx_interrupt->LSR=0x%x\n",UART_ULSTAT(port));
//  udelay(100);
//  }
 /**/
 UART_UTXH(port) = info->xmit.buf[info->xmit.tail];
 info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
 port->icount.tx++;
// if (info->xmit.head == info->xmit.tail)
//  break;
// } while (1);
if (info->xmit.head == info->xmit.tail)
st16c554_stop_tx(state->port,0);
//printk(" head=tail.\n");
//UART_UMCON(port)=0x08;
}
/***************************************
**************************************/
//static void err_interrupt( struct uart_info *info,struct tty_struct *tty,struct uart_port *port)

static void err_interrupt( struct uart_port *port, struct pt_regs *regs)
{
unsigned char err;
struct tty_struct *tty;
unsigned int ch, flg;
 
err= UART_ULSTAT(port)& 0x1E; /* Do not include the break int */

tty = port->info->tty;
//ch = UART_URXH(port);
//printk("Now Start st16c554_startup,line= %d\n",port->line);
printk("Now Start err_interrupt,LSR=0x%x\n",err);

//if (!(err & (0x10 | 0x08 | 0x04 | 0x02 )))
//return;
 
if (err & 0x10)
port->icount.brk++;
if (err & 0x08)
port->icount.frame++;
if (err & 0x04)
port->icount.parity++;
if (err & 0x02)
port->icount.overrun++;
err &= port->read_status_mask;
if (err & 0x04)
flg = TTY_PARITY;
else if (err & 0x08)
flg = TTY_FRAME;
else
flg = TTY_NORMAL;
if (err & 0x02) {
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = flg;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
if (tty->flip.count < TTY_FLIPBUF_SIZE) {
ch = 0;
flg = TTY_OVERRUN;
}
}
//*tty->flip.flag_buf_ptr++ = flg;
//*tty->flip.char_buf_ptr++ = ch;
//tty->flip.count++;
return;
}
void uart_insert_char1(struct uart_port *port, unsigned int status,
   unsigned int overrun, unsigned int ch, unsigned int flag)
{
 struct tty_struct *tty = port->info->tty;
 if ((status & port->ignore_status_mask & ~overrun) == 0)
  tty_insert_flip_char(tty, ch, flag);
 /*
  * Overrun is special.  Since it's reported immediately,
  * it doesn't affect the current character.
  */
 
 if (status & ~port->ignore_status_mask & overrun)
  {
  //tty_insert_flip_char(tty, 0, TTY_OVERRUN);
  //printk("insert 0\n");
  }
}
/***************************************
**************************************/
//static void rx_interrupt(struct uart_info *info, struct tty_struct *tty, struct uart_port *port)

static void rx_interrupt(struct uart_port *port, struct pt_regs *regs)
{
struct tty_struct *tty = port->info->tty;
unsigned char ch, lsr;
int max_count = 256;
char flag;
unsigned char buf[2];
//lsr = serial_in(port, UART_FCR);
//printk("Now Start rx_interrupt,IIR=0x%x\n",lsr);
lsr=UART_ULSTAT(port);
//printk("Now Start rx_interrupt,LSR=0x%x\n",lsr);
do {
/* The following is not allowed by the tty layer and
unsafe. It should be fixed ASAP */
if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
if (tty->low_latency) {
spin_unlock(&port->lock);
tty_flip_buffer_push(tty);
spin_lock(&port->lock);
}
/*
* If this failed then we will throw away the
* bytes but must do so to clear interrupts
*/
}
ch = serial_in(port, UART_RX);
//printk("ch %x\n",ch);
flag = TTY_NORMAL;
port->icount.rx++;
if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |

UART_LSR_FE | UART_LSR_OE))) {
/*
* For statistics only
*/
if (lsr & UART_LSR_BI) {
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
port->icount.brk++;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask.
*/
if (uart_handle_break(port))
goto ignore_char;
} else if (lsr & UART_LSR_PE)
port->icount.parity++;
else if (lsr & UART_LSR_FE)
port->icount.frame++;
if (lsr & UART_LSR_OE)
port->icount.overrun++;
if (lsr & UART_LSR_BI) {
//printk("handling break....");
flag = TTY_BREAK;
} else if (lsr & UART_LSR_PE)
flag = TTY_PARITY;
else if (lsr & UART_LSR_FE)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(port, ch, regs))
goto ignore_char;
uart_insert_char1(port, lsr, UART_LSR_OE, ch, flag);

buf[0] = ch;
buf[1] = 0;
//printk("%s\n",buf);
ignore_char:
lsr = serial_in(port, UART_LSR);
} while ((lsr & UART_LSR_DR) && (max_count-- > 0));
spin_unlock(port->lock);
tty_flip_buffer_push(tty);
spin_lock(port->lock);
return IRQ_HANDLED;
}
/***************************************
give the wanted baudrate,return the divider
**************************************/
static unsigned int st16c554_get_divisor(struct uart_port *port, unsigned int baud)

{
//printk("Now Start st16c554_get_divisor,line= %d baud=%d\n",port->line,baud);
return(11059200/(16*baud));
//return (1843200/(16*baud));
}
/***************************************
**************************************/
static void serial16c554_timeout(unsigned long data)
{
//printk("Now Start serial16c554_timeout\n");
return;
}
/***************************************
**************************************/
static void serial16c554_print_port(struct st16c554_uart_port *up)
{
printk("----------------- port %d---------------------\n",up->port.line);
printk("mcr_mask:\t0x%x\n",up->mcr_mask);
printk("mcr_force:\t0x%x\n",up->mcr_force);
printk("port.iobase:\t0x%x\n",up->port.iobase );
printk("port.irq:\t0x%x\n",up->port.irq);
printk("port.uartclk:\t0x%x\n",up->port.uartclk);
printk("port.flags:\t0x%x\n",up->port.flags);
printk("port.hub6:\t0x%x\n",up->port.hub6);
printk("port.membase:\t0x%x\n",(unsigned int)(up->port.membase));
printk("port.iotype:\t0x%x\n",up->port.iotype);
printk("port.regshift:\t0x%x\n",up->port.regshift);
printk("----------------------------------------------\n");
return ;
}
/***************************************
//address_map
**************************************/
int st16c554_AddressMap(void)
{
int i;
for (i=0; i<UART_16C554_NUM; i++)
{
 ST16C554PortBase[i]=(unsigned char *)ioremap(ST16C554_PHY_ADDRESS+(i<<21),ST16C554_REMAP_SIZE);

 if(ST16C554PortBase[i]==NULL){
 return -1;
 }
 printk("ST16c554 Base Address:0x%x is mapped to 0x%x\n",
  (unsigned int)(ST16C554_PHY_ADDRESS+(i<<21)),
  (unsigned int)ST16C554PortBase[i]);

return 0;
}
/***************************************
**************************************/
int st16c554_InitUARTPort( struct st16c554_uart_port * pUARTPort)
{
int i;
struct uart_port *pPort;
for(i=0;i<UART_16C554_NUM;i++){
pPort=&(pUARTPort[i].port);
pPort->line = i;//+ST16C554_PORT_INDEX;
spin_lock_init(&(pPort->lock));
init_timer(&(pUARTPort[i].timer));
pUARTPort[i].timer.function = serial16c554_timeout;
pPort->ops = &st16c554_pops;
//pPort->iobase = (unsigned int)(ST16C554PortBase +i*8); //map以后的虚拟地址

//pPort->membase = (unsigned char *)(ST16C554PortBase +i*8);
pPort->iobase = (unsigned int )(ST16C554PortBase[i] ); //map以后的虚拟地址
pPort->membase = (unsigned char *)(ST16C554PortBase[i] );
pPort->mapbase = ST16C554_PHY_ADDRESS + ((i)<<21);
pPort->irq = ST16C554_IRQA; //CPU 中断号码
pPort->uartclk = 11059200; //=1.8432MHz
//pPort->uartclk = 1843200; //=1.8432MHz
pPort->fifosize = 16; //发送的FIFO大小
pPort->type = PORT_16550A;
//printk("pPort->mapbase is 0x%x\n",pPort->mapbase);

pPort->flags = ASYNC_SKIP_TEST ;//| ASYNC_BOOT_AUTOCONF;
pPort->iotype = SERIAL_IO_MEM;
serial16c554_print_port(&pUARTPort[i]);
}
return 0;
}
/***************************************
**************************************/
int st16c554_InitReg( void)
{
unsigned char Data,i;
struct uart_port *pPort;
for(i=0;i<UART_16C554_NR;i++){
pPort=&(st16c554_ports[i].port);
//printk("%d \n",ioread8(pPort->membase+3));
UART_ULCON(pPort)=0x03;
UART_UIER(pPort)=0x05;
UART_UMCON(pPort)=0x08;
UART_UFCON(pPort)=0xcf;
//udelay(10000);
mdelay(10);
UART_UFCON(pPort)=0xc9;
//需要做一次空读操作
Data=UART_ULCON(pPort);
PortIsOpen[i]=0;
//printk("st16c554_InitReg->Now set %d to 0\n",i);
}
return 0;
}
/***************************************
**************************************/
static int serial16c554_register_ports(struct uart_driver *drv, struct device *dev)

{
int i,Ret;
struct st16c554_uart_port *pPort;
for (i = 0; i < UART_16C554_NUM; i++) {
pPort = &st16c554_ports[i];
pPort->port.dev = dev;
Ret=uart_add_one_port(drv, &(pPort->port));
if(Ret)
printk(KERN_DEBUG "serial16c554_register_ports->add port error %d->Ret%d\n", i, Ret);

}
return 0;
}
/*
中断服务函数
*/
static irqreturn_t serial16c554_interrupt(int irq, void *dev_id, struct pt_regs *regs)

{
struct uart_port *port;
struct uart_state *state;
unsigned char i,isr,PortNum;
struct uart_driver *drv=(struct uart_driver *)dev_id;
//printk("Now Start serial16c554_interrupt,dev_id=0x%x irq=%d,regs=0x%x\n",(unsigned int)dev_id,irq,(unsigned int)regs);

for(i=0;i<UART_16C554_NR;i++){
if(PortIsOpen[i]==0){
//port is not opened
//printk("serial16c554_interrupt->port is not opened.line=%d,%d\n",PortNum,i);
continue;
}
state = drv->state +i;
port=state->port;
spin_lock(&port->lock);
PortNum=port->line;
isr=UART_UISTAT(port);
//printk("serial16c554_interrupt->line=%d ISR=0x%x\n",i,isr);
while(!(isr&0x01)) {
isr=(isr&0x0E)>>1;
switch(isr) {
case 3:
 err_interrupt(port,regs);
 break;
case 1:
 tx_interrupt(port->info,port);
 break;
case 2:
case 6:
 rx_interrupt(port,regs);
break;
case 0:
 check_modem_status(port,regs);
default:
 printk("Unknow interrupt type!");
break;
}
isr=UART_UISTAT(port);
}//end while
spin_unlock(&port->lock);
}//end for
return IRQ_HANDLED;
}
/***************************************
**************************************/
//static void check_modem_status(struct uart_info *info, struct uart_port *port)
static void check_modem_status(struct uart_port *port, struct pt_regs *regs)
{
int status;
status = UART_UMSTAT(port);
//printk("check_modem_status->MSR=0x%\n",status);
return;
}
/************----------- 模块初始化相关函数 --------------------***************/

/***************************************
**************************************/
int st16c554uart_init(void)
{
int Ret;
int i;
Ret=st16c554_AddressMap();
if(Ret<0){
printk("ERROR:st16c554_AddressMap Failure.\n");
return Ret;
}
Ret=st16c554_InitUARTPort(st16c554_ports);
if(Ret<0){
printk("ERROR:st16c554_InitUARTPort Failure.Ret:0x%x\n",Ret);
return Ret;
}
Ret=st16c554_InitReg();
if(Ret<0){
printk("ERROR:st16c554_InitReg Failure.Ret:0x%x\n",Ret);
return Ret;
}
AT91_SYS->AIC_SMR[25] |= AT91C_AIC_SRCTYPE_EXT_HIGH_LEVEL;
//AT91_SYS->AIC_SMR[26] |= AT91C_AIC_SRCTYPE_EXT_HIGH_LEVEL;
//AT91_SYS->AIC_SMR[27] |= AT91C_AIC_SRCTYPE_EXT_HIGH_LEVEL;
 
//printk("Now Start To register uart driver.\n");

Ret=uart_register_driver(&st16c554_reg);
if(Ret){
//failed
printk("Register st16c554 serial driver Failure.No=0x%x\n",Ret);
return Ret;
}
/*
serial16c554_devs = platform_device_register_simple("serial16C554",
PLAT8250_DEV_LEGACY, NULL, 0);
if (IS_ERR(serial16c554_devs)) {
Ret = PTR_ERR(serial16c554_devs);
goto st16c554_init_out;
}
*/
//printk("Now Start To register ports.\n");
//Ret=serial16c554_register_ports(&st16c554_reg, &serial16c554_devs->dev);
Ret=serial16c554_register_ports(&st16c554_reg, serial16c554_dev);
if(Ret!=0){
printk("register ports failed. Return %d\n",Ret);
goto st16c554_init_out;
}
//printk("Now Start To register interrupt functions.\n");

//for (i=0; i<UART_16C554_NUM; i++)
//{
// free_irq(st16c554_ports[i].port.irq,NULL);
 
// Ret = request_irq(st16c554_ports[i].port.irq, serial16c554_interrupt,

// SA_INTERRUPT|SA_SHIRQ, "serial 16c554", &st16c554_reg);
// if (Ret < 0){
// printk("serial st16c554:interrupt %d request failled. return %d\n",st16c554_ports[i].port.irq,Ret);

// goto st16c554_init_out;
// }
//}
 free_irq(ST16C554_IRQA,NULL); 
 Ret = request_irq(ST16C554_IRQA, serial16c554_interrupt,

 SA_INTERRUPT|SA_SHIRQ, "serial 16c554", &st16c554_reg);
 if (Ret < 0){
 printk("serial st16c554:interrupt %d request failled. return %d\n",ST16C554_IRQA,Ret);

 goto st16c554_init_out;
 }
 
printk("Register ST16C554 Driver OK.\n");
 
return Ret;
st16c554_init_out:
 printk("driver register failed. Return %d\n",Ret);
 uart_unregister_driver(&st16c554_reg);
 return Ret;
}
/***************************************
**************************************/
void __exit st16c554uart_exit(void)
{
int i;
struct uart_driver *drv;
struct device *dev;
int Ret;
struct st16c554_uart_port *pPort;
pPort = &st16c554_reg;
drv = &st16c554_reg;
dev = serial16c554_dev;
for (i = 0; i < UART_16C554_NUM; i++)
 {
 pPort = &st16c554_ports[i];
 pPort->port.dev = dev;
 if (&(pPort->port) != NULL)
 uart_remove_one_port(drv, &(pPort->port)); 
 }
//platform_device_unregister(serial16c554_devs);
uart_unregister_driver(&st16c554_reg);
//for (i=0; i<UART_16C554_NUM; i++)
// free_irq(st16c554_ports[i].port.irq,NULL);
free_irq(ST16C554_IRQA,NULL);
printk("Unregister st16c554 serial driver.\n");
}
module_init(st16c554uart_init);
module_exit(st16c554uart_exit);
MODULE_AUTHOR("Jemywolf Research Inc");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic ST16C554 serial driver Revision: 1.0.0");
MODULE_ALIAS_CHARDEV_MAJOR(ST16C554_MAJOR);
 
///////////////////////////////////////////////////
st16c554.h
#ifndef _ST16C554_H
#define _ST16C554_H
/** ************************************************************************* **
** includes
** ************************************************************************* **/
/** ************************************************************************* **

** defines
** ************************************************************************* **/
struct st16c554_uart_port{
struct uart_port port;
struct timer_list timer; /* "no irq" timer */
struct list_head list; /* ports on this IRQ */
unsigned short capabilities; /* port capabilities */
unsigned short bugs; /* port bugs */
unsigned int tx_loadsz; /* transmit fifo load size */
unsigned char acr;
unsigned char ier;
unsigned char lcr;
unsigned char mcr;
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
unsigned char lsr_break_flag;
/*
* We provide a per-port pm hook.
*/
void (*pm)(struct uart_port *port,
unsigned int state, unsigned int old);
};
#define UART_16C554_NR 4
#define UART_16C554_NUM 4
#define ST16C554_MAJOR 9
#define ST16C554_MINOR 70
#define ST16C554_CONSOLE NULL
//#define ST16C554_CONSOLE st16c554_cons
#define ST16C554_DRIVER_NAME "ST16C554-Serial"
#define ST16C554_DEVFS_NAME "tttb/"
#define ST16C554_DEV_NAME "ttyB"
#define ST16C554_PHY_ADDRESS 0x60800000                ///Ncs5 ADDR21~23
#define ST16C554_REMAP_SIZE 0x100000
#define ST16C554_IRQA 25
//#define ST16C554_IRQB 26
//#define ST16C554_IRQC 27
//#define ST16C554_IRQD 30
/*-------- PPC405EP Registers-----------*/
#define CFG_EBC_PB1AP 0x7f8ffe40 //
#define CFG_EBC_PB1CR 0xF0018000 //BAS:0xf00 BS:000 BU:11 BW:00
/*-------- ST16C554 Registers-----------*/
//port = 0--3
#define UART_URXH(port) (*((volatile unsigned char *)((port)->membase + 0x00)))
#define UART_UTXH(port) (*((volatile unsigned char *)((port)->membase + 0x00)))
#define UART_UIER(port) (*((volatile unsigned char *)((port)->membase + 0x01)))
#define UART_UISTAT(port) (*((volatile unsigned char *)((port)->membase + 0x02)))

#define UART_UFCON(port) (*((volatile unsigned char *)((port)->membase + 0x02)))
#define UART_ULCON(port) (*((volatile unsigned char *)((port)->membase + 0x03)))
#define UART_UMCON(port) (*((volatile unsigned char *)((port)->membase + 0x04)))
#define UART_ULSTAT(port) (*((volatile unsigned char *)((port)->membase + 0x05)))

#define UART_UMSTAT(port) (*((volatile unsigned char *)((port)->membase + 0x06)))

#define UART_URSCRR(port) (*((volatile unsigned char *)((port)->membase + 0x07)))

#define UART_UWSCRR(port) (*((volatile unsigned char *)((port)->membase + 0x07)))

#define UART_UBRDIVL(port) (*((volatile unsigned char *)((port)->membase + 0x00)))

#define UART_UBRDIVM(port) (*((volatile unsigned char *)((port)->membase + 0x01)))
#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */
#define UART_CAP_EFR (1 << 9) /* UART has EFR */
#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */
#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */

#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)

/** ************************************************************************* **
** function prototypes
** ************************************************************************* **/
unsigned int st16c554_tx_empty(struct uart_port *port);

void st16c554_set_mctrl(struct uart_port *port, unsigned int mctrl);
unsigned int st16c554_get_mctrl(struct uart_port *port);
void st16c554_stop_tx(struct uart_port *port, unsigned int tty_stop);
void st16c554_start_tx(struct uart_port *portc, unsigned int tty_start);
void st16c554_stop_rx(struct uart_port *port);
void st16c554_enable_ms(struct uart_port *port);
void st16c554_break_ctl(struct uart_port *port, int break_state);
int st16c554_startup(struct uart_port *port);
void st16c554_shutdown(struct uart_port *port);
void st16c554_set_termios(struct uart_port *port, struct termios *termios, struct termios *old);

void st16c554_pm(struct uart_port *port, unsigned int state, unsigned int oldstate);

const char * st16c554_type(struct uart_port *port);
void st16c554_release_port(struct uart_port *port);
int st16c554_request_port(struct uart_port *port);
void st16c554_config_port(struct uart_port *port, int flags);
int st16c554_verify_port(struct uart_port *port, struct serial_struct *ser);
static void tx_interrupt(struct uart_info *info, struct uart_port *port) ;
static void err_interrupt( struct uart_port *port, struct pt_regs *regs);
//static void err_interrupt( struct uart_info *info,struct tty_struct *tty,struct uart_port *port);

//static void rx_interrupt(struct uart_info *info, struct tty_struct *tty, struct uart_port *port);

static void rx_interrupt(struct uart_port *port, struct pt_regs *regs);
static unsigned int st16c554_get_divisor(struct uart_port *port, unsigned int baud);

static void serial16c554_timeout(unsigned long data);
int st16c554_AddressMap(void);
int st16c554_InitUARTPort( struct st16c554_uart_port * pPort);
int st16c554_InitReg( void);
static int serial16c554_register_ports(struct uart_driver *drv, struct device *dev);
//static void check_modem_status(struct uart_info *info, struct uart_port *port);

static void check_modem_status(struct uart_port *port, struct pt_regs *regs);
#endif /* _ST16C554_H*/

抱歉!评论已关闭.