summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-06 11:14:33 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-06 11:14:33 -0700
commit4de9ad9bc08b4953fc03336ad38908496e2f8826 (patch)
treebd44add223061a58317034a0d6c9686d95d12fba /drivers/tty
parent576c25eb5954035b64112188d9a2683144600f3d (diff)
parent06da6629e68ddc8ffe2933d33b3681f09104b3f1 (diff)
downloadlinux-4de9ad9bc08b4953fc03336ad38908496e2f8826.tar.bz2
Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile
Pull Tile arch updates from Chris Metcalf: "These changes bring in a bunch of new functionality that has been maintained internally at Tilera over the last year, plus other stray bits of work that I've taken into the tile tree from other folks. The changes include some PCI root complex work, interrupt-driven console support, support for performing fast-path unaligned data fixups by kernel-based JIT code generation, CONFIG_PREEMPT support, vDSO support for gettimeofday(), a serial driver for the tilegx on-chip UART, KGDB support, more optimized string routines, support for ftrace and kprobes, improved ASLR, and many bug fixes. We also remove support for the old TILE64 chip, which is no longer buildable" * git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile: (85 commits) tile: refresh tile defconfig files tile: rework <asm/cmpxchg.h> tile PCI RC: make default consistent DMA mask 32-bit tile: add null check for kzalloc in tile/kernel/setup.c tile: make __write_once a synonym for __read_mostly tile: remove support for TILE64 tile: use asm-generic/bitops/builtin-*.h tile: eliminate no-op "noatomichash" boot argument tile: use standard tile_bundle_bits type in traps.c tile: simplify code referencing hypervisor API addresses tile: change <asm/system.h> to <asm/switch_to.h> in comments tile: mark pcibios_init() as __init tile: check for correct compiler earlier in asm-offsets.c tile: use standard 'generic-y' model for <asm/hw_irq.h> tile: use asm-generic version of <asm/local64.h> tile PCI RC: add comment about "PCI hole" problem tile: remove DEBUG_EXTRA_FLAGS kernel config option tile: add virt_to_kpte() API and clean up and document behavior tile: support FRAME_POINTER tile: support reporting Tilera hypervisor statistics ...
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");