123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- /*
- * drivers/serial/v850e_uart.c -- Serial I/O using V850E on-chip UART or UARTB
- *
- * Copyright (C) 2001,02,03 NEC Electronics Corporation
- * Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License. See the file COPYING in the main directory of this
- * archive for more details.
- *
- * Written by Miles Bader <miles@gnu.org>
- */
- /* This driver supports both the original V850E UART interface (called
- merely `UART' in the docs) and the newer `UARTB' interface, which is
- roughly a superset of the first one. The selection is made at
- configure time -- if CONFIG_V850E_UARTB is defined, then UARTB is
- presumed, otherwise the old UART -- as these are on-CPU UARTS, a system
- can never have both.
- The UARTB interface also has a 16-entry FIFO mode, which is not
- yet supported by this driver. */
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/console.h>
- #include <linux/tty.h>
- #include <linux/tty_flip.h>
- #include <linux/serial.h>
- #include <linux/serial_core.h>
- #include <asm/v850e_uart.h>
- /* Initial UART state. This may be overridden by machine-dependent headers. */
- #ifndef V850E_UART_INIT_BAUD
- #define V850E_UART_INIT_BAUD 115200
- #endif
- #ifndef V850E_UART_INIT_CFLAGS
- #define V850E_UART_INIT_CFLAGS (B115200 | CS8 | CREAD)
- #endif
- /* A string used for prefixing printed descriptions; since the same UART
- macro is actually used on other chips than the V850E. This must be a
- constant string. */
- #ifndef V850E_UART_CHIP_NAME
- #define V850E_UART_CHIP_NAME "V850E"
- #endif
- #define V850E_UART_MINOR_BASE 64 /* First tty minor number */
- /* Low-level UART functions. */
- /* Configure and turn on uart channel CHAN, using the termios `control
- modes' bits in CFLAGS, and a baud-rate of BAUD. */
- void v850e_uart_configure (unsigned chan, unsigned cflags, unsigned baud)
- {
- int flags;
- v850e_uart_speed_t old_speed;
- v850e_uart_config_t old_config;
- v850e_uart_speed_t new_speed = v850e_uart_calc_speed (baud);
- v850e_uart_config_t new_config = v850e_uart_calc_config (cflags);
- /* Disable interrupts while we're twiddling the hardware. */
- local_irq_save (flags);
- #ifdef V850E_UART_PRE_CONFIGURE
- V850E_UART_PRE_CONFIGURE (chan, cflags, baud);
- #endif
- old_config = V850E_UART_CONFIG (chan);
- old_speed = v850e_uart_speed (chan);
- if (! v850e_uart_speed_eq (old_speed, new_speed)) {
- /* The baud rate has changed. First, disable the UART. */
- V850E_UART_CONFIG (chan) = V850E_UART_CONFIG_FINI;
- old_config = 0; /* Force the uart to be re-initialized. */
- /* Reprogram the baud-rate generator. */
- v850e_uart_set_speed (chan, new_speed);
- }
- if (! (old_config & V850E_UART_CONFIG_ENABLED)) {
- /* If we are using the uart for the first time, start by
- enabling it, which must be done before turning on any
- other bits. */
- V850E_UART_CONFIG (chan) = V850E_UART_CONFIG_INIT;
- /* See the initial state. */
- old_config = V850E_UART_CONFIG (chan);
- }
- if (new_config != old_config) {
- /* Which of the TXE/RXE bits we'll temporarily turn off
- before changing other control bits. */
- unsigned temp_disable = 0;
- /* Which of the TXE/RXE bits will be enabled. */
- unsigned enable = 0;
- unsigned changed_bits = new_config ^ old_config;
- /* Which of RX/TX will be enabled in the new configuration. */
- if (new_config & V850E_UART_CONFIG_RX_BITS)
- enable |= (new_config & V850E_UART_CONFIG_RX_ENABLE);
- if (new_config & V850E_UART_CONFIG_TX_BITS)
- enable |= (new_config & V850E_UART_CONFIG_TX_ENABLE);
- /* Figure out which of RX/TX needs to be disabled; note
- that this will only happen if they're not already
- disabled. */
- if (changed_bits & V850E_UART_CONFIG_RX_BITS)
- temp_disable
- |= (old_config & V850E_UART_CONFIG_RX_ENABLE);
- if (changed_bits & V850E_UART_CONFIG_TX_BITS)
- temp_disable
- |= (old_config & V850E_UART_CONFIG_TX_ENABLE);
- /* We have to turn off RX and/or TX mode before changing
- any associated control bits. */
- if (temp_disable)
- V850E_UART_CONFIG (chan) = old_config & ~temp_disable;
- /* Write the new control bits, while RX/TX are disabled. */
- if (changed_bits & ~enable)
- V850E_UART_CONFIG (chan) = new_config & ~enable;
- v850e_uart_config_delay (new_config, new_speed);
- /* Write the final version, with enable bits turned on. */
- V850E_UART_CONFIG (chan) = new_config;
- }
- local_irq_restore (flags);
- }
- /* Low-level console. */
- #ifdef CONFIG_V850E_UART_CONSOLE
- static void v850e_uart_cons_write (struct console *co,
- const char *s, unsigned count)
- {
- if (count > 0) {
- unsigned chan = co->index;
- unsigned irq = V850E_UART_TX_IRQ (chan);
- int irq_was_enabled, irq_was_pending, flags;
- /* We don't want to get `transmission completed'
- interrupts, since we're busy-waiting, so we disable them
- while sending (we don't disable interrupts entirely
- because sending over a serial line is really slow). We
- save the status of the tx interrupt and restore it when
- we're done so that using printk doesn't interfere with
- normal serial transmission (other than interleaving the
- output, of course!). This should work correctly even if
- this function is interrupted and the interrupt printks
- something. */
- /* Disable interrupts while fiddling with tx interrupt. */
- local_irq_save (flags);
- /* Get current tx interrupt status. */
- irq_was_enabled = v850e_intc_irq_enabled (irq);
- irq_was_pending = v850e_intc_irq_pending (irq);
- /* Disable tx interrupt if necessary. */
- if (irq_was_enabled)
- v850e_intc_disable_irq (irq);
- /* Turn interrupts back on. */
- local_irq_restore (flags);
- /* Send characters. */
- while (count > 0) {
- int ch = *s++;
- if (ch == '\n') {
- /* We don't have the benefit of a tty
- driver, so translate NL into CR LF. */
- v850e_uart_wait_for_xmit_ok (chan);
- v850e_uart_putc (chan, '\r');
- }
- v850e_uart_wait_for_xmit_ok (chan);
- v850e_uart_putc (chan, ch);
- count--;
- }
- /* Restore saved tx interrupt status. */
- if (irq_was_enabled) {
- /* Wait for the last character we sent to be
- completely transmitted (as we'll get an
- interrupt interrupt at that point). */
- v850e_uart_wait_for_xmit_done (chan);
- /* Clear pending interrupts received due
- to our transmission, unless there was already
- one pending, in which case we want the
- handler to be called. */
- if (! irq_was_pending)
- v850e_intc_clear_pending_irq (irq);
- /* ... and then turn back on handling. */
- v850e_intc_enable_irq (irq);
- }
- }
- }
- extern struct uart_driver v850e_uart_driver;
- static struct console v850e_uart_cons =
- {
- .name = "ttyS",
- .write = v850e_uart_cons_write,
- .device = uart_console_device,
- .flags = CON_PRINTBUFFER,
- .cflag = V850E_UART_INIT_CFLAGS,
- .index = -1,
- .data = &v850e_uart_driver,
- };
- void v850e_uart_cons_init (unsigned chan)
- {
- v850e_uart_configure (chan, V850E_UART_INIT_CFLAGS,
- V850E_UART_INIT_BAUD);
- v850e_uart_cons.index = chan;
- register_console (&v850e_uart_cons);
- printk ("Console: %s on-chip UART channel %d\n",
- V850E_UART_CHIP_NAME, chan);
- }
- /* This is what the init code actually calls. */
- static int v850e_uart_console_init (void)
- {
- v850e_uart_cons_init (V850E_UART_CONSOLE_CHANNEL);
- return 0;
- }
- console_initcall(v850e_uart_console_init);
- #define V850E_UART_CONSOLE &v850e_uart_cons
- #else /* !CONFIG_V850E_UART_CONSOLE */
- #define V850E_UART_CONSOLE 0
- #endif /* CONFIG_V850E_UART_CONSOLE */
- /* TX/RX interrupt handlers. */
- static void v850e_uart_stop_tx (struct uart_port *port);
- void v850e_uart_tx (struct uart_port *port)
- {
- struct circ_buf *xmit = &port->info->xmit;
- int stopped = uart_tx_stopped (port);
- if (v850e_uart_xmit_ok (port->line)) {
- int tx_ch;
- if (port->x_char) {
- tx_ch = port->x_char;
- port->x_char = 0;
- } else if (!uart_circ_empty (xmit) && !stopped) {
- tx_ch = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- } else
- goto no_xmit;
- v850e_uart_putc (port->line, tx_ch);
- port->icount.tx++;
- if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
- uart_write_wakeup (port);
- }
- no_xmit:
- if (uart_circ_empty (xmit) || stopped)
- v850e_uart_stop_tx (port, stopped);
- }
- static irqreturn_t v850e_uart_tx_irq(int irq, void *data, struct pt_regs *regs)
- {
- struct uart_port *port = data;
- v850e_uart_tx (port);
- return IRQ_HANDLED;
- }
- static irqreturn_t v850e_uart_rx_irq(int irq, void *data, struct pt_regs *regs)
- {
- struct uart_port *port = data;
- unsigned ch_stat = TTY_NORMAL;
- unsigned ch = v850e_uart_getc (port->line);
- unsigned err = v850e_uart_err (port->line);
- if (err) {
- if (err & V850E_UART_ERR_OVERRUN) {
- ch_stat = TTY_OVERRUN;
- port->icount.overrun++;
- } else if (err & V850E_UART_ERR_FRAME) {
- ch_stat = TTY_FRAME;
- port->icount.frame++;
- } else if (err & V850E_UART_ERR_PARITY) {
- ch_stat = TTY_PARITY;
- port->icount.parity++;
- }
- }
- port->icount.rx++;
- tty_insert_flip_char (port->info->tty, ch, ch_stat);
- tty_schedule_flip (port->info->tty);
- return IRQ_HANDLED;
- }
- /* Control functions for the serial framework. */
- static void v850e_uart_nop (struct uart_port *port) { }
- static int v850e_uart_success (struct uart_port *port) { return 0; }
- static unsigned v850e_uart_tx_empty (struct uart_port *port)
- {
- return TIOCSER_TEMT; /* Can't detect. */
- }
- static void v850e_uart_set_mctrl (struct uart_port *port, unsigned mctrl)
- {
- #ifdef V850E_UART_SET_RTS
- V850E_UART_SET_RTS (port->line, (mctrl & TIOCM_RTS));
- #endif
- }
- static unsigned v850e_uart_get_mctrl (struct uart_port *port)
- {
- /* We don't support DCD or DSR, so consider them permanently active. */
- int mctrl = TIOCM_CAR | TIOCM_DSR;
- /* We may support CTS. */
- #ifdef V850E_UART_CTS
- mctrl |= V850E_UART_CTS(port->line) ? TIOCM_CTS : 0;
- #else
- mctrl |= TIOCM_CTS;
- #endif
- return mctrl;
- }
- static void v850e_uart_start_tx (struct uart_port *port)
- {
- v850e_intc_disable_irq (V850E_UART_TX_IRQ (port->line));
- v850e_uart_tx (port);
- v850e_intc_enable_irq (V850E_UART_TX_IRQ (port->line));
- }
- static void v850e_uart_stop_tx (struct uart_port *port)
- {
- v850e_intc_disable_irq (V850E_UART_TX_IRQ (port->line));
- }
- static void v850e_uart_start_rx (struct uart_port *port)
- {
- v850e_intc_enable_irq (V850E_UART_RX_IRQ (port->line));
- }
- static void v850e_uart_stop_rx (struct uart_port *port)
- {
- v850e_intc_disable_irq (V850E_UART_RX_IRQ (port->line));
- }
- static void v850e_uart_break_ctl (struct uart_port *port, int break_ctl)
- {
- /* Umm, do this later. */
- }
- static int v850e_uart_startup (struct uart_port *port)
- {
- int err;
- /* Alloc RX irq. */
- err = request_irq (V850E_UART_RX_IRQ (port->line), v850e_uart_rx_irq,
- SA_INTERRUPT, "v850e_uart", port);
- if (err)
- return err;
- /* Alloc TX irq. */
- err = request_irq (V850E_UART_TX_IRQ (port->line), v850e_uart_tx_irq,
- SA_INTERRUPT, "v850e_uart", port);
- if (err) {
- free_irq (V850E_UART_RX_IRQ (port->line), port);
- return err;
- }
- v850e_uart_start_rx (port);
- return 0;
- }
- static void v850e_uart_shutdown (struct uart_port *port)
- {
- /* Disable port interrupts. */
- free_irq (V850E_UART_TX_IRQ (port->line), port);
- free_irq (V850E_UART_RX_IRQ (port->line), port);
- /* Turn off xmit/recv enable bits. */
- V850E_UART_CONFIG (port->line)
- &= ~(V850E_UART_CONFIG_TX_ENABLE
- | V850E_UART_CONFIG_RX_ENABLE);
- /* Then reset the channel. */
- V850E_UART_CONFIG (port->line) = 0;
- }
- static void
- v850e_uart_set_termios (struct uart_port *port, struct termios *termios,
- struct termios *old)
- {
- unsigned cflags = termios->c_cflag;
- /* Restrict flags to legal values. */
- if ((cflags & CSIZE) != CS7 && (cflags & CSIZE) != CS8)
- /* The new value of CSIZE is invalid, use the old value. */
- cflags = (cflags & ~CSIZE)
- | (old ? (old->c_cflag & CSIZE) : CS8);
- termios->c_cflag = cflags;
- v850e_uart_configure (port->line, cflags,
- uart_get_baud_rate (port, termios, old,
- v850e_uart_min_baud(),
- v850e_uart_max_baud()));
- }
- static const char *v850e_uart_type (struct uart_port *port)
- {
- return port->type == PORT_V850E_UART ? "v850e_uart" : 0;
- }
- static void v850e_uart_config_port (struct uart_port *port, int flags)
- {
- if (flags & UART_CONFIG_TYPE)
- port->type = PORT_V850E_UART;
- }
- static int
- v850e_uart_verify_port (struct uart_port *port, struct serial_struct *ser)
- {
- if (ser->type != PORT_UNKNOWN && ser->type != PORT_V850E_UART)
- return -EINVAL;
- if (ser->irq != V850E_UART_TX_IRQ (port->line))
- return -EINVAL;
- return 0;
- }
- static struct uart_ops v850e_uart_ops = {
- .tx_empty = v850e_uart_tx_empty,
- .get_mctrl = v850e_uart_get_mctrl,
- .set_mctrl = v850e_uart_set_mctrl,
- .start_tx = v850e_uart_start_tx,
- .stop_tx = v850e_uart_stop_tx,
- .stop_rx = v850e_uart_stop_rx,
- .enable_ms = v850e_uart_nop,
- .break_ctl = v850e_uart_break_ctl,
- .startup = v850e_uart_startup,
- .shutdown = v850e_uart_shutdown,
- .set_termios = v850e_uart_set_termios,
- .type = v850e_uart_type,
- .release_port = v850e_uart_nop,
- .request_port = v850e_uart_success,
- .config_port = v850e_uart_config_port,
- .verify_port = v850e_uart_verify_port,
- };
- /* Initialization and cleanup. */
- static struct uart_driver v850e_uart_driver = {
- .owner = THIS_MODULE,
- .driver_name = "v850e_uart",
- .devfs_name = "tts/",
- .dev_name = "ttyS",
- .major = TTY_MAJOR,
- .minor = V850E_UART_MINOR_BASE,
- .nr = V850E_UART_NUM_CHANNELS,
- .cons = V850E_UART_CONSOLE,
- };
- static struct uart_port v850e_uart_ports[V850E_UART_NUM_CHANNELS];
- static int __init v850e_uart_init (void)
- {
- int rval;
- printk (KERN_INFO "%s on-chip UART\n", V850E_UART_CHIP_NAME);
- rval = uart_register_driver (&v850e_uart_driver);
- if (rval == 0) {
- unsigned chan;
- for (chan = 0; chan < V850E_UART_NUM_CHANNELS; chan++) {
- struct uart_port *port = &v850e_uart_ports[chan];
-
- memset (port, 0, sizeof *port);
- port->ops = &v850e_uart_ops;
- port->line = chan;
- port->iotype = UPIO_MEM;
- port->flags = UPF_BOOT_AUTOCONF;
- /* We actually use multiple IRQs, but the serial
- framework seems to mainly use this for
- informational purposes anyway. Here we use the TX
- irq. */
- port->irq = V850E_UART_TX_IRQ (chan);
- /* The serial framework doesn't really use these
- membase/mapbase fields for anything useful, but
- it requires that they be something non-zero to
- consider the port `valid', and also uses them
- for informational purposes. */
- port->membase = (void *)V850E_UART_BASE_ADDR (chan);
- port->mapbase = V850E_UART_BASE_ADDR (chan);
- /* The framework insists on knowing the uart's master
- clock freq, though it doesn't seem to do anything
- useful for us with it. We must make it at least
- higher than (the maximum baud rate * 16), otherwise
- the framework will puke during its internal
- calculations, and force the baud rate to be 9600.
- To be accurate though, just repeat the calculation
- we use when actually setting the speed. */
- port->uartclk = v850e_uart_max_clock() * 16;
- uart_add_one_port (&v850e_uart_driver, port);
- }
- }
- return rval;
- }
- static void __exit v850e_uart_exit (void)
- {
- unsigned chan;
- for (chan = 0; chan < V850E_UART_NUM_CHANNELS; chan++)
- uart_remove_one_port (&v850e_uart_driver,
- &v850e_uart_ports[chan]);
- uart_unregister_driver (&v850e_uart_driver);
- }
- module_init (v850e_uart_init);
- module_exit (v850e_uart_exit);
- MODULE_AUTHOR ("Miles Bader");
- MODULE_DESCRIPTION ("NEC " V850E_UART_CHIP_NAME " on-chip UART");
- MODULE_LICENSE ("GPL");
|