#include "sb_pci_mp.h" #include #include extern struct parport *parport_pc_probe_port(unsigned long base_lo, unsigned long base_hi, int irq, int dma, struct device *dev, int irqflags); static struct mp_device_t mp_devs[MAX_MP_DEV]; static int mp_nrpcibrds = sizeof(mp_pciboards)/sizeof(mppcibrd_t); static int NR_BOARD=0; static int NR_PORTS=0; static struct mp_port multi_ports[MAX_MP_PORT]; static struct irq_info irq_lists[NR_IRQS]; static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset); static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value); static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset); static int sb1054_get_register(struct sb_uart_port * port, int page, int reg); static int sb1054_set_register(struct sb_uart_port * port, int page, int reg, int value); static void SendATCommand(struct mp_port * mtpt); static int set_deep_fifo(struct sb_uart_port * port, int status); static int get_deep_fifo(struct sb_uart_port * port); static int get_device_type(int arg); static int set_auto_rts(struct sb_uart_port *port, int status); static void mp_stop(struct tty_struct *tty); static void __mp_start(struct tty_struct *tty); static void mp_start(struct tty_struct *tty); static void mp_tasklet_action(unsigned long data); static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear); static int mp_startup(struct sb_uart_state *state, int init_hw); static void mp_shutdown(struct sb_uart_state *state); static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios); static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c); static int mp_put_char(struct tty_struct *tty, unsigned char ch); static void mp_put_chars(struct tty_struct *tty); static int mp_write(struct tty_struct *tty, const unsigned char * buf, int count); static int mp_write_room(struct tty_struct *tty); static int mp_chars_in_buffer(struct tty_struct *tty); static void mp_flush_buffer(struct tty_struct *tty); static void mp_send_xchar(struct tty_struct *tty, char ch); static void mp_throttle(struct tty_struct *tty); static void mp_unthrottle(struct tty_struct *tty); static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo); static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo); static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value); static int mp_tiocmget(struct tty_struct *tty); static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int mp_break_ctl(struct tty_struct *tty, int break_state); static int mp_do_autoconfig(struct sb_uart_state *state); static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg); static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt); static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios); static void mp_close(struct tty_struct *tty, struct file *filp); static void mp_wait_until_sent(struct tty_struct *tty, int timeout); static void mp_hangup(struct tty_struct *tty); static void mp_update_termios(struct sb_uart_state *state); static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state); static struct sb_uart_state *uart_get(struct uart_driver *drv, int line); static int mp_open(struct tty_struct *tty, struct file *filp); static const char *mp_type(struct sb_uart_port *port); static void mp_change_pm(struct sb_uart_state *state, int pm_state); static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port); static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port); static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state); static int mp_register_driver(struct uart_driver *drv); static void mp_unregister_driver(struct uart_driver *drv); static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port); static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port); static void autoconfig(struct mp_port *mtpt, unsigned int probeflags); static void autoconfig_irq(struct mp_port *mtpt); static void multi_stop_tx(struct sb_uart_port *port); static void multi_start_tx(struct sb_uart_port *port); static void multi_stop_rx(struct sb_uart_port *port); static void multi_enable_ms(struct sb_uart_port *port); static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ); static _INLINE_ void transmit_chars(struct mp_port *mtpt); static _INLINE_ void check_modem_status(struct mp_port *mtpt); static inline void multi_handle_port(struct mp_port *mtpt); static irqreturn_t multi_interrupt(int irq, void *dev_id); static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt); static int serial_link_irq_chain(struct mp_port *mtpt); static void serial_unlink_irq_chain(struct mp_port *mtpt); static void multi_timeout(unsigned long data); static unsigned int multi_tx_empty(struct sb_uart_port *port); static unsigned int multi_get_mctrl(struct sb_uart_port *port); static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl); static void multi_break_ctl(struct sb_uart_port *port, int break_state); static int multi_startup(struct sb_uart_port *port); static void multi_shutdown(struct sb_uart_port *port); static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud); static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old); static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate); static void multi_release_std_resource(struct mp_port *mtpt); static void multi_release_port(struct sb_uart_port *port); static int multi_request_port(struct sb_uart_port *port); static void multi_config_port(struct sb_uart_port *port, int flags); static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser); static const char * multi_type(struct sb_uart_port *port); static void __init multi_init_ports(void); static void __init multi_register_ports(struct uart_driver *drv); static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd); static int deep[256]; static int deep_count; static int fcr_arr[256]; static int fcr_count; static int ttr[256]; static int ttr_count; static int rtr[256]; static int rtr_count; module_param_array(deep,int,&deep_count,0); module_param_array(fcr_arr,int,&fcr_count,0); module_param_array(ttr,int,&ttr_count,0); module_param_array(rtr,int,&rtr_count,0); static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset) { return inb(mtpt->port.iobase + offset); } static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value) { outb(value, mtpt->port.iobase + offset); } static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset) { return inb(mtpt->option_base_addr + offset); } static int sb1053a_get_interface(struct mp_port *mtpt, int port_num) { unsigned long option_base_addr = mtpt->option_base_addr; unsigned int interface = 0; switch (port_num) { case 0: case 1: /* set GPO[1:0] = 00 */ outb(0x00, option_base_addr + MP_OPTR_GPODR); break; case 2: case 3: /* set GPO[1:0] = 01 */ outb(0x01, option_base_addr + MP_OPTR_GPODR); break; case 4: case 5: /* set GPO[1:0] = 10 */ outb(0x02, option_base_addr + MP_OPTR_GPODR); break; default: break; } port_num &= 0x1; /* get interface */ interface = inb(option_base_addr + MP_OPTR_IIR0 + port_num); /* set GPO[1:0] = 11 */ outb(0x03, option_base_addr + MP_OPTR_GPODR); return (interface); } static int sb1054_get_register(struct sb_uart_port * port, int page, int reg) { int ret = 0; unsigned int lcr = 0; unsigned int mcr = 0; unsigned int tmp = 0; if( page <= 0) { printk(" page 0 can not use this fuction\n"); return -1; } switch(page) { case 1: lcr = SB105X_GET_LCR(port); tmp = lcr | SB105X_LCR_DLAB; SB105X_PUT_LCR(port, tmp); tmp = SB105X_GET_LCR(port); ret = SB105X_GET_REG(port,reg); SB105X_PUT_LCR(port,lcr); break; case 2: mcr = SB105X_GET_MCR(port); tmp = mcr | SB105X_MCR_P2S; SB105X_PUT_MCR(port,tmp); ret = SB105X_GET_REG(port,reg); SB105X_PUT_MCR(port,mcr); break; case 3: lcr = SB105X_GET_LCR(port); tmp = lcr | SB105X_LCR_BF; SB105X_PUT_LCR(port,tmp); SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P3KEY); ret = SB105X_GET_REG(port,reg); SB105X_PUT_LCR(port,lcr); break; case 4: lcr = SB105X_GET_LCR(port); tmp = lcr | SB105X_LCR_BF; SB105X_PUT_LCR(port,tmp); SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P4KEY); ret = SB105X_GET_REG(port,reg); SB105X_PUT_LCR(port,lcr); break; default: printk(" error invalid page number \n"); return -1; } return ret; } static int sb1054_set_register(struct sb_uart_port * port, int page, int reg, int value) { int lcr = 0; int mcr = 0; int ret = 0; if( page <= 0) { printk(" page 0 can not use this fuction\n"); return -1; } switch(page) { case 1: lcr = SB105X_GET_LCR(port); SB105X_PUT_LCR(port, lcr | SB105X_LCR_DLAB); SB105X_PUT_REG(port,reg,value); SB105X_PUT_LCR(port, lcr); ret = 1; break; case 2: mcr = SB105X_GET_MCR(port); SB105X_PUT_MCR(port, mcr | SB105X_MCR_P2S); SB105X_PUT_REG(port,reg,value); SB105X_PUT_MCR(port, mcr); ret = 1; break; case 3: lcr = SB105X_GET_LCR(port); SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF); SB105X_PUT_PSR(port, SB105X_PSR_P3KEY); SB105X_PUT_REG(port,reg,value); SB105X_PUT_LCR(port, lcr); ret = 1; break; case 4: lcr = SB105X_GET_LCR(port); SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF); SB105X_PUT_PSR(port, SB105X_PSR_P4KEY); SB105X_PUT_REG(port,reg,value); SB105X_PUT_LCR(port, lcr); ret = 1; break; default: printk(" error invalid page number \n"); return -1; } return ret; } static int set_multidrop_mode(struct sb_uart_port *port, unsigned int mode) { int mdr = SB105XA_MDR_NPS; if (mode & MDMODE_ENABLE) { mdr |= SB105XA_MDR_MDE; } if (1) //(mode & MDMODE_AUTO) { int efr = 0; mdr |= SB105XA_MDR_AME; efr = sb1054_get_register(port, PAGE_3, SB105X_EFR); efr |= SB105X_EFR_SCD; sb1054_set_register(port, PAGE_3, SB105X_EFR, efr); } sb1054_set_register(port, PAGE_1, SB105XA_MDR, mdr); port->mdmode &= ~0x6; port->mdmode |= mode; printk("[%d] multidrop init: %x\n", port->line, port->mdmode); return 0; } static int get_multidrop_addr(struct sb_uart_port *port) { return sb1054_get_register(port, PAGE_3, SB105X_XOFF2); } static int set_multidrop_addr(struct sb_uart_port *port, unsigned int addr) { sb1054_set_register(port, PAGE_3, SB105X_XOFF2, addr); return 0; } static void SendATCommand(struct mp_port * mtpt) { // a t cr lf unsigned char ch[] = {0x61,0x74,0x0d,0x0a,0x0}; unsigned char lineControl; unsigned char i=0; unsigned char Divisor = 0xc; lineControl = serial_inp(mtpt,UART_LCR); serial_outp(mtpt,UART_LCR,(lineControl | UART_LCR_DLAB)); serial_outp(mtpt,UART_DLL,(Divisor & 0xff)); serial_outp(mtpt,UART_DLM,(Divisor & 0xff00)>>8); //baudrate is 4800 serial_outp(mtpt,UART_LCR,lineControl); serial_outp(mtpt,UART_LCR,0x03); // N-8-1 serial_outp(mtpt,UART_FCR,7); serial_outp(mtpt,UART_MCR,0x3); while(ch[i]){ while((serial_inp(mtpt,UART_LSR) & 0x60) !=0x60){ ; } serial_outp(mtpt,0,ch[i++]); } }// end of SendATCommand() static int set_deep_fifo(struct sb_uart_port * port, int status) { int afr_status = 0; afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); if(status == ENABLE) { afr_status |= SB105X_AFR_AFEN; } else { afr_status &= ~SB105X_AFR_AFEN; } sb1054_set_register(port,PAGE_4,SB105X_AFR,afr_status); sb1054_set_register(port,PAGE_4,SB105X_TTR,ttr[port->line]); sb1054_set_register(port,PAGE_4,SB105X_RTR,rtr[port->line]); afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); return afr_status; } static int get_device_type(int arg) { int ret; ret = inb(mp_devs[arg].option_reg_addr+MP_OPTR_DIR0); ret = (ret & 0xf0) >> 4; switch (ret) { case DIR_UART_16C550: return PORT_16C55X; case DIR_UART_16C1050: return PORT_16C105X; case DIR_UART_16C1050A: /* if (mtpt->port.line < 2) { return PORT_16C105XA; } else { if (mtpt->device->device_id & 0x50) { return PORT_16C55X; } else { return PORT_16C105X; } }*/ return PORT_16C105XA; default: return PORT_UNKNOWN; } } static int get_deep_fifo(struct sb_uart_port * port) { int afr_status = 0; afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); return afr_status; } static int set_auto_rts(struct sb_uart_port *port, int status) { int atr_status = 0; #if 0 int efr_status = 0; efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR); if(status == ENABLE) efr_status |= SB105X_EFR_ARTS; else efr_status &= ~SB105X_EFR_ARTS; sb1054_set_register(port,PAGE_3,SB105X_EFR,efr_status); efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR); #endif //ATR atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR); switch(status) { case RS422PTP: atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_A80); break; case RS422MD: atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); break; case RS485NE: atr_status = (SB105X_ATR_RCMS) | (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); break; case RS485ECHO: atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); break; } sb1054_set_register(port,PAGE_3,SB105X_ATR,atr_status); atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR); return atr_status; } static void mp_stop(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->ops->stop_tx(port); spin_unlock_irqrestore(&port->lock, flags); } static void __mp_start(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port); } static void mp_start(struct tty_struct *tty) { __mp_start(tty); } static void mp_tasklet_action(unsigned long data) { struct sb_uart_state *state = (struct sb_uart_state *)data; struct tty_struct *tty; printk("tasklet is called!\n"); tty = state->info->tty; tty_wakeup(tty); } static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear) { unsigned int old; old = port->mctrl; port->mctrl = (old & ~clear) | set; if (old != port->mctrl) port->ops->set_mctrl(port, port->mctrl); } #define uart_set_mctrl(port,set) mp_update_mctrl(port,set,0) #define uart_clear_mctrl(port,clear) mp_update_mctrl(port,0,clear) static int mp_startup(struct sb_uart_state *state, int init_hw) { struct sb_uart_info *info = state->info; struct sb_uart_port *port = state->port; unsigned long page; int retval = 0; if (info->flags & UIF_INITIALIZED) return 0; if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); if (port->type == PORT_UNKNOWN) return 0; if (!info->xmit.buf) { page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; info->xmit.buf = (unsigned char *) page; uart_circ_clear(&info->xmit); } retval = port->ops->startup(port); if (retval == 0) { if (init_hw) { mp_change_speed(state, NULL); if (info->tty->termios.c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); } info->flags |= UIF_INITIALIZED; clear_bit(TTY_IO_ERROR, &info->tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) retval = 0; return retval; } static void mp_shutdown(struct sb_uart_state *state) { struct sb_uart_info *info = state->info; struct sb_uart_port *port = state->port; if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); if (info->flags & UIF_INITIALIZED) { info->flags &= ~UIF_INITIALIZED; if (!info->tty || (info->tty->termios.c_cflag & HUPCL)) uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); wake_up_interruptible(&info->delta_msr_wait); port->ops->shutdown(port); synchronize_irq(port->irq); } tasklet_kill(&info->tlet); if (info->xmit.buf) { free_page((unsigned long)info->xmit.buf); info->xmit.buf = NULL; } } static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios) { struct tty_struct *tty = state->info->tty; struct sb_uart_port *port = state->port; if (!tty || port->type == PORT_UNKNOWN) return; if (tty->termios.c_cflag & CRTSCTS) state->info->flags |= UIF_CTS_FLOW; else state->info->flags &= ~UIF_CTS_FLOW; if (tty->termios.c_cflag & CLOCAL) state->info->flags &= ~UIF_CHECK_CD; else state->info->flags |= UIF_CHECK_CD; port->ops->set_termios(port, &tty->termios, old_termios); } static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c) { unsigned long flags; int ret = 0; if (!circ->buf) return 0; spin_lock_irqsave(&port->lock, flags); if (uart_circ_chars_free(circ) != 0) { circ->buf[circ->head] = c; circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); ret = 1; } spin_unlock_irqrestore(&port->lock, flags); return ret; } static int mp_put_char(struct tty_struct *tty, unsigned char ch) { struct sb_uart_state *state = tty->driver_data; return __mp_put_char(state->port, &state->info->xmit, ch); } static void mp_put_chars(struct tty_struct *tty) { mp_start(tty); } static int mp_write(struct tty_struct *tty, const unsigned char * buf, int count) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port; struct circ_buf *circ; int c, ret = 0; if (!state || !state->info) { return -EL3HLT; } port = state->port; circ = &state->info->xmit; if (!circ->buf) return 0; while (1) { c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); if (count < c) c = count; if (c <= 0) break; memcpy(circ->buf + circ->head, buf, c); circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); buf += c; count -= c; ret += c; } mp_start(tty); return ret; } static int mp_write_room(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; return uart_circ_chars_free(&state->info->xmit); } static int mp_chars_in_buffer(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; return uart_circ_chars_pending(&state->info->xmit); } static void mp_flush_buffer(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port; unsigned long flags; if (!state || !state->info) { return; } port = state->port; spin_lock_irqsave(&port->lock, flags); uart_circ_clear(&state->info->xmit); spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&tty->write_wait); tty_wakeup(tty); } static void mp_send_xchar(struct tty_struct *tty, char ch) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; unsigned long flags; if (port->ops->send_xchar) port->ops->send_xchar(port, ch); else { port->x_char = ch; if (ch) { spin_lock_irqsave(&port->lock, flags); port->ops->start_tx(port); spin_unlock_irqrestore(&port->lock, flags); } } } static void mp_throttle(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; if (I_IXOFF(tty)) mp_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios.c_cflag & CRTSCTS) uart_clear_mctrl(state->port, TIOCM_RTS); } static void mp_unthrottle(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; if (I_IXOFF(tty)) { if (port->x_char) port->x_char = 0; else mp_send_xchar(tty, START_CHAR(tty)); } if (tty->termios.c_cflag & CRTSCTS) uart_set_mctrl(port, TIOCM_RTS); } static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo) { struct sb_uart_port *port = state->port; struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); tmp.type = port->type; tmp.line = port->line; tmp.port = port->iobase; if (HIGH_BITS_OFFSET) tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; tmp.irq = port->irq; tmp.flags = port->flags; tmp.xmit_fifo_size = port->fifosize; tmp.baud_base = port->uartclk / 16; tmp.close_delay = state->close_delay; tmp.closing_wait = state->closing_wait == USF_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : state->closing_wait; tmp.custom_divisor = port->custom_divisor; tmp.hub6 = port->hub6; tmp.io_type = port->iotype; tmp.iomem_reg_shift = port->regshift; tmp.iomem_base = (void *)port->mapbase; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; } static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo) { struct serial_struct new_serial; struct sb_uart_port *port = state->port; unsigned long new_port; unsigned int change_irq, change_port, closing_wait; unsigned int old_custom_divisor; unsigned int old_flags, new_flags; int retval = 0; if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) return -EFAULT; new_port = new_serial.port; if (HIGH_BITS_OFFSET) new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; new_serial.irq = irq_canonicalize(new_serial.irq); closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? USF_CLOSING_WAIT_NONE : new_serial.closing_wait; MP_STATE_LOCK(state); change_irq = new_serial.irq != port->irq; change_port = new_port != port->iobase || (unsigned long)new_serial.iomem_base != port->mapbase || new_serial.hub6 != port->hub6 || new_serial.io_type != port->iotype || new_serial.iomem_reg_shift != port->regshift || new_serial.type != port->type; old_flags = port->flags; new_flags = new_serial.flags; old_custom_divisor = port->custom_divisor; if (!capable(CAP_SYS_ADMIN)) { retval = -EPERM; if (change_irq || change_port || (new_serial.baud_base != port->uartclk / 16) || (new_serial.close_delay != state->close_delay) || (closing_wait != state->closing_wait) || (new_serial.xmit_fifo_size != port->fifosize) || (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) goto exit; port->flags = ((port->flags & ~UPF_USR_MASK) | (new_flags & UPF_USR_MASK)); port->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } if (port->ops->verify_port) retval = port->ops->verify_port(port, &new_serial); if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || (new_serial.baud_base < 9600)) retval = -EINVAL; if (retval) goto exit; if (change_port || change_irq) { retval = -EBUSY; if (uart_users(state) > 1) goto exit; mp_shutdown(state); } if (change_port) { unsigned long old_iobase, old_mapbase; unsigned int old_type, old_iotype, old_hub6, old_shift; old_iobase = port->iobase; old_mapbase = port->mapbase; old_type = port->type; old_hub6 = port->hub6; old_iotype = port->iotype; old_shift = port->regshift; if (old_type != PORT_UNKNOWN) port->ops->release_port(port); port->iobase = new_port; port->type = new_serial.type; port->hub6 = new_serial.hub6; port->iotype = new_serial.io_type; port->regshift = new_serial.iomem_reg_shift; port->mapbase = (unsigned long)new_serial.iomem_base; if (port->type != PORT_UNKNOWN) { retval = port->ops->request_port(port); } else { retval = 0; } if (retval && old_type != PORT_UNKNOWN) { port->iobase = old_iobase; port->type = old_type; port->hub6 = old_hub6; port->iotype = old_iotype; port->regshift = old_shift; port->mapbase = old_mapbase; retval = port->ops->request_port(port); if (retval) port->type = PORT_UNKNOWN; retval = -EBUSY; } } port->irq = new_serial.irq; port->uartclk = new_serial.baud_base * 16; port->flags = (port->flags & ~UPF_CHANGE_MASK) | (new_flags & UPF_CHANGE_MASK); port->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay; state->closing_wait = closing_wait; port->fifosize = new_serial.xmit_fifo_size; if (state->info->tty) state->info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; if (port->type == PORT_UNKNOWN) goto exit; if (state->info->flags & UIF_INITIALIZED) { if (((old_flags ^ port->flags) & UPF_SPD_MASK) || old_custom_divisor != port->custom_divisor) { if (port->flags & UPF_SPD_MASK) { printk(KERN_NOTICE "%s sets custom speed on ttyMP%d. This " "is deprecated.\n", current->comm, port->line); } mp_change_speed(state, NULL); } } else retval = mp_startup(state, 1); exit: MP_STATE_UNLOCK(state); return retval; } static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value) { struct sb_uart_port *port = state->port; unsigned int result; result = port->ops->tx_empty(port); if (port->x_char || ((uart_circ_chars_pending(&state->info->xmit) > 0) && !state->info->tty->stopped && !state->info->tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); } static int mp_tiocmget(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; int result = -EIO; MP_STATE_LOCK(state); if (!(tty->flags & (1 << TTY_IO_ERROR))) { result = port->mctrl; spin_lock_irq(&port->lock); result |= port->ops->get_mctrl(port); spin_unlock_irq(&port->lock); } MP_STATE_UNLOCK(state); return result; } static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; int ret = -EIO; MP_STATE_LOCK(state); if (!(tty->flags & (1 << TTY_IO_ERROR))) { mp_update_mctrl(port, set, clear); ret = 0; } MP_STATE_UNLOCK(state); return ret; } static int mp_break_ctl(struct tty_struct *tty, int break_state) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; MP_STATE_LOCK(state); if (port->type != PORT_UNKNOWN) port->ops->break_ctl(port, break_state); MP_STATE_UNLOCK(state); return 0; } static int mp_do_autoconfig(struct sb_uart_state *state) { struct sb_uart_port *port = state->port; int flags, ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (mutex_lock_interruptible(&state->mutex)) return -ERESTARTSYS; ret = -EBUSY; if (uart_users(state) == 1) { mp_shutdown(state); if (port->type != PORT_UNKNOWN) port->ops->release_port(port); flags = UART_CONFIG_TYPE; if (port->flags & UPF_AUTO_IRQ) flags |= UART_CONFIG_IRQ; port->ops->config_port(port, flags); ret = mp_startup(state, 1); } MP_STATE_UNLOCK(state); return ret; } static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg) { struct sb_uart_port *port = state->port; DECLARE_WAITQUEUE(wait, current); struct sb_uart_icount cprev, cnow; int ret; spin_lock_irq(&port->lock); memcpy(&cprev, &port->icount, sizeof(struct sb_uart_icount)); port->ops->enable_ms(port); spin_unlock_irq(&port->lock); add_wait_queue(&state->info->delta_msr_wait, &wait); for (;;) { spin_lock_irq(&port->lock); memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount)); spin_unlock_irq(&port->lock); set_current_state(TASK_INTERRUPTIBLE); if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { ret = 0; break; } schedule(); if (signal_pending(current)) { ret = -ERESTARTSYS; break; } cprev = cnow; } current->state = TASK_RUNNING; remove_wait_queue(&state->info->delta_msr_wait, &wait); return ret; } static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt) { struct serial_icounter_struct icount; struct sb_uart_icount cnow; struct sb_uart_port *port = state->port; spin_lock_irq(&port->lock); memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount)); spin_unlock_irq(&port->lock); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; icount.dcd = cnow.dcd; icount.rx = cnow.rx; icount.tx = cnow.tx; icount.frame = cnow.frame; icount.overrun = cnow.overrun; icount.parity = cnow.parity; icount.brk = cnow.brk; icount.buf_overrun = cnow.buf_overrun; return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; } static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct sb_uart_state *state = tty->driver_data; struct mp_port *info = (struct mp_port *)state->port; int ret = -ENOIOCTLCMD; switch (cmd) { case TIOCSMULTIDROP: /* set multi-drop mode enable or disable, and default operation mode is H/W mode */ if (info->port.type == PORT_16C105XA) { //arg &= ~0x6; //state->port->mdmode = 0; return set_multidrop_mode((struct sb_uart_port *)info, (unsigned int)arg); } ret = -ENOTSUPP; break; case GETDEEPFIFO: ret = get_deep_fifo(state->port); return ret; case SETDEEPFIFO: ret = set_deep_fifo(state->port,arg); deep[state->port->line] = arg; return ret; case SETTTR: if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ ret = sb1054_set_register(state->port,PAGE_4,SB105X_TTR,arg); ttr[state->port->line] = arg; } return ret; case SETRTR: if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ ret = sb1054_set_register(state->port,PAGE_4,SB105X_RTR,arg); rtr[state->port->line] = arg; } return ret; case GETTTR: if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ ret = sb1054_get_register(state->port,PAGE_4,SB105X_TTR); } return ret; case GETRTR: if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ ret = sb1054_get_register(state->port,PAGE_4,SB105X_RTR); } return ret; case SETFCR: if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ ret = sb1054_set_register(state->port,PAGE_1,SB105X_FCR,arg); } else{ serial_out(info,2,arg); } return ret; case TIOCSMDADDR: /* set multi-drop address */ if (info->port.type == PORT_16C105XA) { state->port->mdmode |= MDMODE_ADDR; return set_multidrop_addr((struct sb_uart_port *)info, (unsigned int)arg); } ret = -ENOTSUPP; break; case TIOCGMDADDR: /* set multi-drop address */ if ((info->port.type == PORT_16C105XA) && (state->port->mdmode & MDMODE_ADDR)) { return get_multidrop_addr((struct sb_uart_port *)info); } ret = -ENOTSUPP; break; case TIOCSENDADDR: /* send address in multi-drop mode */ if ((info->port.type == PORT_16C105XA) && (state->port->mdmode & (MDMODE_ENABLE))) { if (mp_chars_in_buffer(tty) > 0) { tty_wait_until_sent(tty, 0); } //while ((serial_in(info, UART_LSR) & 0x60) != 0x60); //while (sb1054_get_register(state->port, PAGE_2, SB105X_TFCR) != 0); while ((serial_in(info, UART_LSR) & 0x60) != 0x60); serial_out(info, UART_SCR, (int)arg); } break; case TIOCGSERIAL: ret = mp_get_info(state, (struct serial_struct *)arg); break; case TIOCSSERIAL: ret = mp_set_info(state, (struct serial_struct *)arg); break; case TIOCSERCONFIG: ret = mp_do_autoconfig(state); break; case TIOCSERGWILD: /* obsolete */ case TIOCSERSWILD: /* obsolete */ ret = 0; break; /* for Multiport */ case TIOCGNUMOFPORT: /* Get number of ports */ return NR_PORTS; case TIOCGGETDEVID: return mp_devs[arg].device_id; case TIOCGGETREV: return mp_devs[arg].revision; case TIOCGGETNRPORTS: return mp_devs[arg].nr_ports; case TIOCGGETBDNO: return NR_BOARD; case TIOCGGETINTERFACE: if (mp_devs[arg].revision == 0xc0) { /* for SB16C1053APCI */ return (sb1053a_get_interface(info, info->port.line)); } else { return (inb(mp_devs[arg].option_reg_addr+MP_OPTR_IIR0+(state->port->line/8))); } case TIOCGGETPORTTYPE: ret = get_device_type(arg);; return ret; case TIOCSMULTIECHO: /* set to multi-drop mode(RS422) or echo mode(RS485)*/ outb( ( inb(info->interface_config_addr) & ~0x03 ) | 0x01 , info->interface_config_addr); return 0; case TIOCSPTPNOECHO: /* set to multi-drop mode(RS422) or echo mode(RS485) */ outb( ( inb(info->interface_config_addr) & ~0x03 ) , info->interface_config_addr); return 0; } if (ret != -ENOIOCTLCMD) goto out; if (tty->flags & (1 << TTY_IO_ERROR)) { ret = -EIO; goto out; } switch (cmd) { case TIOCMIWAIT: ret = mp_wait_modem_status(state, arg); break; case TIOCGICOUNT: ret = mp_get_count(state, (struct serial_icounter_struct *)arg); break; } if (ret != -ENOIOCTLCMD) goto out; MP_STATE_LOCK(state); switch (cmd) { case TIOCSERGETLSR: /* Get line status register */ ret = mp_get_lsr_info(state, (unsigned int *)arg); break; default: { struct sb_uart_port *port = state->port; if (port->ops->ioctl) ret = port->ops->ioctl(port, cmd, arg); break; } } MP_STATE_UNLOCK(state); out: return ret; } static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios) { struct sb_uart_state *state = tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios.c_cflag; #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) if ((cflag ^ old_termios->c_cflag) == 0 && RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) return; mp_change_speed(state, old_termios); if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { unsigned int mask = TIOCM_DTR; if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; uart_set_mctrl(state->port, mask); } if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { spin_lock_irqsave(&state->port->lock, flags); tty->hw_stopped = 0; __mp_start(tty); spin_unlock_irqrestore(&state->port->lock, flags); } if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { spin_lock_irqsave(&state->port->lock, flags); if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) { tty->hw_stopped = 1; state->port->ops->stop_tx(state->port); } spin_unlock_irqrestore(&state->port->lock, flags); } } static void mp_close(struct tty_struct *tty, struct file *filp) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port; printk("mp_close!\n"); if (!state || !state->port) return; port = state->port; printk("close1 %d\n", __LINE__); MP_STATE_LOCK(state); printk("close2 %d\n", __LINE__); if (tty_hung_up_p(filp)) goto done; printk("close3 %d\n", __LINE__); if ((tty->count == 1) && (state->count != 1)) { printk("mp_close: bad serial port count; tty->count is 1, " "state->count is %d\n", state->count); state->count = 1; } printk("close4 %d\n", __LINE__); if (--state->count < 0) { printk("rs_close: bad serial port count for ttyMP%d: %d\n", port->line, state->count); state->count = 0; } if (state->count) goto done; tty->closing = 1; printk("close5 %d\n", __LINE__); if (state->closing_wait != USF_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, state->closing_wait); printk("close6 %d\n", __LINE__); if (state->info->flags & UIF_INITIALIZED) { unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->ops->stop_rx(port); spin_unlock_irqrestore(&port->lock, flags); mp_wait_until_sent(tty, port->timeout); } printk("close7 %d\n", __LINE__); mp_shutdown(state); printk("close8 %d\n", __LINE__); mp_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; state->info->tty = NULL; if (state->info->blocked_open) { if (state->close_delay) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(state->close_delay); } } else { mp_change_pm(state, 3); } printk("close8 %d\n", __LINE__); state->info->flags &= ~UIF_NORMAL_ACTIVE; wake_up_interruptible(&state->info->open_wait); done: printk("close done\n"); MP_STATE_UNLOCK(state); module_put(THIS_MODULE); } static void mp_wait_until_sent(struct tty_struct *tty, int timeout) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; unsigned long char_time, expire; if (port->type == PORT_UNKNOWN || port->fifosize == 0) return; char_time = (port->timeout - HZ/50) / port->fifosize; char_time = char_time / 5; if (char_time == 0) char_time = 1; if (timeout && timeout < char_time) char_time = timeout; if (timeout == 0 || timeout > 2 * port->timeout) timeout = 2 * port->timeout; expire = jiffies + timeout; while (!port->ops->tx_empty(port)) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(char_time); if (signal_pending(current)) break; if (time_after(jiffies, expire)) break; } set_current_state(TASK_RUNNING); /* might not be needed */ } static void mp_hangup(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; MP_STATE_LOCK(state); if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { mp_flush_buffer(tty); mp_shutdown(state); state->count = 0; state->info->flags &= ~UIF_NORMAL_ACTIVE; state->info->tty = NULL; wake_up_interruptible(&state->info->open_wait); wake_up_interruptible(&state->info->delta_msr_wait); } MP_STATE_UNLOCK(state); } static void mp_update_termios(struct sb_uart_state *state) { struct tty_struct *tty = state->info->tty; struct sb_uart_port *port = state->port; if (!(tty->flags & (1 << TTY_IO_ERROR))) { mp_change_speed(state, NULL); if (tty->termios.c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } } static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state) { DECLARE_WAITQUEUE(wait, current); struct sb_uart_info *info = state->info; struct sb_uart_port *port = state->port; unsigned int mctrl; info->blocked_open++; state->count--; add_wait_queue(&info->open_wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || info->tty == NULL) break; if (!(info->flags & UIF_INITIALIZED)) break; if ((filp->f_flags & O_NONBLOCK) || (info->tty->termios.c_cflag & CLOCAL) || (info->tty->flags & (1 << TTY_IO_ERROR))) { break; } if (info->tty->termios.c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_DTR); spin_lock_irq(&port->lock); port->ops->enable_ms(port); mctrl = port->ops->get_mctrl(port); spin_unlock_irq(&port->lock); if (mctrl & TIOCM_CAR) break; MP_STATE_UNLOCK(state); schedule(); MP_STATE_LOCK(state); if (signal_pending(current)) break; } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); state->count++; info->blocked_open--; if (signal_pending(current)) return -ERESTARTSYS; if (!info->tty || tty_hung_up_p(filp)) return -EAGAIN; return 0; } static struct sb_uart_state *uart_get(struct uart_driver *drv, int line) { struct sb_uart_state *state; MP_MUTEX_LOCK(mp_mutex); state = drv->state + line; if (mutex_lock_interruptible(&state->mutex)) { state = ERR_PTR(-ERESTARTSYS); goto out; } state->count++; if (!state->port) { state->count--; MP_STATE_UNLOCK(state); state = ERR_PTR(-ENXIO); goto out; } if (!state->info) { state->info = kmalloc(sizeof(struct sb_uart_info), GFP_KERNEL); if (state->info) { memset(state->info, 0, sizeof(struct sb_uart_info)); init_waitqueue_head(&state->info->open_wait); init_waitqueue_head(&state->info->delta_msr_wait); state->port->info = state->info; tasklet_init(&state->info->tlet, mp_tasklet_action, (unsigned long)state); } else { state->count--; MP_STATE_UNLOCK(state); state = ERR_PTR(-ENOMEM); } } out: MP_MUTEX_UNLOCK(mp_mutex); return state; } static int mp_open(struct tty_struct *tty, struct file *filp) { struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; struct sb_uart_state *state; int retval; int line = tty->index; struct mp_port *mtpt; retval = -ENODEV; if (line >= tty->driver->num) goto fail; state = uart_get(drv, line); if (IS_ERR(state)) { retval = PTR_ERR(state); goto fail; } mtpt = (struct mp_port *)state->port; tty->driver_data = state; tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; state->info->tty = tty; if (tty_hung_up_p(filp)) { retval = -EAGAIN; state->count--; MP_STATE_UNLOCK(state); goto fail; } if (state->count == 1) mp_change_pm(state, 0); retval = mp_startup(state, 0); if (retval == 0) retval = mp_block_til_ready(filp, state); MP_STATE_UNLOCK(state); if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { state->info->flags |= UIF_NORMAL_ACTIVE; mp_update_termios(state); } uart_clear_mctrl(state->port, TIOCM_RTS); try_module_get(THIS_MODULE); fail: return retval; } static const char *mp_type(struct sb_uart_port *port) { const char *str = NULL; if (port->ops->type) str = port->ops->type(port); if (!str) str = "unknown"; return str; } static void mp_change_pm(struct sb_uart_state *state, int pm_state) { struct sb_uart_port *port = state->port; if (port->ops->pm) port->ops->pm(port, pm_state, state->pm_state); state->pm_state = pm_state; } static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port) { char address[64]; switch (port->iotype) { case UPIO_PORT: snprintf(address, sizeof(address),"I/O 0x%x", port->iobase); break; case UPIO_HUB6: snprintf(address, sizeof(address),"I/O 0x%x offset 0x%x", port->iobase, port->hub6); break; case UPIO_MEM: snprintf(address, sizeof(address),"MMIO 0x%lx", port->mapbase); break; default: snprintf(address, sizeof(address),"*unknown*" ); strlcpy(address, "*unknown*", sizeof(address)); break; } printk( "%s%d at %s (irq = %d) is a %s\n", drv->dev_name, port->line, address, port->irq, mp_type(port)); } static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port) { unsigned int flags; if (!port->iobase && !port->mapbase && !port->membase) { DPRINTK("%s error \n",__FUNCTION__); return; } flags = UART_CONFIG_TYPE; if (port->flags & UPF_AUTO_IRQ) flags |= UART_CONFIG_IRQ; if (port->flags & UPF_BOOT_AUTOCONF) { port->type = PORT_UNKNOWN; port->ops->config_port(port, flags); } if (port->type != PORT_UNKNOWN) { unsigned long flags; mp_report_port(drv, port); spin_lock_irqsave(&port->lock, flags); port->ops->set_mctrl(port, 0); spin_unlock_irqrestore(&port->lock, flags); mp_change_pm(state, 3); } } static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state) { struct sb_uart_port *port = state->port; struct sb_uart_info *info = state->info; if (info && info->tty) tty_hangup(info->tty); MP_STATE_LOCK(state); state->info = NULL; if (port->type != PORT_UNKNOWN) port->ops->release_port(port); port->type = PORT_UNKNOWN; if (info) { tasklet_kill(&info->tlet); kfree(info); } MP_STATE_UNLOCK(state); } static struct tty_operations mp_ops = { .open = mp_open, .close = mp_close, .write = mp_write, .put_char = mp_put_char, .flush_chars = mp_put_chars, .write_room = mp_write_room, .chars_in_buffer= mp_chars_in_buffer, .flush_buffer = mp_flush_buffer, .ioctl = mp_ioctl, .throttle = mp_throttle, .unthrottle = mp_unthrottle, .send_xchar = mp_send_xchar, .set_termios = mp_set_termios, .stop = mp_stop, .start = mp_start, .hangup = mp_hangup, .break_ctl = mp_break_ctl, .wait_until_sent= mp_wait_until_sent, #ifdef CONFIG_PROC_FS .proc_fops = NULL, #endif .tiocmget = mp_tiocmget, .tiocmset = mp_tiocmset, }; static int mp_register_driver(struct uart_driver *drv) { struct tty_driver *normal = NULL; int i, retval; drv->state = kmalloc(sizeof(struct sb_uart_state) * drv->nr, GFP_KERNEL); retval = -ENOMEM; if (!drv->state) { printk("SB PCI Error: Kernel memory allocation error!\n"); goto out; } memset(drv->state, 0, sizeof(struct sb_uart_state) * drv->nr); normal = alloc_tty_driver(drv->nr); if (!normal) { printk("SB PCI Error: tty allocation error!\n"); goto out; } drv->tty_driver = normal; normal->owner = drv->owner; normal->magic = TTY_DRIVER_MAGIC; normal->driver_name = drv->driver_name; normal->name = drv->dev_name; normal->major = drv->major; normal->minor_start = drv->minor; normal->num = MAX_MP_PORT ; normal->type = TTY_DRIVER_TYPE_SERIAL; normal->subtype = SERIAL_TYPE_NORMAL; normal->init_termios = tty_std_termios; normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; normal->driver_state = drv; tty_set_operations(normal, &mp_ops); for (i = 0; i < drv->nr; i++) { struct sb_uart_state *state = drv->state + i; state->close_delay = 500; state->closing_wait = 30000; mutex_init(&state->mutex); } retval = tty_register_driver(normal); out: if (retval < 0) { printk("Register tty driver Fail!\n"); put_tty_driver(normal); kfree(drv->state); } return retval; } void mp_unregister_driver(struct uart_driver *drv) { struct tty_driver *normal = NULL; normal = drv->tty_driver; if (!normal) { return; } tty_unregister_driver(normal); put_tty_driver(normal); drv->tty_driver = NULL; if (drv->state) { kfree(drv->state); } } static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port) { struct sb_uart_state *state; int ret = 0; if (port->line >= drv->nr) return -EINVAL; state = drv->state + port->line; MP_MUTEX_LOCK(mp_mutex); if (state->port) { ret = -EINVAL; goto out; } state->port = port; spin_lock_init(&port->lock); port->cons = drv->cons; port->info = state->info; mp_configure_port(drv, state, port); tty_register_device(drv->tty_driver, port->line, port->dev); out: MP_MUTEX_UNLOCK(mp_mutex); return ret; } static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port) { struct sb_uart_state *state = drv->state + port->line; if (state->port != port) printk(KERN_ALERT "Removing wrong port: %p != %p\n", state->port, port); MP_MUTEX_LOCK(mp_mutex); tty_unregister_device(drv->tty_driver, port->line); mp_unconfigure_port(drv, state); state->port = NULL; MP_MUTEX_UNLOCK(mp_mutex); return 0; } static void autoconfig(struct mp_port *mtpt, unsigned int probeflags) { unsigned char status1, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; unsigned long flags; unsigned char u_type; unsigned char b_ret = 0; if (!mtpt->port.iobase && !mtpt->port.mapbase && !mtpt->port.membase) return; DEBUG_AUTOCONF("ttyMP%d: autoconf (0x%04x, 0x%p): ", mtpt->port.line, mtpt->port.iobase, mtpt->port.membase); spin_lock_irqsave(&mtpt->port.lock, flags); if (!(mtpt->port.flags & UPF_BUGGY_UART)) { scratch = serial_inp(mtpt, UART_IER); serial_outp(mtpt, UART_IER, 0); #ifdef __i386__ outb(0xff, 0x080); #endif scratch2 = serial_inp(mtpt, UART_IER) & 0x0f; serial_outp(mtpt, UART_IER, 0x0F); #ifdef __i386__ outb(0, 0x080); #endif scratch3 = serial_inp(mtpt, UART_IER) & 0x0F; serial_outp(mtpt, UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0F) { DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", scratch2, scratch3); goto out; } } save_mcr = serial_in(mtpt, UART_MCR); save_lcr = serial_in(mtpt, UART_LCR); if (!(mtpt->port.flags & UPF_SKIP_TEST)) { serial_outp(mtpt, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(mtpt, UART_MSR) & 0xF0; serial_outp(mtpt, UART_MCR, save_mcr); if (status1 != 0x90) { DEBUG_AUTOCONF("LOOP test failed (%02x) ", status1); goto out; } } serial_outp(mtpt, UART_LCR, 0xBF); serial_outp(mtpt, UART_EFR, 0); serial_outp(mtpt, UART_LCR, 0); serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(mtpt, UART_IIR) >> 6; DEBUG_AUTOCONF("iir=%d ", scratch); if(mtpt->device->nr_ports >= 8) b_ret = read_option_register(mtpt,(MP_OPTR_DIR0 + ((mtpt->port.line)/8))); else b_ret = read_option_register(mtpt,MP_OPTR_DIR0); u_type = (b_ret & 0xf0) >> 4; if(mtpt->port.type == PORT_UNKNOWN ) { switch (u_type) { case DIR_UART_16C550: mtpt->port.type = PORT_16C55X; break; case DIR_UART_16C1050: mtpt->port.type = PORT_16C105X; break; case DIR_UART_16C1050A: if (mtpt->port.line < 2) { mtpt->port.type = PORT_16C105XA; } else { if (mtpt->device->device_id & 0x50) { mtpt->port.type = PORT_16C55X; } else { mtpt->port.type = PORT_16C105X; } } break; default: mtpt->port.type = PORT_UNKNOWN; break; } } if(mtpt->port.type == PORT_UNKNOWN ) { printk("unknow2\n"); switch (scratch) { case 0: case 1: mtpt->port.type = PORT_UNKNOWN; break; case 2: case 3: mtpt->port.type = PORT_16C55X; break; } } serial_outp(mtpt, UART_LCR, save_lcr); mtpt->port.fifosize = uart_config[mtpt->port.type].dfl_xmit_fifo_size; mtpt->capabilities = uart_config[mtpt->port.type].flags; if (mtpt->port.type == PORT_UNKNOWN) goto out; serial_outp(mtpt, UART_MCR, save_mcr); serial_outp(mtpt, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_outp(mtpt, UART_FCR, 0); (void)serial_in(mtpt, UART_RX); serial_outp(mtpt, UART_IER, 0); out: spin_unlock_irqrestore(&mtpt->port.lock, flags); DEBUG_AUTOCONF("type=%s\n", uart_config[mtpt->port.type].name); } static void autoconfig_irq(struct mp_port *mtpt) { unsigned char save_mcr, save_ier; unsigned long irqs; int irq; /* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on()); save_mcr = serial_inp(mtpt, UART_MCR); save_ier = serial_inp(mtpt, UART_IER); serial_outp(mtpt, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); irqs = probe_irq_on(); serial_outp(mtpt, UART_MCR, 0); serial_outp(mtpt, UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); serial_outp(mtpt, UART_IER, 0x0f); /* enable all intrs */ (void)serial_inp(mtpt, UART_LSR); (void)serial_inp(mtpt, UART_RX); (void)serial_inp(mtpt, UART_IIR); (void)serial_inp(mtpt, UART_MSR); serial_outp(mtpt, UART_TX, 0xFF); irq = probe_irq_off(irqs); serial_outp(mtpt, UART_MCR, save_mcr); serial_outp(mtpt, UART_IER, save_ier); mtpt->port.irq = (irq > 0) ? irq : 0; } static void multi_stop_tx(struct sb_uart_port *port) { struct mp_port *mtpt = (struct mp_port *)port; if (mtpt->ier & UART_IER_THRI) { mtpt->ier &= ~UART_IER_THRI; serial_out(mtpt, UART_IER, mtpt->ier); } tasklet_schedule(&port->info->tlet); } static void multi_start_tx(struct sb_uart_port *port) { struct mp_port *mtpt = (struct mp_port *)port; if (!(mtpt->ier & UART_IER_THRI)) { mtpt->ier |= UART_IER_THRI; serial_out(mtpt, UART_IER, mtpt->ier); } } static void multi_stop_rx(struct sb_uart_port *port) { struct mp_port *mtpt = (struct mp_port *)port; mtpt->ier &= ~UART_IER_RLSI; mtpt->port.read_status_mask &= ~UART_LSR_DR; serial_out(mtpt, UART_IER, mtpt->ier); } static void multi_enable_ms(struct sb_uart_port *port) { struct mp_port *mtpt = (struct mp_port *)port; mtpt->ier |= UART_IER_MSI; serial_out(mtpt, UART_IER, mtpt->ier); } static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ) { struct tty_struct *tty = mtpt->port.info->tty; unsigned char lsr = *status; int max_count = 256; unsigned char ch; char flag; //lsr &= mtpt->port.read_status_mask; do { if ((lsr & UART_LSR_PE) && (mtpt->port.mdmode & MDMODE_ENABLE)) { ch = serial_inp(mtpt, UART_RX); } else if (lsr & UART_LSR_SPECIAL) { flag = 0; ch = serial_inp(mtpt, UART_RX); if (lsr & UART_LSR_BI) { mtpt->port.icount.brk++; flag = TTY_BREAK; if (sb_uart_handle_break(&mtpt->port)) goto ignore_char; } if (lsr & UART_LSR_PE) { mtpt->port.icount.parity++; flag = TTY_PARITY; } if (lsr & UART_LSR_FE) { mtpt->port.icount.frame++; flag = TTY_FRAME; } if (lsr & UART_LSR_OE) { mtpt->port.icount.overrun++; flag = TTY_OVERRUN; } tty_insert_flip_char(tty, ch, flag); } else { ch = serial_inp(mtpt, UART_RX); tty_insert_flip_char(tty, ch, 0); } ignore_char: lsr = serial_inp(mtpt, UART_LSR); } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); tty_flip_buffer_push(tty); } static _INLINE_ void transmit_chars(struct mp_port *mtpt) { struct circ_buf *xmit = &mtpt->port.info->xmit; int count; if (mtpt->port.x_char) { serial_outp(mtpt, UART_TX, mtpt->port.x_char); mtpt->port.icount.tx++; mtpt->port.x_char = 0; return; } if (uart_circ_empty(xmit) || uart_tx_stopped(&mtpt->port)) { multi_stop_tx(&mtpt->port); return; } count = uart_circ_chars_pending(xmit); if(count > mtpt->port.fifosize) { count = mtpt->port.fifosize; } printk("[%d] mdmode: %x\n", mtpt->port.line, mtpt->port.mdmode); do { #if 0 /* check multi-drop mode */ if ((mtpt->port.mdmode & (MDMODE_ENABLE | MDMODE_ADDR)) == (MDMODE_ENABLE | MDMODE_ADDR)) { printk("send address\n"); /* send multi-drop address */ serial_out(mtpt, UART_SCR, xmit->buf[xmit->tail]); } else #endif { serial_out(mtpt, UART_TX, xmit->buf[xmit->tail]); } xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); mtpt->port.icount.tx++; } while (--count > 0); } static _INLINE_ void check_modem_status(struct mp_port *mtpt) { int status; status = serial_in(mtpt, UART_MSR); if ((status & UART_MSR_ANY_DELTA) == 0) return; if (status & UART_MSR_TERI) mtpt->port.icount.rng++; if (status & UART_MSR_DDSR) mtpt->port.icount.dsr++; if (status & UART_MSR_DDCD) sb_uart_handle_dcd_change(&mtpt->port, status & UART_MSR_DCD); if (status & UART_MSR_DCTS) sb_uart_handle_cts_change(&mtpt->port, status & UART_MSR_CTS); wake_up_interruptible(&mtpt->port.info->delta_msr_wait); } static inline void multi_handle_port(struct mp_port *mtpt) { unsigned int status = serial_inp(mtpt, UART_LSR); //printk("lsr: %x\n", status); if ((status & UART_LSR_DR) || (status & UART_LSR_SPECIAL)) receive_chars(mtpt, &status); check_modem_status(mtpt); if (status & UART_LSR_THRE) { if ((mtpt->port.type == PORT_16C105X) || (mtpt->port.type == PORT_16C105XA)) transmit_chars(mtpt); else { if (mtpt->interface >= RS485NE) uart_set_mctrl(&mtpt->port, TIOCM_RTS); transmit_chars(mtpt); if (mtpt->interface >= RS485NE) { while((status=serial_in(mtpt,UART_LSR) &0x60)!=0x60); uart_clear_mctrl(&mtpt->port, TIOCM_RTS); } } } } static irqreturn_t multi_interrupt(int irq, void *dev_id) { struct irq_info *iinfo = dev_id; struct list_head *lhead, *end = NULL; int pass_counter = 0; spin_lock(&iinfo->lock); lhead = iinfo->head; do { struct mp_port *mtpt; unsigned int iir; mtpt = list_entry(lhead, struct mp_port, list); iir = serial_in(mtpt, UART_IIR); printk("interrupt! port %d, iir 0x%x\n", mtpt->port.line, iir); //wlee if (!(iir & UART_IIR_NO_INT)) { printk("interrupt handle\n"); spin_lock(&mtpt->port.lock); multi_handle_port(mtpt); spin_unlock(&mtpt->port.lock); end = NULL; } else if (end == NULL) end = lhead; lhead = lhead->next; if (lhead == iinfo->head && pass_counter++ > PASS_LIMIT) { printk(KERN_ERR "multi: too much work for " "irq%d\n", irq); printk( "multi: too much work for " "irq%d\n", irq); break; } } while (lhead != end); spin_unlock(&iinfo->lock); return IRQ_HANDLED; } static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt) { spin_lock_irq(&i->lock); if (!list_empty(i->head)) { if (i->head == &mtpt->list) i->head = i->head->next; list_del(&mtpt->list); } else { i->head = NULL; } spin_unlock_irq(&i->lock); } static int serial_link_irq_chain(struct mp_port *mtpt) { struct irq_info *i = irq_lists + mtpt->port.irq; int ret, irq_flags = mtpt->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; spin_lock_irq(&i->lock); if (i->head) { list_add(&mtpt->list, i->head); spin_unlock_irq(&i->lock); ret = 0; } else { INIT_LIST_HEAD(&mtpt->list); i->head = &mtpt->list; spin_unlock_irq(&i->lock); ret = request_irq(mtpt->port.irq, multi_interrupt, irq_flags, "serial", i); if (ret < 0) serial_do_unlink(i, mtpt); } return ret; } static void serial_unlink_irq_chain(struct mp_port *mtpt) { struct irq_info *i = irq_lists + mtpt->port.irq; if (list_empty(i->head)) { free_irq(mtpt->port.irq, i); } serial_do_unlink(i, mtpt); } static void multi_timeout(unsigned long data) { struct mp_port *mtpt = (struct mp_port *)data; spin_lock(&mtpt->port.lock); multi_handle_port(mtpt); spin_unlock(&mtpt->port.lock); mod_timer(&mtpt->timer, jiffies+1 ); } static unsigned int multi_tx_empty(struct sb_uart_port *port) { struct mp_port *mtpt = (struct mp_port *)port; unsigned long flags; unsigned int ret; spin_lock_irqsave(&mtpt->port.lock, flags); ret = serial_in(mtpt, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; spin_unlock_irqrestore(&mtpt->port.lock, flags); return ret; } static unsigned int multi_get_mctrl(struct sb_uart_port *port) { struct mp_port *mtpt = (struct mp_port *)port; unsigned char status; unsigned int ret; status = serial_in(mtpt, UART_MSR); ret = 0; if (status & UART_MSR_DCD) ret |= TIOCM_CAR; if (status & UART_MSR_RI) ret |= TIOCM_RNG; if (status & UART_MSR_DSR) ret |= TIOCM_DSR; if (status & UART_MSR_CTS) ret |= TIOCM_CTS; return ret; } static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl) { struct mp_port *mtpt = (struct mp_port *)port; unsigned char mcr = 0; mctrl &= 0xff; if (mctrl & TIOCM_RTS) mcr |= UART_MCR_RTS; if (mctrl & TIOCM_DTR) mcr |= UART_MCR_DTR; if (mctrl & TIOCM_OUT1) mcr |= UART_MCR_OUT1; if (mctrl & TIOCM_OUT2) mcr |= UART_MCR_OUT2; if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; serial_out(mtpt, UART_MCR, mcr); } static void multi_break_ctl(struct sb_uart_port *port, int break_state) { struct mp_port *mtpt = (struct mp_port *)port; unsigned long flags; spin_lock_irqsave(&mtpt->port.lock, flags); if (break_state == -1) mtpt->lcr |= UART_LCR_SBC; else mtpt->lcr &= ~UART_LCR_SBC; serial_out(mtpt, UART_LCR, mtpt->lcr); spin_unlock_irqrestore(&mtpt->port.lock, flags); } static int multi_startup(struct sb_uart_port *port) { struct mp_port *mtpt = (struct mp_port *)port; unsigned long flags; int retval; mtpt->capabilities = uart_config[mtpt->port.type].flags; mtpt->mcr = 0; if (mtpt->capabilities & UART_CLEAR_FIFO) { serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(mtpt, UART_FCR, 0); } (void) serial_inp(mtpt, UART_LSR); (void) serial_inp(mtpt, UART_RX); (void) serial_inp(mtpt, UART_IIR); (void) serial_inp(mtpt, UART_MSR); //test-wlee 9-bit disable serial_outp(mtpt, UART_MSR, 0); if (!(mtpt->port.flags & UPF_BUGGY_UART) && (serial_inp(mtpt, UART_LSR) == 0xff)) { printk("ttyS%d: LSR safety check engaged!\n", mtpt->port.line); //return -ENODEV; } if ((!is_real_interrupt(mtpt->port.irq)) || (mtpt->poll_type==TYPE_POLL)) { unsigned int timeout = mtpt->port.timeout; timeout = timeout > 6 ? (timeout / 2 - 2) : 1; mtpt->timer.data = (unsigned long)mtpt; mod_timer(&mtpt->timer, jiffies + timeout); } else { retval = serial_link_irq_chain(mtpt); if (retval) return retval; } serial_outp(mtpt, UART_LCR, UART_LCR_WLEN8); spin_lock_irqsave(&mtpt->port.lock, flags); if ((is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_INTERRUPT)) mtpt->port.mctrl |= TIOCM_OUT2; multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); spin_unlock_irqrestore(&mtpt->port.lock, flags); mtpt->ier = UART_IER_RLSI | UART_IER_RDI; serial_outp(mtpt, UART_IER, mtpt->ier); (void) serial_inp(mtpt, UART_LSR); (void) serial_inp(mtpt, UART_RX); (void) serial_inp(mtpt, UART_IIR); (void) serial_inp(mtpt, UART_MSR); return 0; } static void multi_shutdown(struct sb_uart_port *port) { struct mp_port *mtpt = (struct mp_port *)port; unsigned long flags; mtpt->ier = 0; serial_outp(mtpt, UART_IER, 0); spin_lock_irqsave(&mtpt->port.lock, flags); mtpt->port.mctrl &= ~TIOCM_OUT2; multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); spin_unlock_irqrestore(&mtpt->port.lock, flags); serial_out(mtpt, UART_LCR, serial_inp(mtpt, UART_LCR) & ~UART_LCR_SBC); serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(mtpt, UART_FCR, 0); (void) serial_in(mtpt, UART_RX); if ((!is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_POLL)) { del_timer_sync(&mtpt->timer); } else { serial_unlink_irq_chain(mtpt); } } static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud) { unsigned int quot; if ((port->flags & UPF_MAGIC_MULTIPLIER) && baud == (port->uartclk/4)) quot = 0x8001; else if ((port->flags & UPF_MAGIC_MULTIPLIER) && baud == (port->uartclk/8)) quot = 0x8002; else quot = sb_uart_get_divisor(port, baud); return quot; } static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old) { struct mp_port *mtpt = (struct mp_port *)port; unsigned char cval, fcr = 0; unsigned long flags; unsigned int baud, quot; switch (termios->c_cflag & CSIZE) { case CS5: cval = 0x00; break; case CS6: cval = 0x01; break; case CS7: cval = 0x02; break; default: case CS8: cval = 0x03; break; } if (termios->c_cflag & CSTOPB) cval |= 0x04; 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 baud = sb_uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); quot = multi_get_divisor(port, baud); if (mtpt->capabilities & UART_USE_FIFO) { //if (baud < 2400) // fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; //else // fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; // fcr = UART_FCR_ENABLE_FIFO | 0x90; fcr = fcr_arr[mtpt->port.line]; } spin_lock_irqsave(&mtpt->port.lock, flags); sb_uart_update_timeout(port, termios->c_cflag, baud); mtpt->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) mtpt->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (termios->c_iflag & (BRKINT | PARMRK)) mtpt->port.read_status_mask |= UART_LSR_BI; mtpt->port.ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) mtpt->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; if (termios->c_iflag & IGNBRK) { mtpt->port.ignore_status_mask |= UART_LSR_BI; if (termios->c_iflag & IGNPAR) mtpt->port.ignore_status_mask |= UART_LSR_OE; } if ((termios->c_cflag & CREAD) == 0) mtpt->port.ignore_status_mask |= UART_LSR_DR; mtpt->ier &= ~UART_IER_MSI; if (UART_ENABLE_MS(&mtpt->port, termios->c_cflag)) mtpt->ier |= UART_IER_MSI; serial_out(mtpt, UART_IER, mtpt->ier); if (mtpt->capabilities & UART_STARTECH) { serial_outp(mtpt, UART_LCR, 0xBF); serial_outp(mtpt, UART_EFR, termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0); } serial_outp(mtpt, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ serial_outp(mtpt, UART_DLL, quot & 0xff); /* LS of divisor */ serial_outp(mtpt, UART_DLM, quot >> 8); /* MS of divisor */ serial_outp(mtpt, UART_LCR, cval); /* reset DLAB */ mtpt->lcr = cval; /* Save LCR */ if (fcr & UART_FCR_ENABLE_FIFO) { /* emulated UARTs (Lucent Venus 167x) need two steps */ serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); } serial_outp(mtpt, UART_FCR, fcr); /* set fcr */ if ((mtpt->port.type == PORT_16C105X) || (mtpt->port.type == PORT_16C105XA)) { if(deep[mtpt->port.line]!=0) set_deep_fifo(port, ENABLE); if (mtpt->interface != RS232) set_auto_rts(port,mtpt->interface); } else { if (mtpt->interface >= RS485NE) { uart_clear_mctrl(&mtpt->port, TIOCM_RTS); } } if(mtpt->device->device_id == PCI_DEVICE_ID_MP4M) { SendATCommand(mtpt); printk("SendATCommand\n"); } multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); spin_unlock_irqrestore(&mtpt->port.lock, flags); } static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate) { struct mp_port *mtpt = (struct mp_port *)port; if (state) { if (mtpt->capabilities & UART_STARTECH) { serial_outp(mtpt, UART_LCR, 0xBF); serial_outp(mtpt, UART_EFR, UART_EFR_ECB); serial_outp(mtpt, UART_LCR, 0); serial_outp(mtpt, UART_IER, UART_IERX_SLEEP); serial_outp(mtpt, UART_LCR, 0xBF); serial_outp(mtpt, UART_EFR, 0); serial_outp(mtpt, UART_LCR, 0); } if (mtpt->pm) mtpt->pm(port, state, oldstate); } else { if (mtpt->capabilities & UART_STARTECH) { serial_outp(mtpt, UART_LCR, 0xBF); serial_outp(mtpt, UART_EFR, UART_EFR_ECB); serial_outp(mtpt, UART_LCR, 0); serial_outp(mtpt, UART_IER, 0); serial_outp(mtpt, UART_LCR, 0xBF); serial_outp(mtpt, UART_EFR, 0); serial_outp(mtpt, UART_LCR, 0); } if (mtpt->pm) mtpt->pm(port, state, oldstate); } } static void multi_release_std_resource(struct mp_port *mtpt) { unsigned int size = 8 << mtpt->port.regshift; switch (mtpt->port.iotype) { case UPIO_MEM: if (!mtpt->port.mapbase) break; if (mtpt->port.flags & UPF_IOREMAP) { iounmap(mtpt->port.membase); mtpt->port.membase = NULL; } release_mem_region(mtpt->port.mapbase, size); break; case UPIO_HUB6: case UPIO_PORT: release_region(mtpt->port.iobase,size); break; } } static void multi_release_port(struct sb_uart_port *port) { } static int multi_request_port(struct sb_uart_port *port) { return 0; } static void multi_config_port(struct sb_uart_port *port, int flags) { struct mp_port *mtpt = (struct mp_port *)port; int probeflags = PROBE_ANY; if (flags & UART_CONFIG_TYPE) autoconfig(mtpt, probeflags); if (mtpt->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(mtpt); if (mtpt->port.type == PORT_UNKNOWN) multi_release_std_resource(mtpt); } static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser) { if (ser->irq >= NR_IRQS || ser->irq < 0 || ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || ser->type == PORT_STARTECH) return -EINVAL; return 0; } static const char * multi_type(struct sb_uart_port *port) { int type = port->type; if (type >= ARRAY_SIZE(uart_config)) type = 0; return uart_config[type].name; } static struct sb_uart_ops multi_pops = { .tx_empty = multi_tx_empty, .set_mctrl = multi_set_mctrl, .get_mctrl = multi_get_mctrl, .stop_tx = multi_stop_tx, .start_tx = multi_start_tx, .stop_rx = multi_stop_rx, .enable_ms = multi_enable_ms, .break_ctl = multi_break_ctl, .startup = multi_startup, .shutdown = multi_shutdown, .set_termios = multi_set_termios, .pm = multi_pm, .type = multi_type, .release_port = multi_release_port, .request_port = multi_request_port, .config_port = multi_config_port, .verify_port = multi_verify_port, }; static struct uart_driver multi_reg = { .owner = THIS_MODULE, .driver_name = "goldel_tulip", .dev_name = "ttyMP", .major = SB_TTY_MP_MAJOR, .minor = 0, .nr = MAX_MP_PORT, .cons = NULL, }; static void __init multi_init_ports(void) { struct mp_port *mtpt; static int first = 1; int i,j,k; unsigned char osc; unsigned char b_ret = 0; static struct mp_device_t * sbdev; if (!first) return; first = 0; mtpt = multi_ports; for (k=0;knr_ports; i++, mtpt++) { mtpt->device = sbdev; mtpt->port.iobase = sbdev->uart_access_addr + 8*i; mtpt->port.irq = sbdev->irq; if ( ((sbdev->device_id == PCI_DEVICE_ID_MP4)&&(sbdev->revision==0x91))) mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i; else if (sbdev->revision == 0xc0) mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + (i & 0x1); else mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i/8; mtpt->option_base_addr = sbdev->option_reg_addr; mtpt->poll_type = sbdev->poll_type; mtpt->port.uartclk = BASE_BAUD * 16; /* get input clock information */ osc = inb(sbdev->option_reg_addr + MP_OPTR_DIR0 + i/8) & 0x0F; if (osc==0x0f) osc = 0; for(j=0;jport.uartclk *= 2; mtpt->port.flags |= STD_COM_FLAGS | UPF_SHARE_IRQ ; mtpt->port.iotype = UPIO_PORT; mtpt->port.ops = &multi_pops; if (sbdev->revision == 0xc0) { /* for SB16C1053APCI */ b_ret = sb1053a_get_interface(mtpt, i); } else { b_ret = read_option_register(mtpt,(MP_OPTR_IIR0 + i/8)); printk("IIR_RET = %x\n",b_ret); } /* default to RS232 */ mtpt->interface = RS232; if (IIR_RS422 == (b_ret & IIR_TYPE_MASK)) mtpt->interface = RS422PTP; if (IIR_RS485 == (b_ret & IIR_TYPE_MASK)) mtpt->interface = RS485NE; } } } static void __init multi_register_ports(struct uart_driver *drv) { int i; multi_init_ports(); for (i = 0; i < NR_PORTS; i++) { struct mp_port *mtpt = &multi_ports[i]; mtpt->port.line = i; mtpt->port.ops = &multi_pops; init_timer(&mtpt->timer); mtpt->timer.function = multi_timeout; mp_add_one_port(drv, &mtpt->port); } } /** * pci_remap_base - remap BAR value of pci device * * PARAMETERS * pcidev - pci_dev structure address * offset - BAR offset PCI_BASE_ADDRESS_0 ~ PCI_BASE_ADDRESS_4 * address - address to be changed BAR value * size - size of address space * * RETURNS * If this function performs successful, it returns 0. Otherwise, It returns -1. */ static int pci_remap_base(struct pci_dev *pcidev, unsigned int offset, unsigned int address, unsigned int size) { #if 0 struct resource *root; unsigned index = (offset - 0x10) >> 2; #endif pci_write_config_dword(pcidev, offset, address); #if 0 root = pcidev->resource[index].parent; release_resource(&pcidev->resource[index]); address &= ~0x1; pcidev->resource[index].start = address; pcidev->resource[index].end = address + size - 1; if (request_resource(root, &pcidev->resource[index]) != NULL) { printk(KERN_ERR "pci remap conflict!! 0x%x\n", address); return (-1); } #endif return (0); } static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd) { static struct mp_device_t * sbdev = mp_devs; unsigned long addr = 0; int j; struct resource * ret = NULL; sbdev->device_id = brd.device_id; pci_read_config_byte(pcidev, PCI_CLASS_REVISION, &(sbdev->revision)); sbdev->name = brd.name; sbdev->uart_access_addr = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; /* check revision. The SB16C1053APCI's option i/o address is BAR4 */ if (sbdev->revision == 0xc0) { /* SB16C1053APCI */ sbdev->option_reg_addr = pcidev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK; } else { sbdev->option_reg_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; } #if 1 if (sbdev->revision == 0xc0) { outb(0x00, sbdev->option_reg_addr + MP_OPTR_GPOCR); inb(sbdev->option_reg_addr + MP_OPTR_GPOCR); outb(0x83, sbdev->option_reg_addr + MP_OPTR_GPOCR); } #endif sbdev->irq = pcidev->irq; if ((brd.device_id & 0x0800) || !(brd.device_id &0xff00)) { sbdev->poll_type = TYPE_INTERRUPT; } else { sbdev->poll_type = TYPE_POLL; } /* codes which is specific to each board*/ switch(brd.device_id){ case PCI_DEVICE_ID_MP1 : case PCIE_DEVICE_ID_MP1 : case PCIE_DEVICE_ID_MP1E : case PCIE_DEVICE_ID_GT_MP1 : sbdev->nr_ports = 1; break; case PCI_DEVICE_ID_MP2 : case PCIE_DEVICE_ID_MP2 : case PCIE_DEVICE_ID_GT_MP2 : case PCIE_DEVICE_ID_MP2B : case PCIE_DEVICE_ID_MP2E : sbdev->nr_ports = 2; /* serial base address remap */ if (sbdev->revision == 0xc0) { int prev_port_addr = 0; pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); } break; case PCI_DEVICE_ID_MP4 : case PCI_DEVICE_ID_MP4A : case PCIE_DEVICE_ID_MP4 : case PCI_DEVICE_ID_GT_MP4 : case PCI_DEVICE_ID_GT_MP4A : case PCIE_DEVICE_ID_GT_MP4 : case PCI_DEVICE_ID_MP4M : case PCIE_DEVICE_ID_MP4B : sbdev->nr_ports = 4; if(sbdev->revision == 0x91){ sbdev->reserved_addr[0] = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; outb(0x03 , sbdev->reserved_addr[0] + 0x01); outb(0x03 , sbdev->reserved_addr[0] + 0x02); outb(0x01 , sbdev->reserved_addr[0] + 0x20); outb(0x00 , sbdev->reserved_addr[0] + 0x21); request_region(sbdev->reserved_addr[0], 32, sbdev->name); sbdev->uart_access_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; sbdev->option_reg_addr = pcidev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK; } /* SB16C1053APCI */ if (sbdev->revision == 0xc0) { int prev_port_addr = 0; pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 8); pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 24, 8); } break; case PCI_DEVICE_ID_MP6 : case PCI_DEVICE_ID_MP6A : case PCI_DEVICE_ID_GT_MP6 : case PCI_DEVICE_ID_GT_MP6A : sbdev->nr_ports = 6; /* SB16C1053APCI */ if (sbdev->revision == 0xc0) { int prev_port_addr = 0; pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 16); pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 32, 16); } break; case PCI_DEVICE_ID_MP8 : case PCIE_DEVICE_ID_MP8 : case PCI_DEVICE_ID_GT_MP8 : case PCIE_DEVICE_ID_GT_MP8 : case PCIE_DEVICE_ID_MP8B : sbdev->nr_ports = 8; break; case PCI_DEVICE_ID_MP32 : case PCIE_DEVICE_ID_MP32 : case PCI_DEVICE_ID_GT_MP32 : case PCIE_DEVICE_ID_GT_MP32 : { int portnum_hex=0; portnum_hex = inb(sbdev->option_reg_addr); sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16); } break; #ifdef CONFIG_PARPORT_PC case PCI_DEVICE_ID_MP2S1P : sbdev->nr_ports = 2; /* SB16C1053APCI */ if (sbdev->revision == 0xc0) { int prev_port_addr = 0; pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); } /* add PC compatible parallel port */ parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); break; case PCI_DEVICE_ID_MP1P : /* add PC compatible parallel port */ parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); break; #endif } ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name); if (sbdev->revision == 0xc0) { ret = request_region(sbdev->option_reg_addr, 0x40, sbdev->name); } else { ret = request_region(sbdev->option_reg_addr, 0x20, sbdev->name); } NR_BOARD++; NR_PORTS += sbdev->nr_ports; /* Enable PCI interrupt */ addr = sbdev->option_reg_addr + MP_OPTR_IMR0; for(j=0; j < (sbdev->nr_ports/8)+1; j++) { if (sbdev->poll_type == TYPE_INTERRUPT) { outb(0xff,addr +j); } } sbdev++; return 0; } static int __init multi_init(void) { int ret, i; struct pci_dev *dev = NULL; if(fcr_count==0) { for(i=0;i<256;i++) { fcr_arr[i] = 0x01; } } if(deep_count==0) { for(i=0;i<256;i++) { deep[i] = 1; } } if(rtr_count==0) { for(i=0;i<256;i++) { rtr[i] = 0x10; } } if(ttr_count==0) { for(i=0;i<256;i++) { ttr[i] = 0x38; } } printk("MULTI INIT\n"); for( i=0; i< mp_nrpcibrds; i++) { while( (dev = pci_get_device(mp_pciboards[i].vendor_id, mp_pciboards[i].device_id, dev) ) ) { printk("FOUND~~~\n"); // Cent OS bug fix // if (mp_pciboards[i].device_id & 0x0800) { int status; pci_disable_device(dev); status = pci_enable_device(dev); if (status != 0) { printk("Multiport Board Enable Fail !\n\n"); status = -ENXIO; return status; } } init_mp_dev(dev, mp_pciboards[i]); } } for (i = 0; i < NR_IRQS; i++) spin_lock_init(&irq_lists[i].lock); ret = mp_register_driver(&multi_reg); if (ret >= 0) multi_register_ports(&multi_reg); return ret; } static void __exit multi_exit(void) { int i; for (i = 0; i < NR_PORTS; i++) mp_remove_one_port(&multi_reg, &multi_ports[i].port); mp_unregister_driver(&multi_reg); } module_init(multi_init); module_exit(multi_exit); MODULE_DESCRIPTION("SystemBase Multiport PCI/PCIe CORE"); MODULE_LICENSE("GPL");