tilegx: Add tty serial support for TILE-Gx on-chip UART
authorChris Metcalf <cmetcalf@tilera.com>
Mon, 12 Aug 2013 18:11:44 +0000 (14:11 -0400)
committerChris Metcalf <cmetcalf@tilera.com>
Tue, 3 Sep 2013 18:50:40 +0000 (14:50 -0400)
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
MAINTAINERS
drivers/tty/serial/Kconfig
drivers/tty/serial/Makefile
drivers/tty/serial/tilegx.c [new file with mode: 0644]
include/uapi/linux/serial_core.h

index 8f49198..23479fa 100644 (file)
@@ -8293,6 +8293,7 @@ F:        drivers/edac/tile_edac.c
 F:     drivers/net/ethernet/tile/
 F:     drivers/rtc/rtc-tile.c
 F:     drivers/tty/hvc/hvc_tile.c
+F:     drivers/tty/serial/tilegx.c
 F:     drivers/usb/host/*-tilegx.c
 F:     include/linux/usb/tilegx.h
 
index 5e3d689..fdcaaef 100644 (file)
@@ -1436,6 +1436,15 @@ config SERIAL_EFM32_UART_CONSOLE
        depends on SERIAL_EFM32_UART=y
        select SERIAL_CORE_CONSOLE
 
+config SERIAL_TILEGX
+       tristate "TILE-Gx on-chip serial port support"
+       depends on TILEGX
+       select TILE_GXIO_UART
+       select SERIAL_CORE
+       ---help---
+         This device provides access to the on-chip UARTs on the TILE-Gx
+         processor.
+
 config SERIAL_ARC
        tristate "ARC UART driver support"
        select SERIAL_CORE
index cf650f0..3d0f097 100644 (file)
@@ -65,6 +65,7 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_TILEGX) += tilegx.o
 obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
 obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
 obj-$(CONFIG_SERIAL_TIMBERDALE)        += timbuart.o
diff --git a/drivers/tty/serial/tilegx.c b/drivers/tty/serial/tilegx.c
new file mode 100644 (file)
index 0000000..f92d7e6
--- /dev/null
@@ -0,0 +1,708 @@
+/*
+ * Copyright 2013 Tilera Corporation. All Rights Reserved.
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ *
+ * TILEGx UART driver.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <gxio/common.h>
+#include <gxio/iorpc_globals.h>
+#include <gxio/iorpc_uart.h>
+#include <gxio/kiorpc.h>
+
+#include <hv/drv_uart_intf.h>
+
+/*
+ * Use device name ttyS, major 4, minor 64-65.
+ * This is the usual serial port name, 8250 conventional range.
+ */
+#define TILEGX_UART_MAJOR      TTY_MAJOR
+#define TILEGX_UART_MINOR      64
+#define TILEGX_UART_NAME       "ttyS"
+#define DRIVER_NAME_STRING     "TILEGx_Serial"
+#define TILEGX_UART_REF_CLK    125000000; /* REF_CLK is always 125 MHz. */
+
+struct tile_uart_port {
+       /* UART port. */
+       struct uart_port        uart;
+
+       /* GXIO device context. */
+       gxio_uart_context_t     context;
+
+       /* UART access mutex. */
+       struct mutex            mutex;
+
+       /* CPU receiving interrupts. */
+       int                     irq_cpu;
+};
+
+static struct tile_uart_port tile_uart_ports[TILEGX_UART_NR];
+static struct uart_driver tilegx_uart_driver;
+
+
+/*
+ * Read UART rx fifo, and insert the chars into tty buffer.
+ */
+static void receive_chars(struct tile_uart_port *tile_uart,
+                         struct tty_struct *tty)
+{
+       int i;
+       char c;
+       UART_FIFO_COUNT_t count;
+       gxio_uart_context_t *context = &tile_uart->context;
+       struct tty_port *port = tty->port;
+
+       count.word = gxio_uart_read(context, UART_FIFO_COUNT);
+       for (i = 0; i < count.rfifo_count; i++) {
+               c = (char)gxio_uart_read(context, UART_RECEIVE_DATA);
+               tty_insert_flip_char(port, c, TTY_NORMAL);
+       }
+}
+
+
+/*
+ * Drain the Rx FIFO, called by interrupt handler.
+ */
+static void handle_receive(struct tile_uart_port *tile_uart)
+{
+       struct tty_port *port = &tile_uart->uart.state->port;
+       struct tty_struct *tty = tty_port_tty_get(port);
+       gxio_uart_context_t *context = &tile_uart->context;
+
+       if (!tty)
+               return;
+
+       /* First read UART rx fifo. */
+       receive_chars(tile_uart, tty);
+
+       /* Reset RFIFO_WE interrupt. */
+       gxio_uart_write(context, UART_INTERRUPT_STATUS,
+                       UART_INTERRUPT_MASK__RFIFO_WE_MASK);
+
+       /* Final read, if any chars comes between the first read and
+        * the interrupt reset.
+        */
+       receive_chars(tile_uart, tty);
+
+       spin_unlock(&tile_uart->uart.lock);
+       tty_flip_buffer_push(port);
+       spin_lock(&tile_uart->uart.lock);
+       tty_kref_put(tty);
+}
+
+
+/*
+ * Push one char to UART Write FIFO.
+ * Return 0 on success, -1 if write filo is full.
+ */
+static int tilegx_putchar(gxio_uart_context_t *context, char c)
+{
+       UART_FLAG_t flag;
+       flag.word = gxio_uart_read(context, UART_FLAG);
+       if (flag.wfifo_full)
+               return -1;
+
+       gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c);
+       return 0;
+}
+
+
+/*
+ * Send chars to UART Write FIFO; called by interrupt handler.
+ */
+static void handle_transmit(struct tile_uart_port *tile_uart)
+{
+       unsigned char ch;
+       struct uart_port *port;
+       struct circ_buf *xmit;
+       gxio_uart_context_t *context = &tile_uart->context;
+
+       /* First reset WFIFO_RE interrupt. */
+       gxio_uart_write(context, UART_INTERRUPT_STATUS,
+                       UART_INTERRUPT_MASK__WFIFO_RE_MASK);
+
+       port = &tile_uart->uart;
+       xmit = &port->state->xmit;
+       if (port->x_char) {
+               if (tilegx_putchar(context, port->x_char))
+                       return;
+               port->x_char = 0;
+               port->icount.tx++;
+       }
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+               return;
+
+       while (!uart_circ_empty(xmit)) {
+               ch = xmit->buf[xmit->tail];
+               if (tilegx_putchar(context, ch))
+                       break;
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+       }
+
+       /* Reset WFIFO_RE interrupt. */
+       gxio_uart_write(context, UART_INTERRUPT_STATUS,
+                       UART_INTERRUPT_MASK__WFIFO_RE_MASK);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+}
+
+
+/*
+ * UART Interrupt handler.
+ */
+static irqreturn_t tilegx_interrupt(int irq, void *dev_id)
+{
+       unsigned long flags;
+       UART_INTERRUPT_STATUS_t intr_stat;
+       struct tile_uart_port *tile_uart;
+       gxio_uart_context_t *context;
+       struct uart_port *port = dev_id;
+       irqreturn_t ret = IRQ_NONE;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       context = &tile_uart->context;
+       intr_stat.word = gxio_uart_read(context, UART_INTERRUPT_STATUS);
+
+       if (intr_stat.rfifo_we) {
+               handle_receive(tile_uart);
+               ret = IRQ_HANDLED;
+       }
+       if (intr_stat.wfifo_re) {
+               handle_transmit(tile_uart);
+               ret = IRQ_HANDLED;
+       }
+
+       spin_unlock_irqrestore(&port->lock, flags);
+       return ret;
+}
+
+
+/*
+ * Return TIOCSER_TEMT when transmitter FIFO is empty.
+ */
+static u_int tilegx_tx_empty(struct uart_port *port)
+{
+       int ret;
+       UART_FLAG_t flag;
+       struct tile_uart_port *tile_uart;
+       gxio_uart_context_t *context;
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       if (!mutex_trylock(&tile_uart->mutex))
+               return 0;
+       context = &tile_uart->context;
+
+       flag.word = gxio_uart_read(context, UART_FLAG);
+       ret = (flag.wfifo_empty) ? TIOCSER_TEMT : 0;
+       mutex_unlock(&tile_uart->mutex);
+
+       return ret;
+}
+
+
+/*
+ * Set state of the modem control output lines.
+ */
+static void tilegx_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+       /* N/A */
+}
+
+
+/*
+ * Get state of the modem control input lines.
+ */
+static u_int tilegx_get_mctrl(struct uart_port *port)
+{
+       return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+
+/*
+ * Stop transmitting.
+ */
+static void tilegx_stop_tx(struct uart_port *port)
+{
+       /* N/A */
+}
+
+
+/*
+ * Start transmitting.
+ */
+static void tilegx_start_tx(struct uart_port *port)
+{
+       unsigned char ch;
+       struct circ_buf *xmit;
+       struct tile_uart_port *tile_uart;
+       gxio_uart_context_t *context;
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       if (!mutex_trylock(&tile_uart->mutex))
+               return;
+       context = &tile_uart->context;
+       xmit = &port->state->xmit;
+       if (port->x_char) {
+               if (tilegx_putchar(context, port->x_char))
+                       return;
+               port->x_char = 0;
+               port->icount.tx++;
+       }
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               mutex_unlock(&tile_uart->mutex);
+               return;
+       }
+
+       while (!uart_circ_empty(xmit)) {
+               ch = xmit->buf[xmit->tail];
+               if (tilegx_putchar(context, ch))
+                       break;
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+       }
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+
+       mutex_unlock(&tile_uart->mutex);
+}
+
+
+/*
+ * Stop receiving - port is in process of being closed.
+ */
+static void tilegx_stop_rx(struct uart_port *port)
+{
+       int err;
+       struct tile_uart_port *tile_uart;
+       gxio_uart_context_t *context;
+       int cpu;
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       if (!mutex_trylock(&tile_uart->mutex))
+               return;
+
+       context = &tile_uart->context;
+       cpu = tile_uart->irq_cpu;
+       err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
+                                     KERNEL_PL, -1);
+       mutex_unlock(&tile_uart->mutex);
+}
+
+
+/*
+ * Enable modem status interrupts.
+ */
+static void tilegx_enable_ms(struct uart_port *port)
+{
+       /* N/A */
+}
+
+/*
+ * Control the transmission of a break signal.
+ */
+static void tilegx_break_ctl(struct uart_port *port, int break_state)
+{
+       /* N/A */
+}
+
+
+/*
+ * Perform initialization and enable port for reception.
+ */
+static int tilegx_startup(struct uart_port *port)
+{
+       struct tile_uart_port *tile_uart;
+       gxio_uart_context_t *context;
+       int ret = 0;
+       int cpu = raw_smp_processor_id();  /* pick an arbitrary cpu */
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       if (mutex_lock_interruptible(&tile_uart->mutex))
+               return -EBUSY;
+       context = &tile_uart->context;
+
+       /* Now open the hypervisor device if we haven't already. */
+       if (context->fd < 0) {
+               UART_INTERRUPT_MASK_t intr_mask;
+
+               /* Initialize UART device. */
+               ret = gxio_uart_init(context, port->line);
+               if (ret) {
+                       ret = -ENXIO;
+                       goto err;
+               }
+
+               /* Create our IRQs. */
+               port->irq = create_irq();
+               if (port->irq < 0)
+                       goto err_uart_dest;
+               tile_irq_activate(port->irq, TILE_IRQ_PERCPU);
+
+               /* Register our IRQs. */
+               ret = request_irq(port->irq, tilegx_interrupt, 0,
+                                 tilegx_uart_driver.driver_name, port);
+               if (ret)
+                       goto err_dest_irq;
+
+               /* Request that the hardware start sending us interrupts. */
+               tile_uart->irq_cpu = cpu;
+               ret = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
+                                             KERNEL_PL, port->irq);
+               if (ret)
+                       goto err_free_irq;
+
+               /* Enable UART Tx/Rx Interrupt. */
+               intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK);
+               intr_mask.wfifo_re = 0;
+               intr_mask.rfifo_we = 0;
+               gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word);
+
+               /* Reset the Tx/Rx interrupt in case it's set. */
+               gxio_uart_write(context, UART_INTERRUPT_STATUS,
+                               UART_INTERRUPT_MASK__WFIFO_RE_MASK |
+                               UART_INTERRUPT_MASK__RFIFO_WE_MASK);
+       }
+
+       mutex_unlock(&tile_uart->mutex);
+       return ret;
+
+err_free_irq:
+       free_irq(port->irq, port);
+err_dest_irq:
+       destroy_irq(port->irq);
+err_uart_dest:
+       gxio_uart_destroy(context);
+       ret = -ENXIO;
+err:
+       mutex_unlock(&tile_uart->mutex);
+       return ret;
+}
+
+
+/*
+ * Release kernel resources if it is the last close, disable the port,
+ * free IRQ and close the port.
+ */
+static void tilegx_shutdown(struct uart_port *port)
+{
+       int err;
+       UART_INTERRUPT_MASK_t intr_mask;
+       struct tile_uart_port *tile_uart;
+       gxio_uart_context_t *context;
+       int cpu;
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       if (mutex_lock_interruptible(&tile_uart->mutex))
+               return;
+       context = &tile_uart->context;
+
+       /* Disable UART Tx/Rx Interrupt. */
+       intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK);
+       intr_mask.wfifo_re = 1;
+       intr_mask.rfifo_we = 1;
+       gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word);
+
+       /* Request that the hardware stop sending us interrupts. */
+       cpu = tile_uart->irq_cpu;
+       err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
+                                     KERNEL_PL, -1);
+
+       if (port->irq > 0) {
+               free_irq(port->irq, port);
+               destroy_irq(port->irq);
+               port->irq = 0;
+       }
+
+       gxio_uart_destroy(context);
+
+       mutex_unlock(&tile_uart->mutex);
+}
+
+
+/*
+ * Flush the buffer.
+ */
+static void tilegx_flush_buffer(struct uart_port *port)
+{
+       /* N/A */
+}
+
+
+/*
+ * Change the port parameters.
+ */
+static void tilegx_set_termios(struct uart_port *port,
+                              struct ktermios *termios, struct ktermios *old)
+{
+       int err;
+       UART_DIVISOR_t divisor;
+       UART_TYPE_t type;
+       unsigned int baud;
+       struct tile_uart_port *tile_uart;
+       gxio_uart_context_t *context;
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       if (!mutex_trylock(&tile_uart->mutex))
+               return;
+       context = &tile_uart->context;
+
+       /* Open the hypervisor device if we haven't already. */
+       if (context->fd < 0) {
+               err = gxio_uart_init(context, port->line);
+               if (err) {
+                       mutex_unlock(&tile_uart->mutex);
+                       return;
+               }
+       }
+
+       divisor.word = gxio_uart_read(context, UART_DIVISOR);
+       type.word = gxio_uart_read(context, UART_TYPE);
+
+       /* Divisor. */
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+       divisor.divisor = uart_get_divisor(port, baud);
+
+       /* Byte size. */
+       if ((termios->c_cflag & CSIZE) == CS7)
+               type.dbits = UART_TYPE__DBITS_VAL_SEVEN_DBITS;
+       else
+               type.dbits = UART_TYPE__DBITS_VAL_EIGHT_DBITS;
+
+       /* Parity. */
+       if (termios->c_cflag & PARENB) {
+               /* Mark or Space parity. */
+               if (termios->c_cflag & CMSPAR)
+                       if (termios->c_cflag & PARODD)
+                               type.ptype = UART_TYPE__PTYPE_VAL_MARK;
+                       else
+                               type.ptype = UART_TYPE__PTYPE_VAL_SPACE;
+               else if (termios->c_cflag & PARODD)
+                       type.ptype = UART_TYPE__PTYPE_VAL_ODD;
+               else
+                       type.ptype = UART_TYPE__PTYPE_VAL_EVEN;
+       } else
+               type.ptype = UART_TYPE__PTYPE_VAL_NONE;
+
+       /* Stop bits. */
+       if (termios->c_cflag & CSTOPB)
+               type.sbits = UART_TYPE__SBITS_VAL_TWO_SBITS;
+       else
+               type.sbits = UART_TYPE__SBITS_VAL_ONE_SBITS;
+
+       /* Set the uart paramters. */
+       gxio_uart_write(context, UART_DIVISOR, divisor.word);
+       gxio_uart_write(context, UART_TYPE, type.word);
+
+       mutex_unlock(&tile_uart->mutex);
+}
+
+
+/*
+ * Return string describing the specified port.
+ */
+static const char *tilegx_type(struct uart_port *port)
+{
+       return port->type == PORT_TILEGX ? DRIVER_NAME_STRING : NULL;
+}
+
+
+/*
+ * Release the resources being used by 'port'.
+ */
+static void tilegx_release_port(struct uart_port *port)
+{
+       /* Nothing to release. */
+}
+
+
+/*
+ * Request the resources being used by 'port'.
+ */
+static int tilegx_request_port(struct uart_port *port)
+{
+       /* Always present. */
+       return 0;
+}
+
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void tilegx_config_port(struct uart_port *port, int flags)
+{
+       if (flags & UART_CONFIG_TYPE)
+               port->type = PORT_TILEGX;
+}
+
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int tilegx_verify_port(struct uart_port *port,
+                             struct serial_struct *ser)
+{
+       if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_TILEGX))
+               return -EINVAL;
+
+       return 0;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int tilegx_poll_get_char(struct uart_port *port)
+{
+       UART_FIFO_COUNT_t count;
+       gxio_uart_context_t *context;
+       struct tile_uart_port *tile_uart;
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       context = &tile_uart->context;
+       count.word = gxio_uart_read(context, UART_FIFO_COUNT);
+       if (count.rfifo_count == 0)
+               return NO_POLL_CHAR;
+       return (char)gxio_uart_read(context, UART_RECEIVE_DATA);
+}
+
+static void tilegx_poll_put_char(struct uart_port *port, unsigned char c)
+{
+       gxio_uart_context_t *context;
+       struct tile_uart_port *tile_uart;
+
+       tile_uart = container_of(port, struct tile_uart_port, uart);
+       context = &tile_uart->context;
+       gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+
+static const struct uart_ops tilegx_ops = {
+       .tx_empty       = tilegx_tx_empty,
+       .set_mctrl      = tilegx_set_mctrl,
+       .get_mctrl      = tilegx_get_mctrl,
+       .stop_tx        = tilegx_stop_tx,
+       .start_tx       = tilegx_start_tx,
+       .stop_rx        = tilegx_stop_rx,
+       .enable_ms      = tilegx_enable_ms,
+       .break_ctl      = tilegx_break_ctl,
+       .startup        = tilegx_startup,
+       .shutdown       = tilegx_shutdown,
+       .flush_buffer   = tilegx_flush_buffer,
+       .set_termios    = tilegx_set_termios,
+       .type           = tilegx_type,
+       .release_port   = tilegx_release_port,
+       .request_port   = tilegx_request_port,
+       .config_port    = tilegx_config_port,
+       .verify_port    = tilegx_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_get_char  = tilegx_poll_get_char,
+       .poll_put_char  = tilegx_poll_put_char,
+#endif
+};
+
+
+static void tilegx_init_ports(void)
+{
+       int i;
+       struct uart_port *port;
+
+       for (i = 0; i < TILEGX_UART_NR; i++) {
+               port = &tile_uart_ports[i].uart;
+               port->ops = &tilegx_ops;
+               port->line = i;
+               port->type = PORT_TILEGX;
+               port->uartclk = TILEGX_UART_REF_CLK;
+               port->flags = UPF_BOOT_AUTOCONF;
+
+               tile_uart_ports[i].context.fd = -1;
+               mutex_init(&tile_uart_ports[i].mutex);
+       }
+}
+
+
+static struct uart_driver tilegx_uart_driver = {
+       .owner          = THIS_MODULE,
+       .driver_name    = DRIVER_NAME_STRING,
+       .dev_name       = TILEGX_UART_NAME,
+       .major          = TILEGX_UART_MAJOR,
+       .minor          = TILEGX_UART_MINOR,
+       .nr             = TILEGX_UART_NR,
+};
+
+
+static int __init tilegx_init(void)
+{
+       int i;
+       int ret;
+       struct tty_driver *tty_drv;
+
+       ret = uart_register_driver(&tilegx_uart_driver);
+       if (ret)
+               return ret;
+       tty_drv = tilegx_uart_driver.tty_driver;
+       tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+       tty_drv->init_termios.c_ispeed = 115200;
+       tty_drv->init_termios.c_ospeed = 115200;
+
+       tilegx_init_ports();
+
+       for (i = 0; i < TILEGX_UART_NR; i++) {
+               struct uart_port *port = &tile_uart_ports[i].uart;
+               ret = uart_add_one_port(&tilegx_uart_driver, port);
+       }
+
+       return 0;
+}
+
+
+static void __exit tilegx_exit(void)
+{
+       int i;
+       struct uart_port *port;
+
+       for (i = 0; i < TILEGX_UART_NR; i++) {
+               port = &tile_uart_ports[i].uart;
+               uart_remove_one_port(&tilegx_uart_driver, port);
+       }
+
+       uart_unregister_driver(&tilegx_uart_driver);
+}
+
+
+module_init(tilegx_init);
+module_exit(tilegx_exit);
+
+MODULE_AUTHOR("Tilera Corporation");
+MODULE_DESCRIPTION("TILEGx serial port driver");
+MODULE_LICENSE("GPL");
index 9119cc0..b466487 100644 (file)
 /* SH-SCI */
 #define PORT_HSCIF     104
 
+/* Tilera TILE-Gx UART */
+#define PORT_TILEGX    105
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */