cregit-Linux how code gets into the kernel

Release 4.11 drivers/tty/serial/tilegx.c

/*
 * 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); } }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf92100.00%1100.00%
Total92100.00%1100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf106100.00%1100.00%
Total106100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf53100.00%1100.00%
Total53100.00%1100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf186100.00%1100.00%
Total186100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf128100.00%1100.00%
Total128100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf93100.00%1100.00%
Total93100.00%1100.00%

/* * Set state of the modem control output lines. */
static void tilegx_set_mctrl(struct uart_port *port, u_int mctrl) { /* N/A */ }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf15100.00%1100.00%
Total15100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf18100.00%1100.00%
Total18100.00%1100.00%

/* * Stop transmitting. */
static void tilegx_stop_tx(struct uart_port *port) { /* N/A */ }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf12100.00%1100.00%
Total12100.00%1100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf203100.00%1100.00%
Total203100.00%1100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf93100.00%1100.00%
Total93100.00%1100.00%

/* * Control the transmission of a break signal. */
static void tilegx_break_ctl(struct uart_port *port, int break_state) { /* N/A */ }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf15100.00%1100.00%
Total15100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf29597.68%150.00%
Thomas Gleixner72.32%150.00%
Total302100.00%2100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf16799.40%150.00%
Thomas Gleixner10.60%150.00%
Total168100.00%2100.00%

/* * Flush the buffer. */
static void tilegx_flush_buffer(struct uart_port *port) { /* N/A */ }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf12100.00%1100.00%
Total12100.00%1100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf313100.00%1100.00%
Total313100.00%1100.00%

/* * Return string describing the specified port. */
static const char *tilegx_type(struct uart_port *port) { return port->type == PORT_TILEGX ? DRIVER_NAME_STRING : NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf24100.00%1100.00%
Total24100.00%1100.00%

/* * Release the resources being used by 'port'. */
static void tilegx_release_port(struct uart_port *port) { /* Nothing to release. */ }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf12100.00%1100.00%
Total12100.00%1100.00%

/* * Request the resources being used by 'port'. */
static int tilegx_request_port(struct uart_port *port) { /* Always present. */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf15100.00%1100.00%
Total15100.00%1100.00%

/* * Configure/autoconfigure the port. */
static void tilegx_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) port->type = PORT_TILEGX; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf26100.00%1100.00%
Total26100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf41100.00%1100.00%
Total41100.00%1100.00%

#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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf75100.00%1100.00%
Total75100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf56100.00%1100.00%
Total56100.00%1100.00%

#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); } }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf95100.00%1100.00%
Total95100.00%1100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf117100.00%1100.00%
Total117100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf56100.00%1100.00%
Total56100.00%1100.00%

module_init(tilegx_init); module_exit(tilegx_exit); MODULE_AUTHOR("Tilera Corporation"); MODULE_DESCRIPTION("TILEGx serial port driver"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf261399.69%150.00%
Thomas Gleixner80.31%150.00%
Total2621100.00%2100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.