URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [serial_txx9.c] - Rev 1774
Go to most recent revision | Compare with Previous | Blame | View Log
/* * drivers/char/serial_txx9.c * * Copyright (C) 1999 Harald Koerfgen * Copyright (C) 2000 Jim Pick <jim@jimpick.com> * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com) * Copyright (C) 2000-2002 Toshiba Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Serial driver for TX3927/TX4927/TX4925/TX4938 internal SIO controller */ #include <linux/init.h> #include <linux/config.h> #include <linux/tty.h> #include <linux/major.h> #include <linux/ptrace.h> #include <linux/init.h> #include <linux/console.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/pm.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/delay.h> #include <linux/serial.h> #include <linux/generic_serial.h> #include <linux/pci.h> #ifdef CONFIG_MAGIC_SYSRQ #include <linux/sysrq.h> #endif #define DEBUG #ifdef DEBUG #define DBG(x...) printk(x) #else #define DBG(x...) #endif static char *serial_version = "0.30-mvl"; static char *serial_name = "TX39/49 Serial driver"; #define GS_INTERNAL_FLAGS (GS_TX_INTEN|GS_RX_INTEN|GS_ACTIVE) #define TXX9_SERIAL_MAGIC 0x39274927 #ifdef CONFIG_SERIAL /* "ttyS","cua" is used for standard serial driver */ #define TXX9_TTY_NAME "ttyTX" #define TXX9_TTY_DEVFS_NAME "tts/TX%d" #define TXX9_TTY_MINOR_START (64 + 64) /* ttyTX0(128), ttyTX1(129) */ #define TXX9_CU_NAME "cuatx" #define TXX9_CU_DEVFS_NAME "cua/TX%d" #else /* acts like standard serial driver */ #define TXX9_TTY_NAME "ttyS" #define TXX9_TTY_DEVFS_NAME "tts/%d" #define TXX9_TTY_MINOR_START 64 #define TXX9_CU_NAME "cua" #define TXX9_CU_DEVFS_NAME "cua/%d" #endif #define TXX9_TTY_MAJOR TTY_MAJOR #define TXX9_TTYAUX_MAJOR TTYAUX_MAJOR #define TXX9_SERIAL_HAVE_CTS_LINE 1 #ifdef CONFIG_PCI /* support for Toshiba TC86C001 SIO */ #define ENABLE_SERIAL_TXX9_PCI #endif /* * Number of serial ports */ #ifdef ENABLE_SERIAL_TXX9_PCI #define NR_PCI_BOARDS 4 #define NR_PORTS (2 + NR_PCI_BOARDS) #else #define NR_PORTS 2 #endif /* * Hardware specific serial port structure */ static struct rs_port { struct gs_port gs; /* Must be first field! */ unsigned long base; int irq; int baud_base; int flags; struct async_icount icount; int x_char; /* XON/XOFF character */ int read_status_mask; int ignore_status_mask; int quot; char io_type; #ifdef ENABLE_SERIAL_TXX9_PCI struct pci_dev * pci_dev; #endif } rs_ports[NR_PORTS] #ifdef CONFIG_TOSHIBA_RBTX4925 = { /* NR_PORTS = 0 */ {base: 0xff1f0000 + 0xf300, irq: 36, baud_base: 40000000 / 16 / 2, io_type: -1, /* virtual memory mapped */ flags: TXX9_SERIAL_HAVE_CTS_LINE,}, /* NR_PORTS = 1 */ {base: 0xff1f0000 + 0xf400, irq: 37, baud_base: 40000000 / 16 / 2, io_type: -1, /* virtual memory mapped */ flags: TXX9_SERIAL_HAVE_CTS_LINE,} }; #endif #ifdef CONFIG_TOSHIBA_RBTX4927 = { /* NR_PORTS = 0 */ {base: 0xff1f0000 + 0xf300, irq: 32, baud_base: 50000000 / 16 / 2, io_type: -1, /* virtual memory mapped */ flags: TXX9_SERIAL_HAVE_CTS_LINE,}, /* NR_PORTS = 1 */ {base: 0xff1f0000 + 0xf400, irq: 33, baud_base: 50000000 / 16 / 2, io_type: -1, /* virtual memory mapped */ flags: TXX9_SERIAL_HAVE_CTS_LINE,} }; #endif /* TXX9 Serial Registers */ #define TXX9_SILCR 0x00 #define TXX9_SIDICR 0x04 #define TXX9_SIDISR 0x08 #define TXX9_SICISR 0x0c #define TXX9_SIFCR 0x10 #define TXX9_SIFLCR 0x14 #define TXX9_SIBGR 0x18 #define TXX9_SITFIFO 0x1c #define TXX9_SIRFIFO 0x20 /* SILCR : Line Control */ #define TXX9_SILCR_SCS_MASK 0x00000060 #define TXX9_SILCR_SCS_IMCLK 0x00000000 #define TXX9_SILCR_SCS_IMCLK_BG 0x00000020 #define TXX9_SILCR_SCS_SCLK 0x00000040 #define TXX9_SILCR_SCS_SCLK_BG 0x00000060 #define TXX9_SILCR_UEPS 0x00000010 #define TXX9_SILCR_UPEN 0x00000008 #define TXX9_SILCR_USBL_MASK 0x00000004 //#define TXX9_SILCR_USBL_1BIT 0x00000004 //#define TXX9_SILCR_USBL_2BIT 0x00000000 #define TXX9_SILCR_USBL_1BIT 0x00000000 #define TXX9_SILCR_USBL_2BIT 0x00000004 #define TXX9_SILCR_UMODE_MASK 0x00000003 #define TXX9_SILCR_UMODE_8BIT 0x00000000 #define TXX9_SILCR_UMODE_7BIT 0x00000001 /* SIDICR : DMA/Int. Control */ #define TXX9_SIDICR_TDE 0x00008000 #define TXX9_SIDICR_RDE 0x00004000 #define TXX9_SIDICR_TIE 0x00002000 #define TXX9_SIDICR_RIE 0x00001000 #define TXX9_SIDICR_SPIE 0x00000800 #define TXX9_SIDICR_CTSAC 0x00000600 #define TXX9_SIDICR_STIE_MASK 0x0000003f #define TXX9_SIDICR_STIE_OERS 0x00000020 #define TXX9_SIDICR_STIE_CTSS 0x00000010 #define TXX9_SIDICR_STIE_RBRKD 0x00000008 #define TXX9_SIDICR_STIE_TRDY 0x00000004 #define TXX9_SIDICR_STIE_TXALS 0x00000002 #define TXX9_SIDICR_STIE_UBRKD 0x00000001 /* SIDISR : DMA/Int. Status */ #define TXX9_SIDISR_UBRK 0x00008000 #define TXX9_SIDISR_UVALID 0x00004000 #define TXX9_SIDISR_UFER 0x00002000 #define TXX9_SIDISR_UPER 0x00001000 #define TXX9_SIDISR_UOER 0x00000800 #define TXX9_SIDISR_ERI 0x00000400 #define TXX9_SIDISR_TOUT 0x00000200 #define TXX9_SIDISR_TDIS 0x00000100 #define TXX9_SIDISR_RDIS 0x00000080 #define TXX9_SIDISR_STIS 0x00000040 #define TXX9_SIDISR_RFDN_MASK 0x0000001f /* SICISR : Change Int. Status */ #define TXX9_SICISR_OERS 0x00000020 #define TXX9_SICISR_CTSS 0x00000010 #define TXX9_SICISR_RBRKD 0x00000008 #define TXX9_SICISR_TRDY 0x00000004 #define TXX9_SICISR_TXALS 0x00000002 #define TXX9_SICISR_UBRKD 0x00000001 /* SIFCR : FIFO Control */ #define TXX9_SIFCR_SWRST 0x00008000 #define TXX9_SIFCR_RDIL_MASK 0x00000180 #define TXX9_SIFCR_RDIL_1 0x00000000 #define TXX9_SIFCR_RDIL_4 0x00000080 #define TXX9_SIFCR_RDIL_8 0x00000100 #define TXX9_SIFCR_RDIL_12 0x00000180 #define TXX9_SIFCR_RDIL_MAX 0x00000180 #define TXX9_SIFCR_TDIL_MASK 0x00000018 #define TXX9_SIFCR_TDIL_MASK 0x00000018 #define TXX9_SIFCR_TDIL_1 0x00000000 #define TXX9_SIFCR_TDIL_4 0x00000001 #define TXX9_SIFCR_TDIL_8 0x00000010 #define TXX9_SIFCR_TDIL_MAX 0x00000010 #define TXX9_SIFCR_TFRST 0x00000004 #define TXX9_SIFCR_RFRST 0x00000002 #define TXX9_SIFCR_FRSTE 0x00000001 #define TXX9_SIO_TX_FIFO 8 #define TXX9_SIO_RX_FIFO 16 /* SIFLCR : Flow Control */ #define TXX9_SIFLCR_RCS 0x00001000 #define TXX9_SIFLCR_TES 0x00000800 #define TXX9_SIFLCR_RTSSC 0x00000200 #define TXX9_SIFLCR_RSDE 0x00000100 #define TXX9_SIFLCR_TSDE 0x00000080 #define TXX9_SIFLCR_RTSTL_MASK 0x0000001e #define TXX9_SIFLCR_RTSTL_MAX 0x0000001e #define TXX9_SIFLCR_TBRK 0x00000001 /* SIBGR : Baudrate Control */ #define TXX9_SIBGR_BCLK_MASK 0x00000300 #define TXX9_SIBGR_BCLK_T0 0x00000000 #define TXX9_SIBGR_BCLK_T2 0x00000100 #define TXX9_SIBGR_BCLK_T4 0x00000200 #define TXX9_SIBGR_BCLK_T6 0x00000300 #define TXX9_SIBGR_BRD_MASK 0x000000ff static /*inline*/ unsigned int sio_in(struct rs_port *port, int offset) { if (port->io_type < 0) /* don't use __raw_readl that does swapping for big endian */ return (*(volatile unsigned long*)(port->base + offset)); else return inl(port->base + offset); } static /*inline*/ void sio_out(struct rs_port *port, int offset, unsigned int value) { if (port->io_type < 0) /* don't use __raw_writel that does swapping for big endian */ (*(volatile unsigned long*)(port->base + offset))=(value); else outl(value, port->base + offset); } static inline void sio_mask(struct rs_port *port, int offset, unsigned int value) { sio_out(port, offset, sio_in(port, offset) & ~value); } static inline void sio_set(struct rs_port *port, int offset, unsigned int value) { sio_out(port, offset, sio_in(port, offset) | value); } /* * Forward declarations for serial routines */ static void rs_disable_tx_interrupts (void * ptr); static void rs_enable_tx_interrupts (void * ptr); static void rs_disable_rx_interrupts (void * ptr); static void rs_enable_rx_interrupts (void * ptr); static int rs_get_CD (void * ptr); static void rs_shutdown_port (void * ptr); static int rs_set_real_termios (void *ptr); static int rs_chars_in_buffer (void * ptr); static void rs_hungup (void *ptr); static void rs_getserial (void*, struct serial_struct *sp); static void rs_close (void *ptr); /* * Used by generic serial driver to access hardware */ static struct real_driver rs_real_driver = { disable_tx_interrupts: rs_disable_tx_interrupts, enable_tx_interrupts: rs_enable_tx_interrupts, disable_rx_interrupts: rs_disable_rx_interrupts, enable_rx_interrupts: rs_enable_rx_interrupts, get_CD: rs_get_CD, shutdown_port: rs_shutdown_port, set_real_termios: rs_set_real_termios, chars_in_buffer: rs_chars_in_buffer, close: rs_close, hungup: rs_hungup, getserial: rs_getserial, }; /* * Structures and such for TTY sessions and usage counts */ static struct tty_driver rs_driver, rs_callout_driver; static struct tty_struct *rs_table[NR_PORTS]; static struct termios *rs_termios[NR_PORTS]; static struct termios *rs_termios_locked[NR_PORTS]; int rs_refcount; int rs_initialized = 0; #ifdef CONFIG_SERIAL_TXX9_CONSOLE static struct console sercons; #endif #if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) static unsigned long break_pressed; /* break, really ... */ #endif static inline void receive_chars(struct rs_port *port, int *status, struct pt_regs *regs) { struct tty_struct *tty = port->gs.tty; unsigned char ch; struct async_icount *icount; int max_count = 256; icount = &port->icount; do { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { tty->flip.tqueue.routine((void *) tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) return; // if TTY_DONT_FLIP is set } ch = sio_in(port, TXX9_SIRFIFO); *tty->flip.char_buf_ptr = ch; icount->rx++; *tty->flip.flag_buf_ptr = 0; if (*status & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER | TXX9_SIDISR_UFER | TXX9_SIDISR_UOER)) { /* * For statistics only */ if (*status & TXX9_SIDISR_UBRK) { *status &= ~(TXX9_SIDISR_UFER | TXX9_SIDISR_UPER); 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 defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) if (port == &rs_ports[sercons.index]) { if (!break_pressed) { break_pressed = jiffies; goto ignore_char; } break_pressed = 0; } #endif if (port->gs.flags & ASYNC_SAK) do_SAK(tty); } else if (*status & TXX9_SIDISR_UPER) icount->parity++; else if (*status & TXX9_SIDISR_UFER) icount->frame++; if (*status & TXX9_SIDISR_UOER) icount->overrun++; /* * Mask off conditions which should be ignored. */ *status &= port->read_status_mask; #ifdef CONFIG_SERIAL_TXX9_CONSOLE /* Break flag is updated by reading RFIFO. */ #endif if (*status & (TXX9_SIDISR_UBRK)) { *tty->flip.flag_buf_ptr = TTY_BREAK; } else if (*status & TXX9_SIDISR_UPER) *tty->flip.flag_buf_ptr = TTY_PARITY; else if (*status & TXX9_SIDISR_UFER) *tty->flip.flag_buf_ptr = TTY_FRAME; } #if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) if (break_pressed && port == &rs_ports[sercons.index]) { if (ch != 0 && time_before(jiffies, break_pressed + HZ*5)) { handle_sysrq(ch, regs, NULL, NULL); break_pressed = 0; goto ignore_char; } break_pressed = 0; } #endif if ((*status & port->ignore_status_mask) == 0) { tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; } if ((*status & TXX9_SIDISR_UOER) && (tty->flip.count < TTY_FLIPBUF_SIZE)) { /* * Overrun is special, since it's reported * immediately, and doesn't affect the current * character */ *tty->flip.flag_buf_ptr = TTY_OVERRUN; tty->flip.count++; tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; } #if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ignore_char: #endif *status = sio_in(port, TXX9_SIDISR); } while ((!(*status & TXX9_SIDISR_UVALID)) && (max_count-- > 0)); tty_flip_buffer_push(tty); } static inline void transmit_chars(struct rs_port *port) { int count; if (port->x_char) { sio_out(port, TXX9_SITFIFO, port->x_char); port->icount.tx++; port->x_char = 0; return; } if (port->gs.xmit_cnt <= 0 || port->gs.tty->stopped || port->gs.tty->hw_stopped) { rs_disable_tx_interrupts(port); return; } count = TXX9_SIO_TX_FIFO; do { sio_out(port, TXX9_SITFIFO, port->gs.xmit_buf[port->gs.xmit_tail++]); port->gs.xmit_tail &= SERIAL_XMIT_SIZE-1; port->icount.tx++; if (--port->gs.xmit_cnt <= 0) break; } while (--count > 0); if (port->gs.xmit_cnt <= 0 || port->gs.tty->stopped || port->gs.tty->hw_stopped) { rs_disable_tx_interrupts(port); } if (port->gs.xmit_cnt <= port->gs.wakeup_chars) { if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && port->gs.tty->ldisc.write_wakeup) (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); wake_up_interruptible(&port->gs.tty->write_wait); } } static inline void check_modem_status(struct rs_port *port) { /* We don't have a carrier detect line - but just respond like we had one anyways so that open() becomes unblocked */ wake_up_interruptible(&port->gs.open_wait); } #define RS_ISR_PASS_LIMIT 256 static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) { struct rs_port * port = (struct rs_port *)dev_id; unsigned long flags; int status; int pass_counter = 0; local_irq_save(flags); if (!port || !port->gs.tty) { local_irq_restore(flags); return; } do { status = sio_in(port, TXX9_SIDISR); if (!(sio_in(port, TXX9_SIDICR) & TXX9_SIDICR_TIE)) status &= ~TXX9_SIDISR_TDIS; if (!(status & (TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | TXX9_SIDISR_TOUT))) break; if (status & TXX9_SIDISR_RDIS) receive_chars(port, &status, regs); #if 0 /* RTS/CTS are controled by HW. (if possible) */ check_modem_status(port); #endif if (status & TXX9_SIDISR_TDIS) transmit_chars(port); /* Clear TX/RX Int. Status */ sio_mask(port, TXX9_SIDISR, TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | TXX9_SIDISR_TOUT); if (pass_counter++ > RS_ISR_PASS_LIMIT) { break; } } while (1); local_irq_restore(flags); } /* *********************************************************************** * Here are the routines that actually * * interface with the generic_serial driver * *********************************************************************** */ static void rs_disable_tx_interrupts (void * ptr) { struct rs_port *port = ptr; unsigned long flags; local_irq_save(flags); port->gs.flags &= ~GS_TX_INTEN; sio_mask(port, TXX9_SIDICR, TXX9_SIDICR_TIE); local_irq_restore(flags); } static void rs_enable_tx_interrupts (void * ptr) { struct rs_port *port = ptr; unsigned long flags; local_irq_save(flags); sio_set(port, TXX9_SIDICR, TXX9_SIDICR_TIE); local_irq_restore(flags); } static void rs_disable_rx_interrupts (void * ptr) { struct rs_port *port = ptr; unsigned long flags; local_irq_save(flags); port->read_status_mask &= ~TXX9_SIDISR_RDIS; #if 0 sio_mask(port, TXX9_SIDICR, TXX9_SIDICR_RIE); #endif local_irq_restore(flags); } static void rs_enable_rx_interrupts (void * ptr) { struct rs_port *port = ptr; sio_set(port, TXX9_SIDICR, TXX9_SIDICR_RIE); } static int rs_get_CD (void * ptr) { /* No Carried Detect in Hardware - just return true */ return (1); } static void rs_shutdown_port (void * ptr) { struct rs_port *port = ptr; port->gs.flags &= ~GS_ACTIVE; free_irq(port->irq, port); sio_out(port, TXX9_SIDICR, 0); /* disable all intrs */ /* disable break condition */ sio_mask(port, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); #ifdef CONFIG_SERIAL_TXX9_CONSOLE if (port == &rs_ports[sercons.index]) { #endif if (!port->gs.tty || (port->gs.tty->termios->c_cflag & HUPCL)) { /* drop RTS */ sio_set(port, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC | TXX9_SIFLCR_RSDE); /* TXX9-SIO can not control DTR... */ } /* reset FIFO's */ sio_set(port, TXX9_SIFCR, TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); /* clear reset */ sio_mask(port, TXX9_SIFCR, TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); /* Disable RX/TX */ sio_set(port, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); #ifdef CONFIG_SERIAL_TXX9_CONSOLE } #endif } static int rs_set_real_termios (void *ptr) { struct rs_port *port = ptr; int quot = 0, baud_base, baud; unsigned cflag, cval, fcr = 0; int bits; unsigned long flags; if (!port->gs.tty || !port->gs.tty->termios) return 0; cflag = port->gs.tty->termios->c_cflag; cval = sio_in(port, TXX9_SILCR); /* byte size and parity */ cval &= ~TXX9_SILCR_UMODE_MASK; switch (cflag & CSIZE) { case CS7: cval |= TXX9_SILCR_UMODE_7BIT; bits = 9; break; case CS5: /* not supported */ case CS6: /* not supported */ case CS8: default: cval |= TXX9_SILCR_UMODE_8BIT; bits = 10; break; } cval &= ~TXX9_SILCR_USBL_MASK; if (cflag & CSTOPB) { cval |= TXX9_SILCR_USBL_2BIT; bits++; } else { cval |= TXX9_SILCR_USBL_1BIT; } cval &= ~(TXX9_SILCR_UPEN | TXX9_SILCR_UEPS); if (cflag & PARENB) { cval |= TXX9_SILCR_UPEN; bits++; } if (!(cflag & PARODD)) cval |= TXX9_SILCR_UEPS; /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(port->gs.tty); if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ baud_base = port->baud_base; quot = (baud_base + baud / 2) / baud; /* As a last resort, if the quotient is zero, default to 9600 bps */ if (!quot) quot = (baud_base + 9600 / 2) / 9600; port->quot = quot; /* Set up FIFO's */ /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ fcr = TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1; /* CTS flow control flag */ if (cflag & CRTSCTS) { port->gs.flags |= ASYNC_CTS_FLOW; if (port->flags & TXX9_SERIAL_HAVE_CTS_LINE) sio_out(port, TXX9_SIFLCR, TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES | TXX9_SIFLCR_RTSTL_MAX /* 15 */); } else { port->gs.flags &= ~ASYNC_CTS_FLOW; sio_mask(port, TXX9_SIFLCR, TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES | TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); } /* * Set up parity check flag */ #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) port->read_status_mask = TXX9_SIDISR_UOER | TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS; if (I_INPCK(port->gs.tty)) port->read_status_mask |= TXX9_SIDISR_UFER | TXX9_SIDISR_UPER; if (I_BRKINT(port->gs.tty) || I_PARMRK(port->gs.tty)) port->read_status_mask |= TXX9_SIDISR_UBRK; /* * Characters to ignore */ port->ignore_status_mask = 0; if (I_IGNPAR(port->gs.tty)) port->ignore_status_mask |= TXX9_SIDISR_UPER | TXX9_SIDISR_UFER; if (I_IGNBRK(port->gs.tty)) { port->ignore_status_mask |= TXX9_SIDISR_UBRK; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ if (I_IGNPAR(port->gs.tty)) port->ignore_status_mask |= TXX9_SIDISR_UOER; } #if 0 /* XXX: This cause problem with some programs(init, mingetty, etc) */ /* * !!! ignore all characters if CREAD is not set */ if ((cflag & CREAD) == 0) port->ignore_status_mask |= TXX9_SIDISR_RDIS; #endif local_irq_save(flags); cval &= ~TXX9_SILCR_SCS_IMCLK; sio_out(port, TXX9_SILCR, cval | TXX9_SILCR_SCS_IMCLK_BG); sio_out(port, TXX9_SIBGR, quot | TXX9_SIBGR_BCLK_T0); sio_out(port, TXX9_SIFCR, fcr); local_irq_restore(flags); return 0; } static int rs_chars_in_buffer (void * ptr) { struct rs_port *port = ptr; /* return 0 if transmitter disabled. */ if (sio_in(port, TXX9_SIFLCR) & TXX9_SIFLCR_TSDE) return 0; return (sio_in(port, TXX9_SICISR) & TXX9_SICISR_TXALS) ? 0 : 1; } /* ********************************************************************** * * Here are the routines that actually * * interface with the rest of the system * * ********************************************************************** */ static int rs_open (struct tty_struct * tty, struct file * filp) { struct rs_port *port; int retval, line; if (!rs_initialized) { return -EIO; } line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (line >= NR_PORTS)) return -ENODEV; /* Pre-initialized already */ port = & rs_ports[line]; if (!port->base) return -ENODEV; tty->driver_data = port; port->gs.tty = tty; port->gs.count++; /* * Start up serial port */ retval = gs_init_port(&port->gs); if (retval) { port->gs.count--; return retval; } port->gs.flags |= GS_ACTIVE; if (port->gs.count == 1) { MOD_INC_USE_COUNT; /* * Clear the FIFO buffers and disable them * (they will be reenabled in rs_set_real_termios()) */ sio_set(port, TXX9_SIFCR, TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); /* clear reset */ sio_mask(port, TXX9_SIFCR, TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); sio_out(port, TXX9_SIDICR, 0); retval = request_irq(port->irq, rs_interrupt, SA_SHIRQ, "serial_txx9", port); if (retval) { printk(KERN_ERR "serial_txx9: request_irq: err %d\n", retval); MOD_DEC_USE_COUNT; port->gs.count--; return retval; } /* * Clear the interrupt registers. */ sio_out(port, TXX9_SIDISR, 0); /* HW RTS/CTS control */ if (port->flags & TXX9_SERIAL_HAVE_CTS_LINE) sio_out(port, TXX9_SIFLCR, TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES | TXX9_SIFLCR_RTSTL_MAX /* 15 */); } /* Enable RX/TX */ sio_mask(port, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); /* * Finally, enable interrupts */ rs_enable_rx_interrupts(port); /* * and set the speed of the serial port */ rs_set_real_termios(port); retval = gs_block_til_ready(&port->gs, filp); if (retval) { if (port->gs.count == 1) { free_irq(port->irq, port); MOD_DEC_USE_COUNT; } port->gs.count--; return retval; } /* tty->low_latency = 1; */ if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)) { if (tty->driver.subtype == SERIAL_TYPE_NORMAL) *tty->termios = port->gs.normal_termios; else *tty->termios = port->gs.callout_termios; rs_set_real_termios(port); } #ifdef CONFIG_SERIAL_TXX9_CONSOLE if (sercons.cflag && sercons.index == line) { tty->termios->c_cflag = sercons.cflag; sercons.cflag = 0; rs_set_real_termios(port); } #endif port->gs.session = current->session; port->gs.pgrp = current->pgrp; return 0; } /* * /proc fs routines.... */ static inline int line_info(char *buf, struct rs_port *port) { char stat_buf[30]; int ret; unsigned long flags; ret = sprintf(buf, "%d: uart:txx9 io%s:%lx irq:%d", port - &rs_ports[0], port->io_type < 0 ? "mem" : "port", port->base, port->irq); if (!port->base) { ret += sprintf(buf+ret, "\n"); return ret; } /* * Figure out the current RS-232 lines */ stat_buf[0] = 0; stat_buf[1] = 0; local_irq_save(flags); if (!(sio_in(port, TXX9_SIFLCR) & TXX9_SIFLCR_RTSSC)) strcat(stat_buf, "|RTS"); if (!(sio_in(port, TXX9_SICISR) & TXX9_SICISR_CTSS)) strcat(stat_buf, "|CTS"); local_irq_restore(flags); if (port->quot) { ret += sprintf(buf+ret, " baud:%d", port->baud_base / port->quot); } ret += sprintf(buf+ret, " tx:%d rx:%d", port->icount.tx, port->icount.rx); if (port->icount.frame) ret += sprintf(buf+ret, " fe:%d", port->icount.frame); if (port->icount.parity) ret += sprintf(buf+ret, " pe:%d", port->icount.parity); if (port->icount.brk) ret += sprintf(buf+ret, " brk:%d", port->icount.brk); if (port->icount.overrun) ret += sprintf(buf+ret, " oe:%d", port->icount.overrun); /* * Last thing is the RS-232 status lines */ ret += sprintf(buf+ret, " %s\n", stat_buf+1); return ret; } static int rs_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int i, len = 0, l; off_t begin = 0; len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); for (i = 0; i < NR_PORTS && len < 4000; i++) { l = line_info(page + len, &rs_ports[i]); len += l; if (len+begin > off+count) goto done; if (len+begin < off) { begin += len; len = 0; } } *eof = 1; done: if (off >= len+begin) return 0; *start = page + (begin-off); return ((count < begin+len-off) ? count : begin+len-off); } static void rs_close (void *ptr) { #if 0 struct rs_port *port = ptr; free_irq(port->irq, port); #endif MOD_DEC_USE_COUNT; } /* I haven't the foggiest why the decrement use count has to happen here. The whole linux serial drivers stuff needs to be redesigned. My guess is that this is a hack to minimize the impact of a bug elsewhere. Thinking about it some more. (try it sometime) Try running minicom on a serial port that is driven by a modularized driver. Have the modem hangup. Then remove the driver module. Then exit minicom. I expect an "oops". -- REW */ static void rs_hungup (void *ptr) { MOD_DEC_USE_COUNT; } static void rs_getserial (void *ptr, struct serial_struct *sp) { struct rs_port *port = ptr; struct tty_struct *tty = port->gs.tty; /* some applications (busybox, dbootstrap, etc.) look this */ sp->line = MINOR(tty->device) - tty->driver.minor_start; } /* * rs_break() --- routine which turns the break handling on or off */ static void rs_break(struct tty_struct *tty, int break_state) { struct rs_port *port = tty->driver_data; unsigned long flags; if (!port->base) return; local_irq_save(flags); if (break_state == -1) sio_set(port, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); else sio_mask(port, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); local_irq_restore(flags); } static int get_modem_info(struct rs_port *port, unsigned int *value) { unsigned int result; unsigned long flags; local_irq_save(flags); result = ((sio_in(port, TXX9_SIFLCR) & TXX9_SIFLCR_RTSSC) ? 0 : TIOCM_RTS) | ((sio_in(port, TXX9_SICISR) & TXX9_SICISR_CTSS) ? 0 : TIOCM_CTS); local_irq_restore(flags); return put_user(result,value); } static int set_modem_info(struct rs_port *port, unsigned int cmd, unsigned int *value) { int error = 0; unsigned int arg; unsigned long flags; if (copy_from_user(&arg, value, sizeof(int))) return -EFAULT; local_irq_save(flags); switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) sio_mask(port, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC | TXX9_SIFLCR_RSDE); break; case TIOCMBIC: if (arg & TIOCM_RTS) sio_set(port, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC | TXX9_SIFLCR_RSDE); break; case TIOCMSET: if (arg & TIOCM_RTS) sio_mask(port, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC | TXX9_SIFLCR_RSDE); else sio_set(port, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC | TXX9_SIFLCR_RSDE); break; default: error = -EINVAL; } local_irq_restore(flags); return error; } static int rs_ioctl (struct tty_struct * tty, struct file * filp, unsigned int cmd, unsigned long arg) { int rc; struct rs_port *port = tty->driver_data; int ival; rc = 0; switch (cmd) { case TIOCMGET: return get_modem_info(port, (unsigned int *) arg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return set_modem_info(port, cmd, (unsigned int *) arg); return 0; case TIOCGSOFTCAR: rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned int *) arg); break; case TIOCSSOFTCAR: if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(int))) == 0) { get_user(ival, (unsigned int *) arg); tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0); } break; case TIOCGSERIAL: if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0) rc = gs_getserial(&port->gs, (struct serial_struct *) arg); break; case TIOCSSERIAL: if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0) rc = gs_setserial(&port->gs, (struct serial_struct *) arg); break; default: rc = -ENOIOCTLCMD; break; } return rc; } /* * This function is used to send a high-priority XON/XOFF character to * the device */ static void rs_send_xchar(struct tty_struct * tty, char ch) { struct rs_port *port = (struct rs_port *)tty->driver_data; port->x_char = ch; if (ch) { /* Make sure transmit interrupts are on */ rs_enable_tx_interrupts(tty); } } /* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */ static void rs_throttle(struct tty_struct * tty) { struct rs_port *port = (struct rs_port *)tty->driver_data; unsigned long flags; if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) { local_irq_save(flags); /* drop RTS */ sio_set(port, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC | TXX9_SIFLCR_RSDE); local_irq_restore(flags); } } static void rs_unthrottle(struct tty_struct * tty) { struct rs_port *port = tty->driver_data; unsigned long flags; if (I_IXOFF(tty)) { if (port->x_char) port->x_char = 0; else rs_send_xchar(tty, START_CHAR(tty)); } if (tty->termios->c_cflag & CRTSCTS) { local_irq_save(flags); sio_mask(port, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC | TXX9_SIFLCR_RSDE); local_irq_restore(flags); } } /* ********************************************************************** * * Here are the initialization routines. * * ********************************************************************** */ static inline void show_serial_version(void) { printk(KERN_INFO "%s version %s\n", serial_name, serial_version); } static int rs_init_portstructs(void) { struct rs_port *port; int i; /* Adjust the values in the "driver" */ rs_driver.termios = rs_termios; rs_driver.termios_locked = rs_termios_locked; port = rs_ports; for (i=0; i < NR_PORTS;i++) { port->gs.callout_termios = tty_std_termios; port->gs.normal_termios = tty_std_termios; port->gs.magic = TXX9_SERIAL_MAGIC; port->gs.close_delay = HZ/2; port->gs.closing_wait = 30 * HZ; port->gs.rd = &rs_real_driver; #ifdef NEW_WRITE_LOCKING port->gs.port_write_sem = MUTEX; #endif #ifdef DECLARE_WAITQUEUE init_waitqueue_head(&port->gs.open_wait); init_waitqueue_head(&port->gs.close_wait); #endif port++; } return 0; } static int rs_init_drivers(void) { int error; memset(&rs_driver, 0, sizeof(rs_driver)); rs_driver.magic = TTY_DRIVER_MAGIC; rs_driver.driver_name = "serial_txx9"; #if defined(CONFIG_DEVFS_FS) rs_driver.name = TXX9_TTY_DEVFS_NAME; #else rs_driver.name = TXX9_TTY_NAME; #endif rs_driver.major = TXX9_TTY_MAJOR; rs_driver.minor_start = TXX9_TTY_MINOR_START; rs_driver.num = NR_PORTS; rs_driver.type = TTY_DRIVER_TYPE_SERIAL; rs_driver.subtype = SERIAL_TYPE_NORMAL; rs_driver.init_termios = tty_std_termios; rs_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; rs_driver.refcount = &rs_refcount; rs_driver.table = rs_table; rs_driver.termios = rs_termios; rs_driver.termios_locked = rs_termios_locked; rs_driver.open = rs_open; rs_driver.close = gs_close; rs_driver.write = gs_write; rs_driver.put_char = gs_put_char; rs_driver.flush_chars = gs_flush_chars; rs_driver.write_room = gs_write_room; rs_driver.chars_in_buffer = gs_chars_in_buffer; rs_driver.flush_buffer = gs_flush_buffer; rs_driver.ioctl = rs_ioctl; rs_driver.throttle = rs_throttle; rs_driver.unthrottle = rs_unthrottle; rs_driver.set_termios = gs_set_termios; rs_driver.stop = gs_stop; rs_driver.start = gs_start; rs_driver.hangup = gs_hangup; rs_driver.break_ctl = rs_break; rs_driver.read_proc = rs_read_proc; rs_callout_driver = rs_driver; #if defined(CONFIG_DEVFS_FS) rs_callout_driver.name = TXX9_CU_DEVFS_NAME; #else rs_callout_driver.name = TXX9_CU_NAME; #endif rs_callout_driver.major = TXX9_TTYAUX_MAJOR; rs_callout_driver.subtype = SERIAL_TYPE_CALLOUT; rs_callout_driver.read_proc = 0; rs_callout_driver.proc_entry = 0; if ((error = tty_register_driver(&rs_driver))) { printk(KERN_ERR "Couldn't register serial driver, error = %d\n", error); return 1; } if ((error = tty_register_driver(&rs_callout_driver))) { tty_unregister_driver(&rs_driver); printk(KERN_ERR "Couldn't register callout driver, error = %d\n", error); return 1; } return 0; } /* * This routine is called by txx9_rs_init() to initialize a specific serial * port. */ static void txx9_config(struct rs_port *port) { unsigned long flags; if (port - &rs_ports[0] != sercons.index) { local_irq_save(flags); /* * Reset the UART. */ sio_out(port, TXX9_SIFCR, TXX9_SIFCR_SWRST); #ifdef CONFIG_CPU_TX49XX /* TX4925 BUG WORKAROUND. Accessing SIOC register * immediately after soft reset causes bus error. */ wbflush();/* change iob(); */ udelay(1); #endif while (sio_in(port, TXX9_SIFCR) & TXX9_SIFCR_SWRST) ; /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ sio_set(port, TXX9_SIFCR, TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1); /* initial settings */ sio_out(port, TXX9_SILCR, TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | TXX9_SILCR_SCS_IMCLK_BG); sio_out(port, TXX9_SIBGR, ((port->baud_base + 9600 / 2) / 9600) | TXX9_SIBGR_BCLK_T0); local_irq_restore(flags); } DBG("txx9_config: port->io_type is %d\n", port->io_type); if (port->io_type < 0) request_mem_region(port->base, 36, "serial_txx9"); else request_region(port->base, 36, "serial_txx9"); } #ifdef ENABLE_SERIAL_TXX9_PCI static int __devinit serial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { int rc, i; struct rs_port *port; rc = pci_enable_device(dev); if (rc) return rc; /* find empty slot */ for (i = 0; i < NR_PORTS && rs_ports[i].base; i++) ; if (i == NR_PORTS) return -ENODEV; port = &rs_ports[i]; DBG("port number is %d\n",i); port->pci_dev = dev; port->base = pci_resource_start(dev, 1); DBG("port->base is %x\n",(u32)port->base); port->io_type = SERIAL_IO_PORT; port->irq = dev->irq; port->flags |= TXX9_SERIAL_HAVE_CTS_LINE; port->baud_base = 66670000 / 16 / 2; /* 66.67MHz */ DBG("port->baud_base %x\n",port->baud_base); txx9_config(port); printk(KERN_INFO "%s%d at 0x%08lx (irq = %d) is a TX39/49 SIO\n", TXX9_TTY_NAME, i, port->base, port->irq); return 0; } static void __devexit serial_txx9_remove_one(struct pci_dev *dev) { int i; for (i = 0; i < NR_PORTS; i++) { if (rs_ports[i].pci_dev == dev) { rs_ports[i].irq = 0; rs_ports[i].base = 0; rs_ports[i].pci_dev = 0; /* XXX NOT IMPLEMENTED YET */ break; } } } static struct pci_device_id serial_txx9_pci_tbl[] __devinitdata = { #ifdef PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, #endif { 0, } }; MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl); static struct pci_driver serial_txx9_pci_driver = { name: "serial_txx9", probe: serial_txx9_init_one, remove: __devexit_p(serial_txx9_remove_one), id_table: serial_txx9_pci_tbl, }; /* * Query PCI space for known serial boards * If found, add them to the PCI device space in rs_table[] */ static void __devinit probe_serial_txx9_pci(void) { /* Register call PCI serial devices. Null out * the driver name upon failure, as a signal * not to attempt to unregister the driver later */ if (pci_module_init (&serial_txx9_pci_driver) != 0) serial_txx9_pci_driver.name = ""; return; } #endif /* ENABLE_SERIAL_TXX9_PCI */ static int __init txx9_rs_init(void) { int rc; struct rs_port *port; int i; #ifndef ENABLE_SERIAL_TXX9_PCI for (i = 0, port = &rs_ports[0]; i < NR_PORTS; i++,port++) { if (port->base) goto config_ok; } return -ENODEV; config_ok: #endif show_serial_version(); rc = rs_init_portstructs (); rs_init_drivers (); for (i = 0, port = &rs_ports[0]; i < NR_PORTS; i++,port++) { if (!port->base) continue; if (port->io_type < 0) { if (check_mem_region(port->base, 36)) continue; } else { if (check_region(port->base, 36)) continue; } txx9_config(port); printk(KERN_INFO "%s%d at 0x%08lx (irq = %d) is a TX39/49 SIO\n", TXX9_TTY_NAME, i, port->base, port->irq); } /* Note: I didn't do anything to enable the second UART */ if (rc >= 0) rs_initialized++; #ifdef ENABLE_SERIAL_TXX9_PCI probe_serial_txx9_pci(); #endif return 0; } /* * This is for use by architectures that know their serial console * attributes only at run time. Not to be invoked after rs_init(). */ int __init early_serial_txx9_setup(int line, unsigned long base, int irq, int baud_base, int have_cts) { if (line >= NR_PORTS) return(-ENOENT); rs_ports[line].base = base; rs_ports[line].irq = irq; rs_ports[line].baud_base = baud_base; rs_ports[line].io_type = -1; /* virtual memory mapped */ if (have_cts) rs_ports[line].flags |= TXX9_SERIAL_HAVE_CTS_LINE; return(0); } static void __exit txx9_rs_fini(void) { unsigned long flags; int e1, e2; int i; local_irq_save(flags); if ((e1 = tty_unregister_driver(&rs_driver))) printk("serial: failed to unregister serial driver (%d)\n", e1); if ((e2 = tty_unregister_driver(&rs_callout_driver))) printk("serial: failed to unregister callout driver (%d)\n", e2); local_irq_restore(flags); for (i = 0; i < NR_PORTS; i++) { if (!rs_ports[i].base) continue; if (rs_ports[i].io_type < 0) release_mem_region(rs_ports[i].base, 36); else release_region(rs_ports[i].base, 36); } #ifdef ENABLE_SERIAL_PCI if (serial_txx9_pci_driver.name[0]) pci_unregister_driver (&serial_txx9_pci_driver); #endif } module_init(txx9_rs_init); module_exit(txx9_rs_fini); MODULE_DESCRIPTION("TX39/49 serial driver"); MODULE_AUTHOR("TOSHIBA Corporation"); MODULE_LICENSE("GPL"); /* * Begin serial console routines */ #ifdef CONFIG_SERIAL_TXX9_CONSOLE /* * Wait for transmitter & holding register to empty */ static inline void wait_for_xmitr(struct rs_port *port) { unsigned int tmout = 1000000; do { if (--tmout == 0) break; } while (!(sio_in(port, TXX9_SICISR) & TXX9_SICISR_TXALS)); /* Wait for flow control if necessary */ #if (ASYNC_INTERNAL_FLAGS & GS_INTERNAL_FLAGS) == 0 /* check conflict... */ if (port->gs.flags & ASYNC_CONS_FLOW) { tmout = 1000000; while (--tmout && (sio_in(port, TXX9_SICISR) & TXX9_SICISR_CTSS)); } #endif } /* * Print a string to the serial port trying not to disturb * any possible real use of the port... * * The console_lock must be held when we get here. */ static void serial_console_write(struct console *co, const char *s, unsigned count) { struct rs_port *port = &rs_ports[co->index]; int ier; unsigned i; /* * First save the IER then disable the interrupts */ ier = sio_in(port, TXX9_SIDICR); sio_out(port, TXX9_SIDICR, 0); /* * Now, do each character */ for (i = 0; i < count; i++, s++) { wait_for_xmitr(port); /* * Send the character out. * If a LF, also do CR... */ sio_out(port, TXX9_SITFIFO, *s); if (*s == 10) { wait_for_xmitr(port); sio_out(port, TXX9_SITFIFO, 13); } } /* * Finally, Wait for transmitter & holding register to empty * and restore the IER */ wait_for_xmitr(port); sio_out(port, TXX9_SIDICR, ier); } static kdev_t serial_console_device(struct console *c) { return MKDEV(TXX9_TTY_MAJOR, TXX9_TTY_MINOR_START + c->index); } static __init int serial_console_setup(struct console *co, char *options) { struct rs_port *port; unsigned cval; int baud = 9600; int bits = 8; int parity = 'n'; int doflow = 0; int cflag = CREAD | HUPCL | CLOCAL; int quot = 0; char *s; if (co->index < 0 || co->index >= NR_PORTS) return -1; if (options) { baud = simple_strtoul(options, NULL, 10); s = options; while(*s >= '0' && *s <= '9') s++; if (*s) parity = *s++; if (*s) bits = *s - '0'; if (*s) doflow = (*s++ == 'r'); } /* * Now construct a cflag setting. */ switch(baud) { case 1200: cflag |= B1200; break; case 2400: cflag |= B2400; break; case 4800: cflag |= B4800; break; case 19200: cflag |= B19200; break; case 38400: cflag |= B38400; break; case 57600: cflag |= B57600; break; case 115200: cflag |= B115200; break; default: /* * Set this to a sane value to prevent a divide error */ baud = 9600; case 9600: cflag |= B9600; break; } switch(bits) { case 7: cflag |= CS7; break; default: case 8: cflag |= CS8; break; } switch(parity) { case 'o': case 'O': cflag |= PARODD; break; case 'e': case 'E': cflag |= PARENB; break; } co->cflag = cflag; port = &rs_ports[co->index]; if (!port->base) return -1; /* * Divisor, bytesize and parity */ #if (ASYNC_INTERNAL_FLAGS & GS_INTERNAL_FLAGS) == 0 /* check conflict... */ if (doflow) port->gs.flags |= ASYNC_CONS_FLOW; #endif quot = port->baud_base / baud; switch (cflag & CSIZE) { case CS7: cval = TXX9_SILCR_UMODE_7BIT; break; default: case CS8: cval = TXX9_SILCR_UMODE_8BIT; break; } if (cflag & CSTOPB) cval |= TXX9_SILCR_USBL_2BIT; else cval |= TXX9_SILCR_USBL_1BIT; if (cflag & PARENB) cval |= TXX9_SILCR_UPEN; if (!(cflag & PARODD)) cval |= TXX9_SILCR_UEPS; /* * Disable UART interrupts, set DTR and RTS high * and set speed. */ sio_out(port, TXX9_SIDICR, 0); sio_out(port, TXX9_SILCR, cval | TXX9_SILCR_SCS_IMCLK_BG); sio_out(port, TXX9_SIBGR, quot | TXX9_SIBGR_BCLK_T0); /* no RTS/CTS control */ sio_out(port, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */); /* Enable RX/TX */ sio_mask(port, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); /* console port should not use RTC/CTS. */ port->flags &= ~TXX9_SERIAL_HAVE_CTS_LINE; return 0; } static struct console sercons = { name: TXX9_TTY_NAME, write: serial_console_write, device: serial_console_device, setup: serial_console_setup, flags: CON_PRINTBUFFER, index: -1, }; void __init txx9_serial_console_init(void) { register_console(&sercons); } #endif /******************************************************************************/ /* BEG: KDBG Routines */ /******************************************************************************/ #ifdef CONFIG_KGDB int kgdb_init_count = 0; #endif #ifdef CONFIG_KGDB void txx9_sio_kgdb_hook(unsigned int port, unsigned int baud_rate) { static struct resource kgdb_resource; int ret; /* prevent initialization by driver */ kgdb_resource.name = "serial_txx9(debug)"; kgdb_resource.start = rs_ports[port].base; kgdb_resource.end = rs_ports[port].base + 36 - 1; kgdb_resource.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ret = request_resource(&iomem_resource, &kgdb_resource); if(ret == -EBUSY) printk(" serial_txx9(debug): request_resource failed\n"); return; } #endif /* CONFIG_KGDB */ #ifdef CONFIG_KGDB void txx9_sio_kdbg_init( unsigned int port_number ) { if ( port_number == 1 ) { txx9_sio_kgdb_hook( port_number, 38400 ); } else { printk("Bad Port Number [%u] != [1]\n",port_number); } return; } #endif /* CONFIG_KGDB */ #ifdef CONFIG_KGDB u8 txx9_sio_kdbg_rd( void ) { unsigned int status,ch; if ( kgdb_init_count == 0 ) { txx9_sio_kdbg_init( 1 ); kgdb_init_count = 1; } while ( 1 ) { status = sio_in(&rs_ports[1], TXX9_SIDISR); if ( status & 0x1f ) { ch = sio_in(&rs_ports[1], TXX9_SIRFIFO ); break; } } return( ch ); } #endif /* CONFIG_KGDB */ #ifdef CONFIG_KGDB int txx9_sio_kdbg_wr( u8 ch ) { unsigned int status; if ( kgdb_init_count == 0 ) { txx9_sio_kdbg_init( 1 ); kgdb_init_count = 1; } while ( 1 ) { status = sio_in(&rs_ports[1], TXX9_SICISR); if (status & TXX9_SICISR_TRDY) { if ( ch == '\n' ) { txx9_sio_kdbg_wr( '\r' ); } sio_out(&rs_ports[1], TXX9_SITFIFO, (u32)ch ); break; } } return( 1 ); } #endif /* CONFIG_KGDB */ /******************************************************************************/ /* END: KDBG Routines */ /******************************************************************************/ void txx9_raw_output(char c) { struct rs_port *port = &rs_ports[0]; if ( c == '\n' ) { sio_out(port, TXX9_SITFIFO, '\r'); wait_for_xmitr(port); } sio_out(port, TXX9_SITFIFO, c); wait_for_xmitr(port); return; }
Go to most recent revision | Compare with Previous | Blame | View Log