| /* | |
| * 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, | |
| IRQF_DISABLED, "v850e_uart", port); | |
| if (err) | |
| return err; | |
| /* Alloc TX irq. */ | |
| err = request_irq (V850E_UART_TX_IRQ (port->line), v850e_uart_tx_irq, | |
| IRQF_DISABLED, "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", | |
| .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"); |