summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/hvc/hvc_tile.c149
-rw-r--r--drivers/tty/serial/Kconfig9
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/tilegx.c708
4 files changed, 861 insertions, 6 deletions
diff --git a/drivers/tty/hvc/hvc_tile.c b/drivers/tty/hvc/hvc_tile.c
index 7a84a0595477..af8cdaa1dcb9 100644
--- a/drivers/tty/hvc/hvc_tile.c
+++ b/drivers/tty/hvc/hvc_tile.c
@@ -18,16 +18,46 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
#include <linux/types.h>
+#include <asm/setup.h>
+#include <arch/sim_def.h>
+
#include <hv/hypervisor.h>
#include "hvc_console.h"
+static int use_sim_console;
+static int __init sim_console(char *str)
+{
+ use_sim_console = 1;
+ return 0;
+}
+early_param("sim_console", sim_console);
+
+int tile_console_write(const char *buf, int count)
+{
+ if (unlikely(use_sim_console)) {
+ int i;
+ for (i = 0; i < count; ++i)
+ __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
+ (buf[i] << _SIM_CONTROL_OPERATOR_BITS));
+ __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
+ (SIM_PUTC_FLUSH_BINARY <<
+ _SIM_CONTROL_OPERATOR_BITS));
+ return 0;
+ } else {
+ return hv_console_write((HV_VirtAddr)buf, count);
+ }
+}
+
static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count)
{
- return hv_console_write((HV_VirtAddr)buf, count);
+ return tile_console_write(buf, count);
}
static int hvc_tile_get_chars(uint32_t vt, char *buf, int count)
@@ -44,25 +74,132 @@ static int hvc_tile_get_chars(uint32_t vt, char *buf, int count)
return i;
}
+#ifdef __tilegx__
+/*
+ * IRQ based callbacks.
+ */
+static int hvc_tile_notifier_add_irq(struct hvc_struct *hp, int irq)
+{
+ int rc;
+ int cpu = raw_smp_processor_id(); /* Choose an arbitrary cpu */
+ HV_Coord coord = { .x = cpu_x(cpu), .y = cpu_y(cpu) };
+
+ rc = notifier_add_irq(hp, irq);
+ if (rc)
+ return rc;
+
+ /*
+ * Request that the hypervisor start sending us interrupts.
+ * If the hypervisor returns an error, we still return 0, so that
+ * we can fall back to polling.
+ */
+ if (hv_console_set_ipi(KERNEL_PL, irq, coord) < 0)
+ notifier_del_irq(hp, irq);
+
+ return 0;
+}
+
+static void hvc_tile_notifier_del_irq(struct hvc_struct *hp, int irq)
+{
+ HV_Coord coord = { 0, 0 };
+
+ /* Tell the hypervisor to stop sending us interrupts. */
+ hv_console_set_ipi(KERNEL_PL, -1, coord);
+
+ notifier_del_irq(hp, irq);
+}
+
+static void hvc_tile_notifier_hangup_irq(struct hvc_struct *hp, int irq)
+{
+ hvc_tile_notifier_del_irq(hp, irq);
+}
+#endif
+
static const struct hv_ops hvc_tile_get_put_ops = {
.get_chars = hvc_tile_get_chars,
.put_chars = hvc_tile_put_chars,
+#ifdef __tilegx__
+ .notifier_add = hvc_tile_notifier_add_irq,
+ .notifier_del = hvc_tile_notifier_del_irq,
+ .notifier_hangup = hvc_tile_notifier_hangup_irq,
+#endif
+};
+
+
+#ifdef __tilegx__
+static int hvc_tile_probe(struct platform_device *pdev)
+{
+ struct hvc_struct *hp;
+ int tile_hvc_irq;
+
+ /* Create our IRQ and register it. */
+ tile_hvc_irq = create_irq();
+ if (tile_hvc_irq < 0)
+ return -ENXIO;
+
+ tile_irq_activate(tile_hvc_irq, TILE_IRQ_PERCPU);
+ hp = hvc_alloc(0, tile_hvc_irq, &hvc_tile_get_put_ops, 128);
+ if (IS_ERR(hp)) {
+ destroy_irq(tile_hvc_irq);
+ return PTR_ERR(hp);
+ }
+ dev_set_drvdata(&pdev->dev, hp);
+
+ return 0;
+}
+
+static int hvc_tile_remove(struct platform_device *pdev)
+{
+ int rc;
+ struct hvc_struct *hp = dev_get_drvdata(&pdev->dev);
+
+ rc = hvc_remove(hp);
+ if (rc == 0)
+ destroy_irq(hp->data);
+
+ return rc;
+}
+
+static void hvc_tile_shutdown(struct platform_device *pdev)
+{
+ struct hvc_struct *hp = dev_get_drvdata(&pdev->dev);
+
+ hvc_tile_notifier_del_irq(hp, hp->data);
+}
+
+static struct platform_device hvc_tile_pdev = {
+ .name = "hvc-tile",
+ .id = 0,
+};
+
+static struct platform_driver hvc_tile_driver = {
+ .probe = hvc_tile_probe,
+ .remove = hvc_tile_remove,
+ .shutdown = hvc_tile_shutdown,
+ .driver = {
+ .name = "hvc-tile",
+ .owner = THIS_MODULE,
+ }
};
+#endif
static int __init hvc_tile_console_init(void)
{
- extern void disable_early_printk(void);
hvc_instantiate(0, 0, &hvc_tile_get_put_ops);
add_preferred_console("hvc", 0, NULL);
- disable_early_printk();
return 0;
}
console_initcall(hvc_tile_console_init);
static int __init hvc_tile_init(void)
{
- struct hvc_struct *s;
- s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128);
- return IS_ERR(s) ? PTR_ERR(s) : 0;
+#ifndef __tilegx__
+ struct hvc_struct *hp;
+ hp = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128);
+ return IS_ERR(hp) ? PTR_ERR(hp) : 0;
+#else
+ platform_device_register(&hvc_tile_pdev);
+ return platform_driver_register(&hvc_tile_driver);
+#endif
}
device_initcall(hvc_tile_init);
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index cc4c8682b47b..47c6e7b9e150 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1439,6 +1439,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
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 47b679c547e9..3068c7722087 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -66,6 +66,7 @@ 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_ST_ASC) += st-asc.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
index 000000000000..f92d7e6bd876
--- /dev/null
+++ b/drivers/tty/serial/tilegx.c
@@ -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");