|
|
/*
|
|
|
* 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 uart_port *port = data;
|
|
|
v850e_uart_tx (port);
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
static irqreturn_t v850e_uart_rx_irq(int irq, void *data)
|
|
|
{
|
|
|
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->port.tty, ch, ch_stat);
|
|
|
tty_schedule_flip (port->info->port.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 ktermios *termios,
|
|
|
struct ktermios *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");
|
|
|
|