www.pudn.com > ST16C554-linux2.6.14.rar > ST16C554.c
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CONFIG_HUB6 1 #include #include #include #include "ST16C554.h" /*------------ 接口全局变量------------*/ static unsigned char * ST16C554PortBase; static struct platform_device *serial16c554_devs; struct st16c554_uart_port st16c554_ports[UART_16C554_NUM]; static DECLARE_MUTEX(serial16c554_sem); static char PortIsOpen[UART_16C554_NR]; /*------------ 接口操作函数------------*/ 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%02x\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) { struct st16c554_uart_port *up; //printk("Now Start st16c554_stop_tx,line= %d\n",port->line); up=&st16c554_ports[port->line]; up->ier&= ~0x02; UART_UIER(port) = up->ier; return; } /*************************************** **************************************/ void st16c554_start_tx(struct uart_port *port) { 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]; 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); 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_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%08x\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%02x\n",up->ier); UART_ULCON(port)=cval | UART_LCR_DLAB;/* set DLAB;UART_LCR_DLAB=0x80*/ //printk("tx_interrupt->set LCR to 0x%02x\n",cval | UART_LCR_DLAB); UART_UBRDIVL(port)=quot & 0xff; /* LS of divisor */ //printk("tx_interrupt->set DLL to 0x%02x\n",quot & 0xff); UART_UBRDIVM(port)=quot >> 8; /* MS of divisor */ //printk("tx_interrupt->set DLM to 0x%02x\n",quot >> 8); cval&=0x7f; UART_ULCON(port)=cval; /* reset DLAB */ up->lcr = cval; /* Save LCR */ //printk("tx_interrupt->set LCR to 0x%02x\n",cval); fcr=0xcf; UART_UFCON(port)=fcr; /* set fcr */ udelay(10000); fcr=0xc9; UART_UFCON(port)=fcr; /* set fcr */ //printk("tx_interrupt->set FCR to 0x%02x\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 +8)|| (port->mapbase == ST16C554_PHY_ADDRESS +2*8)|| (port->mapbase == ST16C554_PHY_ADDRESS +3*8)){ port->line=(port->mapbase-ST16C554_PHY_ADDRESS)/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->irq = ST16C554_IRQ; //CPU 中断号码 port->uartclk = 1843200; //=1.8432MHz 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_IRQ || 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); //printk("tx_interrupt: head=tail.\n"); return; } do { /**/ while(!(UART_ULSTAT(port)&0x20)){//确保TXH为空 //printk("tx_interrupt->LSR=0x%02x\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); } /*************************************** **************************************/ //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 err_interrupt,LSR=0x%02x\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; } /*************************************** **************************************/ //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; lsr=UART_ULSTAT(port); //printk("Now Start rx_interrupt,LSR=0x%02x\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); 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_char(port, lsr, UART_LSR_OE, ch, flag); 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); } /*************************************** 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(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) { ST16C554PortBase=(unsigned char *)ioremap(ST16C554_PHY_ADDRESS,ST16C554_REMAP_SIZE); if(ST16C554PortBase==NULL){ return -1; } //printk("ST16c554 Base Address:0x%08x is mapped to 0x%08x\n", // (unsigned int)ST16C554_PHY_ADDRESS, // (unsigned int)ST16C554PortBase); return 0; } /*************************************** **************************************/ int st16c554_InitUARTPort( struct st16c554_uart_port * pUARTPort) { int i; struct uart_port *pPort; for(i=0;i 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->mapbase = ST16C554_PHY_ADDRESS + i*8; pPort->irq = ST16C554_IRQ; //CPU 中断号码 pPort->uartclk = 1843200; //=1.8432MHz pPort->fifosize = 16; //发送的FIFO大小 pPort->type = PORT_16550A; 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 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("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%08x irq=%d,regs=0x%08x\n",(unsigned int)dev_id,irq,(unsigned int)regs); for(i=0;i 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%02x\n",i,isr); while(!(isr&0x01)) { isr=(isr&0x0E)>>1; switch(isr) { case 3: err_interrupt(port,regs); break; case 4: 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 1; } /*************************************** **************************************/ //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%02x\n",status); return; } /************----------- 模块初始化相关函数 --------------------***************/ /*************************************** **************************************/ int st16c554uart_init(void) { int Ret; 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; } //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); if(Ret!=0){ printk("register ports failed. Return %d\n",Ret); goto st16c554_init_out; } //printk("Now Start To register interrupt functions.\n"); free_irq(st16c554_ports[0].port.irq,NULL); Ret = request_irq(st16c554_ports[0].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[0].port.irq,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) { platform_device_unregister(serial16c554_devs); uart_unregister_driver(&st16c554_reg); free_irq(st16c554_ports[0].port.irq,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);