mirror of
https://github.com/torvalds/linux.git
synced 2024-12-22 19:01:37 +00:00
4793f2ebff
Now that the SPDX tag is in all tty files, that identifies the license in a specific and legally-defined manner. So the extra GPL text wording can be removed as it is no longer needed at all. This is done on a quest to remove the 700+ different ways that files in the kernel describe the GPL license text. And there's unneeded stuff like the address (sometimes incorrect) for the FSF which is never needed. No copyright headers or other non-license-description text was removed. Cc: Jiri Slaby <jslaby@suse.com> Cc: Eric Anholt <eric@anholt.net> Cc: Stefan Wahren <stefan.wahren@i2se.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: Ray Jui <rjui@broadcom.com> Cc: Scott Branden <sbranden@broadcom.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: "James E.J. Bottomley" <jejb@parisc-linux.org> Cc: Helge Deller <deller@gmx.de> Cc: Joachim Eastwood <manabian@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: Pat Gefre <pfg@sgi.com> Cc: "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> Cc: Jason Wessel <jason.wessel@windriver.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Sylvain Lemieux <slemieux.tyco@gmail.com> Cc: Carlo Caione <carlo@caione.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Andy Gross <andy.gross@linaro.org> Cc: David Brown <david.brown@linaro.org> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Barry Song <baohua@kernel.org> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@st.com> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@tabi.org> Cc: Tony Prisk <linux@prisktech.co.nz> Cc: Michal Simek <michal.simek@xilinx.com> Cc: "Sören Brinkmann" <soren.brinkmann@xilinx.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
690 lines
15 KiB
C
690 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2013 Tilera Corporation. All Rights Reserved.
|
|
*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* 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 = irq_alloc_hwirq(-1);
|
|
if (!port->irq)
|
|
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:
|
|
irq_free_hwirq(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);
|
|
irq_free_hwirq(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,
|
|
.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");
|