All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] tilegx: Add tty serial support for TILE-Gx on-chip UART
@ 2013-08-12 18:11 ` Chris Metcalf
  0 siblings, 0 replies; 6+ messages in thread
From: Chris Metcalf @ 2013-08-12 18:11 UTC (permalink / raw)
  To: linux-kernel, linux-serial, Greg Kroah-Hartman, Jiri Slaby

Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
---
 MAINTAINERS                      |   1 +
 drivers/tty/serial/Kconfig       |   9 +
 drivers/tty/serial/Makefile      |   1 +
 drivers/tty/serial/tilegx.c      | 708 +++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |   3 +
 5 files changed, 722 insertions(+)
 create mode 100644 drivers/tty/serial/tilegx.c

diff --git a/MAINTAINERS b/MAINTAINERS
index b8e3bea..ec99ec5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8294,6 +8294,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
 
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 5e3d689..fdcaaef 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -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
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cf650f0..3d0f097 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -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
index 0000000..f92d7e6
--- /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");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 9119cc0..b466487 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -232,4 +232,7 @@
 /* SH-SCI */
 #define PORT_HSCIF	104
 
+/* Tilera TILE-Gx UART */
+#define PORT_TILEGX	105
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH] tilegx: Add tty serial support for TILE-Gx on-chip UART
@ 2013-08-12 18:11 ` Chris Metcalf
  0 siblings, 0 replies; 6+ messages in thread
From: Chris Metcalf @ 2013-08-12 18:11 UTC (permalink / raw)
  To: linux-kernel, linux-serial, Greg Kroah-Hartman, Jiri Slaby

Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
---
 MAINTAINERS                      |   1 +
 drivers/tty/serial/Kconfig       |   9 +
 drivers/tty/serial/Makefile      |   1 +
 drivers/tty/serial/tilegx.c      | 708 +++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |   3 +
 5 files changed, 722 insertions(+)
 create mode 100644 drivers/tty/serial/tilegx.c

diff --git a/MAINTAINERS b/MAINTAINERS
index b8e3bea..ec99ec5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8294,6 +8294,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
 
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 5e3d689..fdcaaef 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -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
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cf650f0..3d0f097 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -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
index 0000000..f92d7e6
--- /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");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 9119cc0..b466487 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -232,4 +232,7 @@
 /* SH-SCI */
 #define PORT_HSCIF	104
 
+/* Tilera TILE-Gx UART */
+#define PORT_TILEGX	105
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] tilegx: Add tty serial support for TILE-Gx on-chip UART
  2013-08-12 18:11 ` Chris Metcalf
  (?)
@ 2013-08-12 18:43 ` Greg Kroah-Hartman
  2013-08-12 18:56     ` Chris Metcalf
  -1 siblings, 1 reply; 6+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-12 18:43 UTC (permalink / raw)
  To: Chris Metcalf; +Cc: linux-kernel, linux-serial, Jiri Slaby

On Mon, Aug 12, 2013 at 02:11:44PM -0400, Chris Metcalf wrote:
> +/*
> + * 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 */
> +}

For most of these "empty" callbacks, you shouldn't have to declare them
at all, and all should work just fine.

Other than that minor nit, at first glance this looks fine to me.

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] tilegx: Add tty serial support for TILE-Gx on-chip UART
  2013-08-12 18:43 ` Greg Kroah-Hartman
@ 2013-08-12 18:56     ` Chris Metcalf
  0 siblings, 0 replies; 6+ messages in thread
From: Chris Metcalf @ 2013-08-12 18:56 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, linux-serial, Jiri Slaby

On 8/12/2013 2:43 PM, Greg Kroah-Hartman wrote:
> On Mon, Aug 12, 2013 at 02:11:44PM -0400, Chris Metcalf wrote:
>> +/*
>> + * 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 */
>> +}
> For most of these "empty" callbacks, you shouldn't have to declare them
> at all, and all should work just fine.

I may be missing something, but it looks like drivers/tty/serial/serial_core.c
calls all of the "empty" callbacks unconditionally, so some definition
is required.

> Other than that minor nit, at first glance this looks fine to me.
>
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Thanks!

-- 
Chris Metcalf, Tilera Corp.
http://www.tilera.com


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] tilegx: Add tty serial support for TILE-Gx on-chip UART
@ 2013-08-12 18:56     ` Chris Metcalf
  0 siblings, 0 replies; 6+ messages in thread
From: Chris Metcalf @ 2013-08-12 18:56 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, linux-serial, Jiri Slaby

On 8/12/2013 2:43 PM, Greg Kroah-Hartman wrote:
> On Mon, Aug 12, 2013 at 02:11:44PM -0400, Chris Metcalf wrote:
>> +/*
>> + * 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 */
>> +}
> For most of these "empty" callbacks, you shouldn't have to declare them
> at all, and all should work just fine.

I may be missing something, but it looks like drivers/tty/serial/serial_core.c
calls all of the "empty" callbacks unconditionally, so some definition
is required.

> Other than that minor nit, at first glance this looks fine to me.
>
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Thanks!

-- 
Chris Metcalf, Tilera Corp.
http://www.tilera.com


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] tilegx: Add tty serial support for TILE-Gx on-chip UART
  2013-08-12 18:56     ` Chris Metcalf
  (?)
@ 2013-08-12 20:23     ` Greg Kroah-Hartman
  -1 siblings, 0 replies; 6+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-12 20:23 UTC (permalink / raw)
  To: Chris Metcalf; +Cc: linux-kernel, linux-serial, Jiri Slaby

On Mon, Aug 12, 2013 at 02:56:37PM -0400, Chris Metcalf wrote:
> On 8/12/2013 2:43 PM, Greg Kroah-Hartman wrote:
> > On Mon, Aug 12, 2013 at 02:11:44PM -0400, Chris Metcalf wrote:
> >> +/*
> >> + * 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 */
> >> +}
> > For most of these "empty" callbacks, you shouldn't have to declare them
> > at all, and all should work just fine.
> 
> I may be missing something, but it looks like drivers/tty/serial/serial_core.c
> calls all of the "empty" callbacks unconditionally, so some definition
> is required.

Ah, you are right, I was looking at the "raw" tty driver, not the uart
driver, my mistake.

greg k-h

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2013-08-12 20:23 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-12 18:11 [PATCH] tilegx: Add tty serial support for TILE-Gx on-chip UART Chris Metcalf
2013-08-12 18:11 ` Chris Metcalf
2013-08-12 18:43 ` Greg Kroah-Hartman
2013-08-12 18:56   ` Chris Metcalf
2013-08-12 18:56     ` Chris Metcalf
2013-08-12 20:23     ` Greg Kroah-Hartman

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.