Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
513
drivers/serial/21285.c
Normal file
513
drivers/serial/21285.c
Normal file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* linux/drivers/serial/21285.c
|
||||
*
|
||||
* Driver for the serial port on the 21285 StrongArm-110 core logic chip.
|
||||
*
|
||||
* Based on drivers/char/serial.c
|
||||
*
|
||||
* $Id: 21285.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/hardware/dec21285.h>
|
||||
#include <asm/hardware.h>
|
||||
|
||||
#define BAUD_BASE (mem_fclk_21285/64)
|
||||
|
||||
#define SERIAL_21285_NAME "ttyFB"
|
||||
#define SERIAL_21285_MAJOR 204
|
||||
#define SERIAL_21285_MINOR 4
|
||||
|
||||
#define RXSTAT_DUMMY_READ 0x80000000
|
||||
#define RXSTAT_FRAME (1 << 0)
|
||||
#define RXSTAT_PARITY (1 << 1)
|
||||
#define RXSTAT_OVERRUN (1 << 2)
|
||||
#define RXSTAT_ANYERR (RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN)
|
||||
|
||||
#define H_UBRLCR_BREAK (1 << 0)
|
||||
#define H_UBRLCR_PARENB (1 << 1)
|
||||
#define H_UBRLCR_PAREVN (1 << 2)
|
||||
#define H_UBRLCR_STOPB (1 << 3)
|
||||
#define H_UBRLCR_FIFO (1 << 4)
|
||||
|
||||
static const char serial21285_name[] = "Footbridge UART";
|
||||
|
||||
#define tx_enabled(port) ((port)->unused[0])
|
||||
#define rx_enabled(port) ((port)->unused[1])
|
||||
|
||||
/*
|
||||
* The documented expression for selecting the divisor is:
|
||||
* BAUD_BASE / baud - 1
|
||||
* However, typically BAUD_BASE is not divisible by baud, so
|
||||
* we want to select the divisor that gives us the minimum
|
||||
* error. Therefore, we want:
|
||||
* int(BAUD_BASE / baud - 0.5) ->
|
||||
* int(BAUD_BASE / baud - (baud >> 1) / baud) ->
|
||||
* int((BAUD_BASE - (baud >> 1)) / baud)
|
||||
*/
|
||||
|
||||
static void serial21285_stop_tx(struct uart_port *port)
|
||||
{
|
||||
if (tx_enabled(port)) {
|
||||
disable_irq(IRQ_CONTX);
|
||||
tx_enabled(port) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void serial21285_start_tx(struct uart_port *port)
|
||||
{
|
||||
if (!tx_enabled(port)) {
|
||||
enable_irq(IRQ_CONTX);
|
||||
tx_enabled(port) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void serial21285_stop_rx(struct uart_port *port)
|
||||
{
|
||||
if (rx_enabled(port)) {
|
||||
disable_irq(IRQ_CONRX);
|
||||
rx_enabled(port) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void serial21285_enable_ms(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static irqreturn_t serial21285_rx_chars(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
struct tty_struct *tty = port->info->tty;
|
||||
unsigned int status, ch, flag, rxs, max_count = 256;
|
||||
|
||||
status = *CSR_UARTFLG;
|
||||
while (!(status & 0x10) && max_count--) {
|
||||
ch = *CSR_UARTDR;
|
||||
flag = TTY_NORMAL;
|
||||
port->icount.rx++;
|
||||
|
||||
rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ;
|
||||
if (unlikely(rxs & RXSTAT_ANYERR)) {
|
||||
if (rxs & RXSTAT_PARITY)
|
||||
port->icount.parity++;
|
||||
else if (rxs & RXSTAT_FRAME)
|
||||
port->icount.frame++;
|
||||
if (rxs & RXSTAT_OVERRUN)
|
||||
port->icount.overrun++;
|
||||
|
||||
rxs &= port->read_status_mask;
|
||||
|
||||
if (rxs & RXSTAT_PARITY)
|
||||
flag = TTY_PARITY;
|
||||
else if (rxs & RXSTAT_FRAME)
|
||||
flag = TTY_FRAME;
|
||||
}
|
||||
|
||||
uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag);
|
||||
|
||||
status = *CSR_UARTFLG;
|
||||
}
|
||||
tty_flip_buffer_push(tty);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t serial21285_tx_chars(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
int count = 256;
|
||||
|
||||
if (port->x_char) {
|
||||
*CSR_UARTDR = port->x_char;
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
goto out;
|
||||
}
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
serial21285_stop_tx(port);
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
*CSR_UARTDR = xmit->buf[xmit->tail];
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (--count > 0 && !(*CSR_UARTFLG & 0x20));
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
serial21285_stop_tx(port);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int serial21285_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
/* no modem control lines */
|
||||
static unsigned int serial21285_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
|
||||
}
|
||||
|
||||
static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
static void serial21285_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int h_lcr;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
h_lcr = *CSR_H_UBRLCR;
|
||||
if (break_state)
|
||||
h_lcr |= H_UBRLCR_BREAK;
|
||||
else
|
||||
h_lcr &= ~H_UBRLCR_BREAK;
|
||||
*CSR_H_UBRLCR = h_lcr;
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int serial21285_startup(struct uart_port *port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
tx_enabled(port) = 1;
|
||||
rx_enabled(port) = 1;
|
||||
|
||||
ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0,
|
||||
serial21285_name, port);
|
||||
if (ret == 0) {
|
||||
ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0,
|
||||
serial21285_name, port);
|
||||
if (ret)
|
||||
free_irq(IRQ_CONRX, port);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serial21285_shutdown(struct uart_port *port)
|
||||
{
|
||||
free_irq(IRQ_CONTX, port);
|
||||
free_irq(IRQ_CONRX, port);
|
||||
}
|
||||
|
||||
static void
|
||||
serial21285_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot, h_lcr;
|
||||
|
||||
/*
|
||||
* We don't support modem control lines.
|
||||
*/
|
||||
termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
|
||||
termios->c_cflag |= CLOCAL;
|
||||
|
||||
/*
|
||||
* We don't support BREAK character recognition.
|
||||
*/
|
||||
termios->c_iflag &= ~(IGNBRK | BRKINT);
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
h_lcr = 0x00;
|
||||
break;
|
||||
case CS6:
|
||||
h_lcr = 0x20;
|
||||
break;
|
||||
case CS7:
|
||||
h_lcr = 0x40;
|
||||
break;
|
||||
default: /* CS8 */
|
||||
h_lcr = 0x60;
|
||||
break;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
h_lcr |= H_UBRLCR_STOPB;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
h_lcr |= H_UBRLCR_PARENB;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
h_lcr |= H_UBRLCR_PAREVN;
|
||||
}
|
||||
|
||||
if (port->fifosize)
|
||||
h_lcr |= H_UBRLCR_FIFO;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/*
|
||||
* Which character status flags are we interested in?
|
||||
*/
|
||||
port->read_status_mask = RXSTAT_OVERRUN;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
|
||||
|
||||
/*
|
||||
* Which character status flags should we ignore?
|
||||
*/
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
|
||||
if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= RXSTAT_OVERRUN;
|
||||
|
||||
/*
|
||||
* Ignore all characters if CREAD is not set.
|
||||
*/
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |= RXSTAT_DUMMY_READ;
|
||||
|
||||
quot -= 1;
|
||||
|
||||
*CSR_UARTCON = 0;
|
||||
*CSR_L_UBRLCR = quot & 0xff;
|
||||
*CSR_M_UBRLCR = (quot >> 8) & 0x0f;
|
||||
*CSR_H_UBRLCR = h_lcr;
|
||||
*CSR_UARTCON = 1;
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *serial21285_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_21285 ? "DC21285" : NULL;
|
||||
}
|
||||
|
||||
static void serial21285_release_port(struct uart_port *port)
|
||||
{
|
||||
release_mem_region(port->mapbase, 32);
|
||||
}
|
||||
|
||||
static int serial21285_request_port(struct uart_port *port)
|
||||
{
|
||||
return request_mem_region(port->mapbase, 32, serial21285_name)
|
||||
!= NULL ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
static void serial21285_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0)
|
||||
port->type = PORT_21285;
|
||||
}
|
||||
|
||||
/*
|
||||
* verify the new serial_struct (for TIOCSSERIAL).
|
||||
*/
|
||||
static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
int ret = 0;
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)
|
||||
ret = -EINVAL;
|
||||
if (ser->irq != NO_IRQ)
|
||||
ret = -EINVAL;
|
||||
if (ser->baud_base != port->uartclk / 16)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops serial21285_ops = {
|
||||
.tx_empty = serial21285_tx_empty,
|
||||
.get_mctrl = serial21285_get_mctrl,
|
||||
.set_mctrl = serial21285_set_mctrl,
|
||||
.stop_tx = serial21285_stop_tx,
|
||||
.start_tx = serial21285_start_tx,
|
||||
.stop_rx = serial21285_stop_rx,
|
||||
.enable_ms = serial21285_enable_ms,
|
||||
.break_ctl = serial21285_break_ctl,
|
||||
.startup = serial21285_startup,
|
||||
.shutdown = serial21285_shutdown,
|
||||
.set_termios = serial21285_set_termios,
|
||||
.type = serial21285_type,
|
||||
.release_port = serial21285_release_port,
|
||||
.request_port = serial21285_request_port,
|
||||
.config_port = serial21285_config_port,
|
||||
.verify_port = serial21285_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_port serial21285_port = {
|
||||
.mapbase = 0x42000160,
|
||||
.iotype = UPIO_MEM,
|
||||
.irq = NO_IRQ,
|
||||
.fifosize = 16,
|
||||
.ops = &serial21285_ops,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
};
|
||||
|
||||
static void serial21285_setup_ports(void)
|
||||
{
|
||||
serial21285_port.uartclk = mem_fclk_21285 / 4;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_21285_CONSOLE
|
||||
static void serial21285_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (*CSR_UARTFLG & 0x20)
|
||||
barrier();
|
||||
*CSR_UARTDR = ch;
|
||||
}
|
||||
|
||||
static void
|
||||
serial21285_console_write(struct console *co, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
uart_console_write(&serial21285_port, s, count, serial21285_console_putchar);
|
||||
}
|
||||
|
||||
static void __init
|
||||
serial21285_get_options(struct uart_port *port, int *baud,
|
||||
int *parity, int *bits)
|
||||
{
|
||||
if (*CSR_UARTCON == 1) {
|
||||
unsigned int tmp;
|
||||
|
||||
tmp = *CSR_H_UBRLCR;
|
||||
switch (tmp & 0x60) {
|
||||
case 0x00:
|
||||
*bits = 5;
|
||||
break;
|
||||
case 0x20:
|
||||
*bits = 6;
|
||||
break;
|
||||
case 0x40:
|
||||
*bits = 7;
|
||||
break;
|
||||
default:
|
||||
case 0x60:
|
||||
*bits = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp & H_UBRLCR_PARENB) {
|
||||
*parity = 'o';
|
||||
if (tmp & H_UBRLCR_PAREVN)
|
||||
*parity = 'e';
|
||||
}
|
||||
|
||||
tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8);
|
||||
|
||||
*baud = port->uartclk / (16 * (tmp + 1));
|
||||
}
|
||||
}
|
||||
|
||||
static int __init serial21285_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port = &serial21285_port;
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (machine_is_personal_server())
|
||||
baud = 57600;
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
serial21285_get_options(port, &baud, &parity, &bits);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver serial21285_reg;
|
||||
|
||||
static struct console serial21285_console =
|
||||
{
|
||||
.name = SERIAL_21285_NAME,
|
||||
.write = serial21285_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = serial21285_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &serial21285_reg,
|
||||
};
|
||||
|
||||
static int __init rs285_console_init(void)
|
||||
{
|
||||
serial21285_setup_ports();
|
||||
register_console(&serial21285_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(rs285_console_init);
|
||||
|
||||
#define SERIAL_21285_CONSOLE &serial21285_console
|
||||
#else
|
||||
#define SERIAL_21285_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver serial21285_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "ttyFB",
|
||||
.dev_name = "ttyFB",
|
||||
.major = SERIAL_21285_MAJOR,
|
||||
.minor = SERIAL_21285_MINOR,
|
||||
.nr = 1,
|
||||
.cons = SERIAL_21285_CONSOLE,
|
||||
};
|
||||
|
||||
static int __init serial21285_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "Serial: 21285 driver $Revision: 1.1.1.1 $\n");
|
||||
|
||||
serial21285_setup_ports();
|
||||
|
||||
ret = uart_register_driver(&serial21285_reg);
|
||||
if (ret == 0)
|
||||
uart_add_one_port(&serial21285_reg, &serial21285_port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit serial21285_exit(void)
|
||||
{
|
||||
uart_remove_one_port(&serial21285_reg, &serial21285_port);
|
||||
uart_unregister_driver(&serial21285_reg);
|
||||
}
|
||||
|
||||
module_init(serial21285_init);
|
||||
module_exit(serial21285_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver $Revision: 1.1.1.1 $");
|
||||
MODULE_ALIAS_CHARDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);
|
||||
1576
drivers/serial/68328serial.c
Normal file
1576
drivers/serial/68328serial.c
Normal file
File diff suppressed because it is too large
Load Diff
193
drivers/serial/68328serial.h
Normal file
193
drivers/serial/68328serial.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/* 68328serial.h: Definitions for the mc68328 serial driver.
|
||||
*
|
||||
* Copyright (C) 1995 David S. Miller <davem@caip.rutgers.edu>
|
||||
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>
|
||||
* Copyright (C) 1998, 1999 D. Jeff Dionne <jeff@uclinux.org>
|
||||
* Copyright (C) 1999 Vladimir Gurevich <vgurevic@cisco.com>
|
||||
*
|
||||
* VZ Support/Fixes Evan Stawnyczy <e@lineo.ca>
|
||||
*/
|
||||
|
||||
#ifndef _MC683XX_SERIAL_H
|
||||
#define _MC683XX_SERIAL_H
|
||||
|
||||
|
||||
struct serial_struct {
|
||||
int type;
|
||||
int line;
|
||||
int port;
|
||||
int irq;
|
||||
int flags;
|
||||
int xmit_fifo_size;
|
||||
int custom_divisor;
|
||||
int baud_base;
|
||||
unsigned short close_delay;
|
||||
char reserved_char[2];
|
||||
int hub6; /* FIXME: We don't have AT&T Hub6 boards! */
|
||||
unsigned short closing_wait; /* time to wait before closing */
|
||||
unsigned short closing_wait2; /* no longer used... */
|
||||
int reserved[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* For the close wait times, 0 means wait forever for serial port to
|
||||
* flush its output. 65535 means don't wait at all.
|
||||
*/
|
||||
#define S_CLOSING_WAIT_INF 0
|
||||
#define S_CLOSING_WAIT_NONE 65535
|
||||
|
||||
/*
|
||||
* Definitions for S_struct (and serial_struct) flags field
|
||||
*/
|
||||
#define S_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
|
||||
on the callout port */
|
||||
#define S_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
|
||||
#define S_SAK 0x0004 /* Secure Attention Key (Orange book) */
|
||||
#define S_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
|
||||
|
||||
#define S_SPD_MASK 0x0030
|
||||
#define S_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
|
||||
|
||||
#define S_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
|
||||
#define S_SPD_CUST 0x0030 /* Use user-specified divisor */
|
||||
|
||||
#define S_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
|
||||
#define S_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
|
||||
#define S_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
|
||||
#define S_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
|
||||
#define S_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
|
||||
|
||||
#define S_FLAGS 0x0FFF /* Possible legal S flags */
|
||||
#define S_USR_MASK 0x0430 /* Legal flags that non-privileged
|
||||
* users can set or reset */
|
||||
|
||||
/* Internal flags used only by kernel/chr_drv/serial.c */
|
||||
#define S_INITIALIZED 0x80000000 /* Serial port was initialized */
|
||||
#define S_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
|
||||
#define S_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
|
||||
#define S_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
|
||||
#define S_CLOSING 0x08000000 /* Serial port is closing */
|
||||
#define S_CTS_FLOW 0x04000000 /* Do CTS flow control */
|
||||
#define S_CHECK_CD 0x02000000 /* i.e., CLOCAL */
|
||||
|
||||
/* Software state per channel */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/*
|
||||
* I believe this is the optimal setting that reduces the number of interrupts.
|
||||
* At high speeds the output might become a little "bursted" (use USTCNT_TXHE
|
||||
* if that bothers you), but in most cases it will not, since we try to
|
||||
* transmit characters every time rs_interrupt is called. Thus, quite often
|
||||
* you'll see that a receive interrupt occures before the transmit one.
|
||||
* -- Vladimir Gurevich
|
||||
*/
|
||||
#define USTCNT_TX_INTR_MASK (USTCNT_TXEE)
|
||||
|
||||
/*
|
||||
* 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special
|
||||
* "Old data interrupt" which occures whenever the data stay in the FIFO
|
||||
* longer than 30 bits time. This allows us to use FIFO without compromising
|
||||
* latency. '328 does not have this feature and without the real 328-based
|
||||
* board I would assume that RXRE is the safest setting.
|
||||
*
|
||||
* For EZ328 I use RXHE (Half empty) interrupt to reduce the number of
|
||||
* interrupts. RXFE (receive queue full) causes the system to lose data
|
||||
* at least at 115200 baud
|
||||
*
|
||||
* If your board is busy doing other stuff, you might consider to use
|
||||
* RXRE (data ready intrrupt) instead.
|
||||
*
|
||||
* The other option is to make these INTR masks run-time configurable, so
|
||||
* that people can dynamically adapt them according to the current usage.
|
||||
* -- Vladimir Gurevich
|
||||
*/
|
||||
|
||||
/* (es) */
|
||||
#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328)
|
||||
#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN)
|
||||
#elif defined(CONFIG_M68328)
|
||||
#define USTCNT_RX_INTR_MASK (USTCNT_RXRE)
|
||||
#else
|
||||
#error Please, define the Rx interrupt events for your CPU
|
||||
#endif
|
||||
/* (/es) */
|
||||
|
||||
/*
|
||||
* This is our internal structure for each serial port's state.
|
||||
*
|
||||
* Many fields are paralleled by the structure used by the serial_struct
|
||||
* structure.
|
||||
*
|
||||
* For definitions of the flags field, see tty.h
|
||||
*/
|
||||
|
||||
struct m68k_serial {
|
||||
char soft_carrier; /* Use soft carrier on this channel */
|
||||
char break_abort; /* Is serial console in, so process brk/abrt */
|
||||
char is_cons; /* Is this our console. */
|
||||
|
||||
/* We need to know the current clock divisor
|
||||
* to read the bps rate the chip has currently
|
||||
* loaded.
|
||||
*/
|
||||
unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
|
||||
int baud;
|
||||
int magic;
|
||||
int baud_base;
|
||||
int port;
|
||||
int irq;
|
||||
int flags; /* defined in tty.h */
|
||||
int type; /* UART type */
|
||||
struct tty_struct *tty;
|
||||
int read_status_mask;
|
||||
int ignore_status_mask;
|
||||
int timeout;
|
||||
int xmit_fifo_size;
|
||||
int custom_divisor;
|
||||
int x_char; /* xon/xoff character */
|
||||
int close_delay;
|
||||
unsigned short closing_wait;
|
||||
unsigned short closing_wait2;
|
||||
unsigned long event;
|
||||
unsigned long last_active;
|
||||
int line;
|
||||
int count; /* # of fd on device */
|
||||
int blocked_open; /* # of blocked opens */
|
||||
unsigned char *xmit_buf;
|
||||
int xmit_head;
|
||||
int xmit_tail;
|
||||
int xmit_cnt;
|
||||
struct work_struct tqueue;
|
||||
struct work_struct tqueue_hangup;
|
||||
wait_queue_head_t open_wait;
|
||||
wait_queue_head_t close_wait;
|
||||
};
|
||||
|
||||
|
||||
#define SERIAL_MAGIC 0x5301
|
||||
|
||||
/*
|
||||
* The size of the serial xmit buffer is 1 page, or 4096 bytes
|
||||
*/
|
||||
#define SERIAL_XMIT_SIZE 4096
|
||||
|
||||
/*
|
||||
* Events are used to schedule things to happen at timer-interrupt
|
||||
* time, instead of at rs interrupt time.
|
||||
*/
|
||||
#define RS_EVENT_WRITE_WAKEUP 0
|
||||
|
||||
/*
|
||||
* Define the number of ports supported and their irqs.
|
||||
*/
|
||||
#ifndef CONFIG_68328_SERIAL_UART2
|
||||
#define NR_PORTS 1
|
||||
#define UART_IRQ_DEFNS {UART_IRQ_NUM}
|
||||
#else
|
||||
#define NR_PORTS 2
|
||||
#define UART_IRQ_DEFNS {UART1_IRQ_NUM, UART2_IRQ_NUM}
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* !(_MC683XX_SERIAL_H) */
|
||||
2995
drivers/serial/68360serial.c
Normal file
2995
drivers/serial/68360serial.c
Normal file
File diff suppressed because it is too large
Load Diff
2817
drivers/serial/8250.c
Normal file
2817
drivers/serial/8250.c
Normal file
File diff suppressed because it is too large
Load Diff
80
drivers/serial/8250.h
Normal file
80
drivers/serial/8250.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* linux/drivers/char/8250.h
|
||||
*
|
||||
* Driver for 8250/16550-type serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* Copyright (C) 2001 Russell King.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* $Id: 8250.h,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
struct old_serial_port {
|
||||
unsigned int uart;
|
||||
unsigned int baud_base;
|
||||
unsigned int port;
|
||||
unsigned int irq;
|
||||
unsigned int flags;
|
||||
unsigned char hub6;
|
||||
unsigned char io_type;
|
||||
unsigned char *iomem_base;
|
||||
unsigned short iomem_reg_shift;
|
||||
};
|
||||
|
||||
/*
|
||||
* This replaces serial_uart_config in include/linux/serial.h
|
||||
*/
|
||||
struct serial8250_config {
|
||||
const char *name;
|
||||
unsigned short fifo_size;
|
||||
unsigned short tx_loadsz;
|
||||
unsigned char fcr;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */
|
||||
#define UART_CAP_EFR (1 << 9) /* UART has EFR */
|
||||
#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */
|
||||
#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
|
||||
#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
|
||||
|
||||
#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */
|
||||
#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */
|
||||
#define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */
|
||||
|
||||
#define PROBE_RSA (1 << 0)
|
||||
#define PROBE_ANY (~0)
|
||||
|
||||
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_SHARE_IRQ
|
||||
#define SERIAL8250_SHARE_IRQS 1
|
||||
#else
|
||||
#define SERIAL8250_SHARE_IRQS 0
|
||||
#endif
|
||||
|
||||
#if defined(__alpha__) && !defined(CONFIG_PCI)
|
||||
/*
|
||||
* Digital did something really horribly wrong with the OUT1 and OUT2
|
||||
* lines on at least some ALPHA's. The failure mode is that if either
|
||||
* is cleared, the machine locks up with endless interrupts.
|
||||
*/
|
||||
#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1)
|
||||
#elif defined(CONFIG_SBC8560)
|
||||
/*
|
||||
* WindRiver did something similarly broken on their SBC8560 board. The
|
||||
* UART tristates its IRQ output while OUT2 is clear, but they pulled
|
||||
* the interrupt line _up_ instead of down, so if we register the IRQ
|
||||
* while the UART is in that state, we die in an IRQ storm. */
|
||||
#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2)
|
||||
#else
|
||||
#define ALPHA_KLUDGE_MCR 0
|
||||
#endif
|
||||
47
drivers/serial/8250_accent.c
Normal file
47
drivers/serial/8250_accent.c
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* linux/drivers/serial/8250_accent.c
|
||||
*
|
||||
* Copyright (C) 2005 Russell King.
|
||||
* Data taken from include/asm-i386/serial.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
#define PORT(_base,_irq) \
|
||||
{ \
|
||||
.iobase = _base, \
|
||||
.irq = _irq, \
|
||||
.uartclk = 1843200, \
|
||||
.iotype = UPIO_PORT, \
|
||||
.flags = UPF_BOOT_AUTOCONF, \
|
||||
}
|
||||
|
||||
static struct plat_serial8250_port accent_data[] = {
|
||||
PORT(0x330, 4),
|
||||
PORT(0x338, 4),
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_device accent_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_ACCENT,
|
||||
.dev = {
|
||||
.platform_data = accent_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init accent_init(void)
|
||||
{
|
||||
return platform_device_register(&accent_device);
|
||||
}
|
||||
|
||||
module_init(accent_init);
|
||||
|
||||
MODULE_AUTHOR("Russell King");
|
||||
MODULE_DESCRIPTION("8250 serial probe module for Accent Async cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
142
drivers/serial/8250_acorn.c
Normal file
142
drivers/serial/8250_acorn.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* linux/drivers/serial/acorn.c
|
||||
*
|
||||
* Copyright (C) 1996-2003 Russell King.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/ecard.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
#define MAX_PORTS 3
|
||||
|
||||
struct serial_card_type {
|
||||
unsigned int num_ports;
|
||||
unsigned int uartclk;
|
||||
unsigned int type;
|
||||
unsigned int offset[MAX_PORTS];
|
||||
};
|
||||
|
||||
struct serial_card_info {
|
||||
unsigned int num_ports;
|
||||
int ports[MAX_PORTS];
|
||||
void __iomem *vaddr;
|
||||
};
|
||||
|
||||
static int __devinit
|
||||
serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
|
||||
{
|
||||
struct serial_card_info *info;
|
||||
struct serial_card_type *type = id->data;
|
||||
struct uart_port port;
|
||||
unsigned long bus_addr;
|
||||
unsigned int i;
|
||||
|
||||
info = kzalloc(sizeof(struct serial_card_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->num_ports = type->num_ports;
|
||||
|
||||
bus_addr = ecard_resource_start(ec, type->type);
|
||||
info->vaddr = ioremap(bus_addr, ecard_resource_len(ec, type->type));
|
||||
if (!info->vaddr) {
|
||||
kfree(info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ecard_set_drvdata(ec, info);
|
||||
|
||||
memset(&port, 0, sizeof(struct uart_port));
|
||||
port.irq = ec->irq;
|
||||
port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
|
||||
port.uartclk = type->uartclk;
|
||||
port.iotype = UPIO_MEM;
|
||||
port.regshift = 2;
|
||||
port.dev = &ec->dev;
|
||||
|
||||
for (i = 0; i < info->num_ports; i ++) {
|
||||
port.membase = info->vaddr + type->offset[i];
|
||||
port.mapbase = bus_addr + type->offset[i];
|
||||
|
||||
info->ports[i] = serial8250_register_port(&port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit serial_card_remove(struct expansion_card *ec)
|
||||
{
|
||||
struct serial_card_info *info = ecard_get_drvdata(ec);
|
||||
int i;
|
||||
|
||||
ecard_set_drvdata(ec, NULL);
|
||||
|
||||
for (i = 0; i < info->num_ports; i++)
|
||||
if (info->ports[i] > 0)
|
||||
serial8250_unregister_port(info->ports[i]);
|
||||
|
||||
iounmap(info->vaddr);
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
static struct serial_card_type atomwide_type = {
|
||||
.num_ports = 3,
|
||||
.uartclk = 7372800,
|
||||
.type = ECARD_RES_IOCSLOW,
|
||||
.offset = { 0x2800, 0x2400, 0x2000 },
|
||||
};
|
||||
|
||||
static struct serial_card_type serport_type = {
|
||||
.num_ports = 2,
|
||||
.uartclk = 3686400,
|
||||
.type = ECARD_RES_IOCSLOW,
|
||||
.offset = { 0x2000, 0x2020 },
|
||||
};
|
||||
|
||||
static const struct ecard_id serial_cids[] = {
|
||||
{ MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, &atomwide_type },
|
||||
{ MANU_SERPORT, PROD_SERPORT_DSPORT, &serport_type },
|
||||
{ 0xffff, 0xffff }
|
||||
};
|
||||
|
||||
static struct ecard_driver serial_card_driver = {
|
||||
.probe = serial_card_probe,
|
||||
.remove = __devexit_p(serial_card_remove),
|
||||
.id_table = serial_cids,
|
||||
.drv = {
|
||||
.name = "8250_acorn",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init serial_card_init(void)
|
||||
{
|
||||
return ecard_register_driver(&serial_card_driver);
|
||||
}
|
||||
|
||||
static void __exit serial_card_exit(void)
|
||||
{
|
||||
ecard_remove_driver(&serial_card_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Russell King");
|
||||
MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(serial_card_init);
|
||||
module_exit(serial_card_exit);
|
||||
100
drivers/serial/8250_au1x00.c
Normal file
100
drivers/serial/8250_au1x00.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Serial Device Initialisation for Au1x00
|
||||
*
|
||||
* (C) Copyright Embedded Alley Solutions, Inc 2005
|
||||
* Author: Pantelis Antoniou <pantelis@embeddedalley.com>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
#define PORT(_base, _irq) \
|
||||
{ \
|
||||
.iobase = _base, \
|
||||
.membase = (void __iomem *)_base,\
|
||||
.mapbase = CPHYSADDR(_base), \
|
||||
.irq = _irq, \
|
||||
.uartclk = 0, /* filled */ \
|
||||
.regshift = 2, \
|
||||
.iotype = UPIO_AU, \
|
||||
.flags = UPF_SKIP_TEST \
|
||||
}
|
||||
|
||||
static struct plat_serial8250_port au1x00_data[] = {
|
||||
#if defined(CONFIG_SOC_AU1000)
|
||||
PORT(UART0_ADDR, AU1000_UART0_INT),
|
||||
PORT(UART1_ADDR, AU1000_UART1_INT),
|
||||
PORT(UART2_ADDR, AU1000_UART2_INT),
|
||||
PORT(UART3_ADDR, AU1000_UART3_INT),
|
||||
#elif defined(CONFIG_SOC_AU1500)
|
||||
PORT(UART0_ADDR, AU1500_UART0_INT),
|
||||
PORT(UART3_ADDR, AU1500_UART3_INT),
|
||||
#elif defined(CONFIG_SOC_AU1100)
|
||||
PORT(UART0_ADDR, AU1100_UART0_INT),
|
||||
PORT(UART1_ADDR, AU1100_UART1_INT),
|
||||
/* The internal UART2 does not exist on the AU1100 processor. */
|
||||
PORT(UART3_ADDR, AU1100_UART3_INT),
|
||||
#elif defined(CONFIG_SOC_AU1550)
|
||||
PORT(UART0_ADDR, AU1550_UART0_INT),
|
||||
PORT(UART1_ADDR, AU1550_UART1_INT),
|
||||
PORT(UART3_ADDR, AU1550_UART3_INT),
|
||||
#elif defined(CONFIG_SOC_AU1200)
|
||||
PORT(UART0_ADDR, AU1200_UART0_INT),
|
||||
PORT(UART1_ADDR, AU1200_UART1_INT),
|
||||
#endif
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_device au1x00_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_AU1X00,
|
||||
.dev = {
|
||||
.platform_data = au1x00_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init au1x00_init(void)
|
||||
{
|
||||
int i;
|
||||
unsigned int uartclk;
|
||||
|
||||
/* get uart clock */
|
||||
uartclk = get_au1x00_uart_baud_base() * 16;
|
||||
|
||||
/* fill up uartclk */
|
||||
for (i = 0; au1x00_data[i].flags ; i++)
|
||||
au1x00_data[i].uartclk = uartclk;
|
||||
|
||||
return platform_device_register(&au1x00_device);
|
||||
}
|
||||
|
||||
/* XXX: Yes, I know this doesn't yet work. */
|
||||
static void __exit au1x00_exit(void)
|
||||
{
|
||||
platform_device_unregister(&au1x00_device);
|
||||
}
|
||||
|
||||
module_init(au1x00_init);
|
||||
module_exit(au1x00_exit);
|
||||
|
||||
MODULE_AUTHOR("Pantelis Antoniou <pantelis@embeddedalley.com>");
|
||||
MODULE_DESCRIPTION("8250 serial probe module for Au1x000 cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
61
drivers/serial/8250_boca.c
Normal file
61
drivers/serial/8250_boca.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* linux/drivers/serial/8250_boca.c
|
||||
*
|
||||
* Copyright (C) 2005 Russell King.
|
||||
* Data taken from include/asm-i386/serial.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
#define PORT(_base,_irq) \
|
||||
{ \
|
||||
.iobase = _base, \
|
||||
.irq = _irq, \
|
||||
.uartclk = 1843200, \
|
||||
.iotype = UPIO_PORT, \
|
||||
.flags = UPF_BOOT_AUTOCONF, \
|
||||
}
|
||||
|
||||
static struct plat_serial8250_port boca_data[] = {
|
||||
PORT(0x100, 12),
|
||||
PORT(0x108, 12),
|
||||
PORT(0x110, 12),
|
||||
PORT(0x118, 12),
|
||||
PORT(0x120, 12),
|
||||
PORT(0x128, 12),
|
||||
PORT(0x130, 12),
|
||||
PORT(0x138, 12),
|
||||
PORT(0x140, 12),
|
||||
PORT(0x148, 12),
|
||||
PORT(0x150, 12),
|
||||
PORT(0x158, 12),
|
||||
PORT(0x160, 12),
|
||||
PORT(0x168, 12),
|
||||
PORT(0x170, 12),
|
||||
PORT(0x178, 12),
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_device boca_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_BOCA,
|
||||
.dev = {
|
||||
.platform_data = boca_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init boca_init(void)
|
||||
{
|
||||
return platform_device_register(&boca_device);
|
||||
}
|
||||
|
||||
module_init(boca_init);
|
||||
|
||||
MODULE_AUTHOR("Russell King");
|
||||
MODULE_DESCRIPTION("8250 serial probe module for Boca cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
250
drivers/serial/8250_early.c
Normal file
250
drivers/serial/8250_early.c
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Early serial console for 8250/16550 devices
|
||||
*
|
||||
* (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
|
||||
* Bjorn Helgaas <bjorn.helgaas@hp.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Based on the 8250.c serial driver, Copyright (C) 2001 Russell King,
|
||||
* and on early_printk.c by Andi Kleen.
|
||||
*
|
||||
* This is for use before the serial driver has initialized, in
|
||||
* particular, before the UARTs have been discovered and named.
|
||||
* Instead of specifying the console device as, e.g., "ttyS0",
|
||||
* we locate the device directly by its MMIO or I/O port address.
|
||||
*
|
||||
* The user can specify the device directly, e.g.,
|
||||
* console=uart,io,0x3f8,9600n8
|
||||
* console=uart,mmio,0xff5e0000,115200n8
|
||||
* or platform code can call early_uart_console_init() to set
|
||||
* the early UART device.
|
||||
*
|
||||
* After the normal serial driver starts, we try to locate the
|
||||
* matching ttyS device and start a console there.
|
||||
*/
|
||||
|
||||
#include <linux/tty.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/serial.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/serial.h>
|
||||
|
||||
struct early_uart_device {
|
||||
struct uart_port port;
|
||||
char options[16]; /* e.g., 115200n8 */
|
||||
unsigned int baud;
|
||||
};
|
||||
|
||||
static struct early_uart_device early_device __initdata;
|
||||
static int early_uart_registered __initdata;
|
||||
|
||||
static unsigned int __init serial_in(struct uart_port *port, int offset)
|
||||
{
|
||||
if (port->iotype == UPIO_MEM)
|
||||
return readb(port->membase + offset);
|
||||
else
|
||||
return inb(port->iobase + offset);
|
||||
}
|
||||
|
||||
static void __init serial_out(struct uart_port *port, int offset, int value)
|
||||
{
|
||||
if (port->iotype == UPIO_MEM)
|
||||
writeb(value, port->membase + offset);
|
||||
else
|
||||
outb(value, port->iobase + offset);
|
||||
}
|
||||
|
||||
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
|
||||
|
||||
static void __init wait_for_xmitr(struct uart_port *port)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
for (;;) {
|
||||
status = serial_in(port, UART_LSR);
|
||||
if ((status & BOTH_EMPTY) == BOTH_EMPTY)
|
||||
return;
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
static void __init putc(struct uart_port *port, int c)
|
||||
{
|
||||
wait_for_xmitr(port);
|
||||
serial_out(port, UART_TX, c);
|
||||
}
|
||||
|
||||
static void __init early_uart_write(struct console *console, const char *s, unsigned int count)
|
||||
{
|
||||
struct uart_port *port = &early_device.port;
|
||||
unsigned int ier;
|
||||
|
||||
/* Save the IER and disable interrupts */
|
||||
ier = serial_in(port, UART_IER);
|
||||
serial_out(port, UART_IER, 0);
|
||||
|
||||
uart_console_write(port, s, count, putc);
|
||||
|
||||
/* Wait for transmitter to become empty and restore the IER */
|
||||
wait_for_xmitr(port);
|
||||
serial_out(port, UART_IER, ier);
|
||||
}
|
||||
|
||||
static unsigned int __init probe_baud(struct uart_port *port)
|
||||
{
|
||||
unsigned char lcr, dll, dlm;
|
||||
unsigned int quot;
|
||||
|
||||
lcr = serial_in(port, UART_LCR);
|
||||
serial_out(port, UART_LCR, lcr | UART_LCR_DLAB);
|
||||
dll = serial_in(port, UART_DLL);
|
||||
dlm = serial_in(port, UART_DLM);
|
||||
serial_out(port, UART_LCR, lcr);
|
||||
|
||||
quot = (dlm << 8) | dll;
|
||||
return (port->uartclk / 16) / quot;
|
||||
}
|
||||
|
||||
static void __init init_port(struct early_uart_device *device)
|
||||
{
|
||||
struct uart_port *port = &device->port;
|
||||
unsigned int divisor;
|
||||
unsigned char c;
|
||||
|
||||
serial_out(port, UART_LCR, 0x3); /* 8n1 */
|
||||
serial_out(port, UART_IER, 0); /* no interrupt */
|
||||
serial_out(port, UART_FCR, 0); /* no fifo */
|
||||
serial_out(port, UART_MCR, 0x3); /* DTR + RTS */
|
||||
|
||||
divisor = port->uartclk / (16 * device->baud);
|
||||
c = serial_in(port, UART_LCR);
|
||||
serial_out(port, UART_LCR, c | UART_LCR_DLAB);
|
||||
serial_out(port, UART_DLL, divisor & 0xff);
|
||||
serial_out(port, UART_DLM, (divisor >> 8) & 0xff);
|
||||
serial_out(port, UART_LCR, c & ~UART_LCR_DLAB);
|
||||
}
|
||||
|
||||
static int __init parse_options(struct early_uart_device *device, char *options)
|
||||
{
|
||||
struct uart_port *port = &device->port;
|
||||
int mapsize = 64;
|
||||
int mmio, length;
|
||||
|
||||
if (!options)
|
||||
return -ENODEV;
|
||||
|
||||
port->uartclk = BASE_BAUD * 16;
|
||||
if (!strncmp(options, "mmio,", 5)) {
|
||||
port->iotype = UPIO_MEM;
|
||||
port->mapbase = simple_strtoul(options + 5, &options, 0);
|
||||
port->membase = ioremap(port->mapbase, mapsize);
|
||||
if (!port->membase) {
|
||||
printk(KERN_ERR "%s: Couldn't ioremap 0x%lx\n",
|
||||
__FUNCTION__, port->mapbase);
|
||||
return -ENOMEM;
|
||||
}
|
||||
mmio = 1;
|
||||
} else if (!strncmp(options, "io,", 3)) {
|
||||
port->iotype = UPIO_PORT;
|
||||
port->iobase = simple_strtoul(options + 3, &options, 0);
|
||||
mmio = 0;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
if ((options = strchr(options, ','))) {
|
||||
options++;
|
||||
device->baud = simple_strtoul(options, NULL, 0);
|
||||
length = min(strcspn(options, " "), sizeof(device->options));
|
||||
strncpy(device->options, options, length);
|
||||
} else {
|
||||
device->baud = probe_baud(port);
|
||||
snprintf(device->options, sizeof(device->options), "%u",
|
||||
device->baud);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Early serial console at %s 0x%lx (options '%s')\n",
|
||||
mmio ? "MMIO" : "I/O port",
|
||||
mmio ? port->mapbase : (unsigned long) port->iobase,
|
||||
device->options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init early_uart_setup(struct console *console, char *options)
|
||||
{
|
||||
struct early_uart_device *device = &early_device;
|
||||
int err;
|
||||
|
||||
if (device->port.membase || device->port.iobase)
|
||||
return 0;
|
||||
|
||||
if ((err = parse_options(device, options)) < 0)
|
||||
return err;
|
||||
|
||||
init_port(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct console early_uart_console __initdata = {
|
||||
.name = "uart",
|
||||
.write = early_uart_write,
|
||||
.setup = early_uart_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
static int __init early_uart_console_init(void)
|
||||
{
|
||||
if (!early_uart_registered) {
|
||||
register_console(&early_uart_console);
|
||||
early_uart_registered = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
console_initcall(early_uart_console_init);
|
||||
|
||||
int __init early_serial_console_init(char *cmdline)
|
||||
{
|
||||
char *options;
|
||||
int err;
|
||||
|
||||
options = strstr(cmdline, "console=uart,");
|
||||
if (!options)
|
||||
return -ENODEV;
|
||||
|
||||
options = strchr(cmdline, ',') + 1;
|
||||
if ((err = early_uart_setup(NULL, options)) < 0)
|
||||
return err;
|
||||
return early_uart_console_init();
|
||||
}
|
||||
|
||||
static int __init early_uart_console_switch(void)
|
||||
{
|
||||
struct early_uart_device *device = &early_device;
|
||||
struct uart_port *port = &device->port;
|
||||
int mmio, line;
|
||||
|
||||
if (!(early_uart_console.flags & CON_ENABLED))
|
||||
return 0;
|
||||
|
||||
/* Try to start the normal driver on a matching line. */
|
||||
mmio = (port->iotype == UPIO_MEM);
|
||||
line = serial8250_start_console(port, device->options);
|
||||
if (line < 0)
|
||||
printk("No ttyS device at %s 0x%lx for console\n",
|
||||
mmio ? "MMIO" : "I/O port",
|
||||
mmio ? port->mapbase :
|
||||
(unsigned long) port->iobase);
|
||||
|
||||
unregister_console(&early_uart_console);
|
||||
if (mmio)
|
||||
iounmap(port->membase);
|
||||
|
||||
return 0;
|
||||
}
|
||||
late_initcall(early_uart_console_switch);
|
||||
52
drivers/serial/8250_exar_st16c554.c
Normal file
52
drivers/serial/8250_exar_st16c554.c
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* linux/drivers/serial/8250_exar.c
|
||||
*
|
||||
* Written by Paul B Schroeder < pschroeder "at" uplogix "dot" com >
|
||||
* Based on 8250_boca.
|
||||
*
|
||||
* Copyright (C) 2005 Russell King.
|
||||
* Data taken from include/asm-i386/serial.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
#define PORT(_base,_irq) \
|
||||
{ \
|
||||
.iobase = _base, \
|
||||
.irq = _irq, \
|
||||
.uartclk = 1843200, \
|
||||
.iotype = UPIO_PORT, \
|
||||
.flags = UPF_BOOT_AUTOCONF, \
|
||||
}
|
||||
|
||||
static struct plat_serial8250_port exar_data[] = {
|
||||
PORT(0x100, 5),
|
||||
PORT(0x108, 5),
|
||||
PORT(0x110, 5),
|
||||
PORT(0x118, 5),
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_device exar_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_EXAR_ST16C554,
|
||||
.dev = {
|
||||
.platform_data = exar_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init exar_init(void)
|
||||
{
|
||||
return platform_device_register(&exar_device);
|
||||
}
|
||||
|
||||
module_init(exar_init);
|
||||
|
||||
MODULE_AUTHOR("Paul B Schroeder");
|
||||
MODULE_DESCRIPTION("8250 serial probe module for Exar cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
53
drivers/serial/8250_fourport.c
Normal file
53
drivers/serial/8250_fourport.c
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* linux/drivers/serial/8250_fourport.c
|
||||
*
|
||||
* Copyright (C) 2005 Russell King.
|
||||
* Data taken from include/asm-i386/serial.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
#define PORT(_base,_irq) \
|
||||
{ \
|
||||
.iobase = _base, \
|
||||
.irq = _irq, \
|
||||
.uartclk = 1843200, \
|
||||
.iotype = UPIO_PORT, \
|
||||
.flags = UPF_BOOT_AUTOCONF | UPF_FOURPORT, \
|
||||
}
|
||||
|
||||
static struct plat_serial8250_port fourport_data[] = {
|
||||
PORT(0x1a0, 9),
|
||||
PORT(0x1a8, 9),
|
||||
PORT(0x1b0, 9),
|
||||
PORT(0x1b8, 9),
|
||||
PORT(0x2a0, 5),
|
||||
PORT(0x2a8, 5),
|
||||
PORT(0x2b0, 5),
|
||||
PORT(0x2b8, 5),
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_device fourport_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_FOURPORT,
|
||||
.dev = {
|
||||
.platform_data = fourport_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init fourport_init(void)
|
||||
{
|
||||
return platform_device_register(&fourport_device);
|
||||
}
|
||||
|
||||
module_init(fourport_init);
|
||||
|
||||
MODULE_AUTHOR("Russell King");
|
||||
MODULE_DESCRIPTION("8250 serial probe module for AST Fourport cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
122
drivers/serial/8250_gsc.c
Normal file
122
drivers/serial/8250_gsc.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Serial Device Initialisation for Lasi/Asp/Wax/Dino
|
||||
*
|
||||
* (c) Copyright Matthew Wilcox <willy@debian.org> 2001-2002
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/parisc-device.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
static int __init
|
||||
serial_init_chip(struct parisc_device *dev)
|
||||
{
|
||||
struct uart_port port;
|
||||
unsigned long address;
|
||||
int err;
|
||||
|
||||
if (!dev->irq) {
|
||||
/* We find some unattached serial ports by walking native
|
||||
* busses. These should be silently ignored. Otherwise,
|
||||
* what we have here is a missing parent device, so tell
|
||||
* the user what they're missing.
|
||||
*/
|
||||
if (parisc_parent(dev)->id.hw_type != HPHW_IOA) {
|
||||
printk(KERN_INFO "Serial: device 0x%lx not configured.\n"
|
||||
"Enable support for Wax, Lasi, Asp or Dino.\n",
|
||||
dev->hpa.start);
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
address = dev->hpa.start;
|
||||
if (dev->id.sversion != 0x8d) {
|
||||
address += 0x800;
|
||||
}
|
||||
|
||||
memset(&port, 0, sizeof(port));
|
||||
port.iotype = UPIO_MEM;
|
||||
/* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */
|
||||
port.uartclk = 7272727;
|
||||
port.mapbase = address;
|
||||
port.membase = ioremap_nocache(address, 16);
|
||||
port.irq = dev->irq;
|
||||
port.flags = UPF_BOOT_AUTOCONF;
|
||||
port.dev = &dev->dev;
|
||||
|
||||
err = serial8250_register_port(&port);
|
||||
if (err < 0) {
|
||||
printk(KERN_WARNING "serial8250_register_port returned error %d\n", err);
|
||||
iounmap(port.membase);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct parisc_device_id serial_tbl[] = {
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 },
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c },
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
/* Hack. Some machines have SERIAL_0 attached to Lasi and SERIAL_1
|
||||
* attached to Dino. Unfortunately, Dino appears before Lasi in the device
|
||||
* tree. To ensure that ttyS0 == SERIAL_0, we register two drivers; one
|
||||
* which only knows about Lasi and then a second which will find all the
|
||||
* other serial ports. HPUX ignores this problem.
|
||||
*/
|
||||
static struct parisc_device_id lasi_tbl[] = {
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03E, 0x0008C }, /* B132L+ */
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03F, 0x0008C }, /* B180L+ */
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x046, 0x0008C }, /* Rocky2 120 */
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x047, 0x0008C }, /* Rocky2 150 */
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x04E, 0x0008C }, /* Kiji L2 132 */
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x056, 0x0008C }, /* Raven+ */
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
MODULE_DEVICE_TABLE(parisc, serial_tbl);
|
||||
|
||||
static struct parisc_driver lasi_driver = {
|
||||
.name = "serial_1",
|
||||
.id_table = lasi_tbl,
|
||||
.probe = serial_init_chip,
|
||||
};
|
||||
|
||||
static struct parisc_driver serial_driver = {
|
||||
.name = "serial",
|
||||
.id_table = serial_tbl,
|
||||
.probe = serial_init_chip,
|
||||
};
|
||||
|
||||
int __init probe_serial_gsc(void)
|
||||
{
|
||||
register_parisc_driver(&lasi_driver);
|
||||
register_parisc_driver(&serial_driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(probe_serial_gsc);
|
||||
330
drivers/serial/8250_hp300.c
Normal file
330
drivers/serial/8250_hp300.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Driver for the 98626/98644/internal serial interface on hp300/hp400
|
||||
* (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs)
|
||||
*
|
||||
* Ported from 2.2 and modified to use the normal 8250 driver
|
||||
* by Kars de Jong <jongk@linux-m68k.org>, May 2004.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dio.h>
|
||||
#include <linux/console.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI)
|
||||
#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure?
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HPAPCI
|
||||
struct hp300_port
|
||||
{
|
||||
struct hp300_port *next; /* next port */
|
||||
int line; /* line (tty) number */
|
||||
};
|
||||
|
||||
static struct hp300_port *hp300_ports;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HPDCA
|
||||
|
||||
static int __devinit hpdca_init_one(struct dio_dev *d,
|
||||
const struct dio_device_id *ent);
|
||||
static void __devexit hpdca_remove_one(struct dio_dev *d);
|
||||
|
||||
static struct dio_device_id hpdca_dio_tbl[] = {
|
||||
{ DIO_ID_DCA0 },
|
||||
{ DIO_ID_DCA0REM },
|
||||
{ DIO_ID_DCA1 },
|
||||
{ DIO_ID_DCA1REM },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct dio_driver hpdca_driver = {
|
||||
.name = "hpdca",
|
||||
.id_table = hpdca_dio_tbl,
|
||||
.probe = hpdca_init_one,
|
||||
.remove = __devexit_p(hpdca_remove_one),
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static unsigned int num_ports;
|
||||
|
||||
extern int hp300_uart_scode;
|
||||
|
||||
/* Offset to UART registers from base of DCA */
|
||||
#define UART_OFFSET 17
|
||||
|
||||
#define DCA_ID 0x01 /* ID (read), reset (write) */
|
||||
#define DCA_IC 0x03 /* Interrupt control */
|
||||
|
||||
/* Interrupt control */
|
||||
#define DCA_IC_IE 0x80 /* Master interrupt enable */
|
||||
|
||||
#define HPDCA_BAUD_BASE 153600
|
||||
|
||||
/* Base address of the Frodo part */
|
||||
#define FRODO_BASE (0x41c000)
|
||||
|
||||
/*
|
||||
* Where we find the 8250-like APCI ports, and how far apart they are.
|
||||
*/
|
||||
#define FRODO_APCIBASE 0x0
|
||||
#define FRODO_APCISPACE 0x20
|
||||
#define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE))
|
||||
|
||||
#define HPAPCI_BAUD_BASE 500400
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
||||
/*
|
||||
* Parse the bootinfo to find descriptions for headless console and
|
||||
* debug serial ports and register them with the 8250 driver.
|
||||
* This function should be called before serial_console_init() is called
|
||||
* to make sure the serial console will be available for use. IA-64 kernel
|
||||
* calls this function from setup_arch() after the EFI and ACPI tables have
|
||||
* been parsed.
|
||||
*/
|
||||
int __init hp300_setup_serial_console(void)
|
||||
{
|
||||
int scode;
|
||||
struct uart_port port;
|
||||
|
||||
memset(&port, 0, sizeof(port));
|
||||
|
||||
if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX)
|
||||
return 0;
|
||||
|
||||
if (DIO_SCINHOLE(hp300_uart_scode))
|
||||
return 0;
|
||||
|
||||
scode = hp300_uart_scode;
|
||||
|
||||
/* Memory mapped I/O */
|
||||
port.iotype = UPIO_MEM;
|
||||
port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
|
||||
port.type = PORT_UNKNOWN;
|
||||
|
||||
/* Check for APCI console */
|
||||
if (scode == 256) {
|
||||
#ifdef CONFIG_HPAPCI
|
||||
printk(KERN_INFO "Serial console is HP APCI 1\n");
|
||||
|
||||
port.uartclk = HPAPCI_BAUD_BASE * 16;
|
||||
port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1));
|
||||
port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
|
||||
port.regshift = 2;
|
||||
add_preferred_console("ttyS", port.line, "9600n8");
|
||||
#else
|
||||
printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef CONFIG_HPDCA
|
||||
unsigned long pa = dio_scodetophysaddr(scode);
|
||||
if (!pa) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode);
|
||||
|
||||
port.uartclk = HPDCA_BAUD_BASE * 16;
|
||||
port.mapbase = (pa + UART_OFFSET);
|
||||
port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
|
||||
port.regshift = 1;
|
||||
port.irq = DIO_IPL(pa + DIO_VIRADDRBASE);
|
||||
|
||||
/* Enable board-interrupts */
|
||||
out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE);
|
||||
|
||||
if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) {
|
||||
add_preferred_console("ttyS", port.line, "9600n8");
|
||||
}
|
||||
#else
|
||||
printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (early_serial_setup(&port) < 0) {
|
||||
printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SERIAL_8250_CONSOLE */
|
||||
|
||||
#ifdef CONFIG_HPDCA
|
||||
static int __devinit hpdca_init_one(struct dio_dev *d,
|
||||
const struct dio_device_id *ent)
|
||||
{
|
||||
struct uart_port port;
|
||||
int line;
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
||||
if (hp300_uart_scode == d->scode) {
|
||||
/* Already got it. */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
memset(&port, 0, sizeof(struct uart_port));
|
||||
|
||||
/* Memory mapped I/O */
|
||||
port.iotype = UPIO_MEM;
|
||||
port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
|
||||
port.irq = d->ipl;
|
||||
port.uartclk = HPDCA_BAUD_BASE * 16;
|
||||
port.mapbase = (d->resource.start + UART_OFFSET);
|
||||
port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
|
||||
port.regshift = 1;
|
||||
port.dev = &d->dev;
|
||||
line = serial8250_register_port(&port);
|
||||
|
||||
if (line < 0) {
|
||||
printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
|
||||
" irq %d failed\n", d->scode, port.irq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Enable board-interrupts */
|
||||
out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE);
|
||||
dio_set_drvdata(d, (void *)line);
|
||||
|
||||
/* Reset the DCA */
|
||||
out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff);
|
||||
udelay(100);
|
||||
|
||||
num_ports++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init hp300_8250_init(void)
|
||||
{
|
||||
static int called = 0;
|
||||
#ifdef CONFIG_HPAPCI
|
||||
int line;
|
||||
unsigned long base;
|
||||
struct uart_port uport;
|
||||
struct hp300_port *port;
|
||||
int i;
|
||||
#endif
|
||||
if (called)
|
||||
return -ENODEV;
|
||||
called = 1;
|
||||
|
||||
if (!MACH_IS_HP300)
|
||||
return -ENODEV;
|
||||
|
||||
#ifdef CONFIG_HPDCA
|
||||
dio_register_driver(&hpdca_driver);
|
||||
#endif
|
||||
#ifdef CONFIG_HPAPCI
|
||||
if (hp300_model < HP_400) {
|
||||
if (!num_ports)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
/* These models have the Frodo chip.
|
||||
* Port 0 is reserved for the Apollo Domain keyboard.
|
||||
* Port 1 is either the console or the DCA.
|
||||
*/
|
||||
for (i = 1; i < 4; i++) {
|
||||
/* Port 1 is the console on a 425e, on other machines it's mapped to
|
||||
* DCA.
|
||||
*/
|
||||
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
||||
if (i == 1) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create new serial device */
|
||||
port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&uport, 0, sizeof(struct uart_port));
|
||||
|
||||
base = (FRODO_BASE + FRODO_APCI_OFFSET(i));
|
||||
|
||||
/* Memory mapped I/O */
|
||||
uport.iotype = UPIO_MEM;
|
||||
uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
|
||||
/* XXX - no interrupt support yet */
|
||||
uport.irq = 0;
|
||||
uport.uartclk = HPAPCI_BAUD_BASE * 16;
|
||||
uport.mapbase = base;
|
||||
uport.membase = (char *)(base + DIO_VIRADDRBASE);
|
||||
uport.regshift = 2;
|
||||
|
||||
line = serial8250_register_port(&uport);
|
||||
|
||||
if (line < 0) {
|
||||
printk(KERN_NOTICE "8250_hp300: register_serial() APCI %d"
|
||||
" irq %d failed\n", i, uport.irq);
|
||||
kfree(port);
|
||||
continue;
|
||||
}
|
||||
|
||||
port->line = line;
|
||||
port->next = hp300_ports;
|
||||
hp300_ports = port;
|
||||
|
||||
num_ports++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Any boards found? */
|
||||
if (!num_ports)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HPDCA
|
||||
static void __devexit hpdca_remove_one(struct dio_dev *d)
|
||||
{
|
||||
int line;
|
||||
|
||||
line = (int) dio_get_drvdata(d);
|
||||
if (d->resource.start) {
|
||||
/* Disable board-interrupts */
|
||||
out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0);
|
||||
}
|
||||
serial8250_unregister_port(line);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __exit hp300_8250_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_HPAPCI
|
||||
struct hp300_port *port, *to_free;
|
||||
|
||||
for (port = hp300_ports; port; ) {
|
||||
serial8250_unregister_port(port->line);
|
||||
to_free = port;
|
||||
port = port->next;
|
||||
kfree(to_free);
|
||||
}
|
||||
|
||||
hp300_ports = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_HPDCA
|
||||
dio_unregister_driver(&hpdca_driver);
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(hp300_8250_init);
|
||||
module_exit(hp300_8250_exit);
|
||||
MODULE_DESCRIPTION("HP DCA/APCI serial driver");
|
||||
MODULE_AUTHOR("Kars de Jong <jongk@linux-m68k.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
58
drivers/serial/8250_hub6.c
Normal file
58
drivers/serial/8250_hub6.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* linux/drivers/serial/8250_hub6.c
|
||||
*
|
||||
* Copyright (C) 2005 Russell King.
|
||||
* Data taken from include/asm-i386/serial.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
#define HUB6(card,port) \
|
||||
{ \
|
||||
.iobase = 0x302, \
|
||||
.irq = 3, \
|
||||
.uartclk = 1843200, \
|
||||
.iotype = UPIO_HUB6, \
|
||||
.flags = UPF_BOOT_AUTOCONF, \
|
||||
.hub6 = (card) << 6 | (port) << 3 | 1, \
|
||||
}
|
||||
|
||||
static struct plat_serial8250_port hub6_data[] = {
|
||||
HUB6(0,0),
|
||||
HUB6(0,1),
|
||||
HUB6(0,2),
|
||||
HUB6(0,3),
|
||||
HUB6(0,4),
|
||||
HUB6(0,5),
|
||||
HUB6(1,0),
|
||||
HUB6(1,1),
|
||||
HUB6(1,2),
|
||||
HUB6(1,3),
|
||||
HUB6(1,4),
|
||||
HUB6(1,5),
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_device hub6_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_HUB6,
|
||||
.dev = {
|
||||
.platform_data = hub6_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init hub6_init(void)
|
||||
{
|
||||
return platform_device_register(&hub6_device);
|
||||
}
|
||||
|
||||
module_init(hub6_init);
|
||||
|
||||
MODULE_AUTHOR("Russell King");
|
||||
MODULE_DESCRIPTION("8250 serial probe module for Hub6 cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
63
drivers/serial/8250_mca.c
Normal file
63
drivers/serial/8250_mca.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* linux/drivers/serial/8250_mca.c
|
||||
*
|
||||
* Copyright (C) 2005 Russell King.
|
||||
* Data taken from include/asm-i386/serial.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mca.h>
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
/*
|
||||
* FIXME: Should we be doing AUTO_IRQ here?
|
||||
*/
|
||||
#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
|
||||
#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ
|
||||
#else
|
||||
#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST
|
||||
#endif
|
||||
|
||||
#define PORT(_base,_irq) \
|
||||
{ \
|
||||
.iobase = _base, \
|
||||
.irq = _irq, \
|
||||
.uartclk = 1843200, \
|
||||
.iotype = UPIO_PORT, \
|
||||
.flags = MCA_FLAGS, \
|
||||
}
|
||||
|
||||
static struct plat_serial8250_port mca_data[] = {
|
||||
PORT(0x3220, 3),
|
||||
PORT(0x3228, 3),
|
||||
PORT(0x4220, 3),
|
||||
PORT(0x4228, 3),
|
||||
PORT(0x5220, 3),
|
||||
PORT(0x5228, 3),
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_device mca_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_MCA,
|
||||
.dev = {
|
||||
.platform_data = mca_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mca_init(void)
|
||||
{
|
||||
if (!MCA_bus)
|
||||
return -ENODEV;
|
||||
return platform_device_register(&mca_device);
|
||||
}
|
||||
|
||||
module_init(mca_init);
|
||||
|
||||
MODULE_AUTHOR("Russell King");
|
||||
MODULE_DESCRIPTION("8250 serial probe module for MCA ports");
|
||||
MODULE_LICENSE("GPL");
|
||||
2449
drivers/serial/8250_pci.c
Normal file
2449
drivers/serial/8250_pci.c
Normal file
File diff suppressed because it is too large
Load Diff
518
drivers/serial/8250_pnp.c
Normal file
518
drivers/serial/8250_pnp.c
Normal file
@@ -0,0 +1,518 @@
|
||||
/*
|
||||
* linux/drivers/char/8250_pnp.c
|
||||
*
|
||||
* Probe module for 8250/16550-type ISAPNP serial ports.
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* Copyright (C) 2001 Russell King, All Rights Reserved.
|
||||
*
|
||||
* Ported to the Linux PnP Layer - (C) Adam Belay.
|
||||
*
|
||||
* 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; either version 2 of the License.
|
||||
*
|
||||
* $Id: 8250_pnp.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pnp.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
#define UNKNOWN_DEV 0x3000
|
||||
|
||||
|
||||
static const struct pnp_device_id pnp_dev_table[] = {
|
||||
/* Archtek America Corp. */
|
||||
/* Archtek SmartLink Modem 3334BT Plug & Play */
|
||||
{ "AAC000F", 0 },
|
||||
/* Anchor Datacomm BV */
|
||||
/* SXPro 144 External Data Fax Modem Plug & Play */
|
||||
{ "ADC0001", 0 },
|
||||
/* SXPro 288 External Data Fax Modem Plug & Play */
|
||||
{ "ADC0002", 0 },
|
||||
/* PROLiNK 1456VH ISA PnP K56flex Fax Modem */
|
||||
{ "AEI0250", 0 },
|
||||
/* Actiontec ISA PNP 56K X2 Fax Modem */
|
||||
{ "AEI1240", 0 },
|
||||
/* Rockwell 56K ACF II Fax+Data+Voice Modem */
|
||||
{ "AKY1021", 0 /*SPCI_FL_NO_SHIRQ*/ },
|
||||
/* AZT3005 PnP SOUND DEVICE */
|
||||
{ "AZT4001", 0 },
|
||||
/* Best Data Products Inc. Smart One 336F PnP Modem */
|
||||
{ "BDP3336", 0 },
|
||||
/* Boca Research */
|
||||
/* Boca Complete Ofc Communicator 14.4 Data-FAX */
|
||||
{ "BRI0A49", 0 },
|
||||
/* Boca Research 33,600 ACF Modem */
|
||||
{ "BRI1400", 0 },
|
||||
/* Boca 33.6 Kbps Internal FD34FSVD */
|
||||
{ "BRI3400", 0 },
|
||||
/* Boca 33.6 Kbps Internal FD34FSVD */
|
||||
{ "BRI0A49", 0 },
|
||||
/* Best Data Products Inc. Smart One 336F PnP Modem */
|
||||
{ "BDP3336", 0 },
|
||||
/* Computer Peripherals Inc */
|
||||
/* EuroViVa CommCenter-33.6 SP PnP */
|
||||
{ "CPI4050", 0 },
|
||||
/* Creative Labs */
|
||||
/* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */
|
||||
{ "CTL3001", 0 },
|
||||
/* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */
|
||||
{ "CTL3011", 0 },
|
||||
/* Creative */
|
||||
/* Creative Modem Blaster Flash56 DI5601-1 */
|
||||
{ "DMB1032", 0 },
|
||||
/* Creative Modem Blaster V.90 DI5660 */
|
||||
{ "DMB2001", 0 },
|
||||
/* E-Tech */
|
||||
/* E-Tech CyberBULLET PC56RVP */
|
||||
{ "ETT0002", 0 },
|
||||
/* FUJITSU */
|
||||
/* Fujitsu 33600 PnP-I2 R Plug & Play */
|
||||
{ "FUJ0202", 0 },
|
||||
/* Fujitsu FMV-FX431 Plug & Play */
|
||||
{ "FUJ0205", 0 },
|
||||
/* Fujitsu 33600 PnP-I4 R Plug & Play */
|
||||
{ "FUJ0206", 0 },
|
||||
/* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */
|
||||
{ "FUJ0209", 0 },
|
||||
/* Archtek America Corp. */
|
||||
/* Archtek SmartLink Modem 3334BT Plug & Play */
|
||||
{ "GVC000F", 0 },
|
||||
/* Hayes */
|
||||
/* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */
|
||||
{ "HAY0001", 0 },
|
||||
/* Hayes Optima 336 V.34 + FAX + Voice PnP */
|
||||
{ "HAY000C", 0 },
|
||||
/* Hayes Optima 336B V.34 + FAX + Voice PnP */
|
||||
{ "HAY000D", 0 },
|
||||
/* Hayes Accura 56K Ext Fax Modem PnP */
|
||||
{ "HAY5670", 0 },
|
||||
/* Hayes Accura 56K Ext Fax Modem PnP */
|
||||
{ "HAY5674", 0 },
|
||||
/* Hayes Accura 56K Fax Modem PnP */
|
||||
{ "HAY5675", 0 },
|
||||
/* Hayes 288, V.34 + FAX */
|
||||
{ "HAYF000", 0 },
|
||||
/* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */
|
||||
{ "HAYF001", 0 },
|
||||
/* IBM */
|
||||
/* IBM Thinkpad 701 Internal Modem Voice */
|
||||
{ "IBM0033", 0 },
|
||||
/* Intertex */
|
||||
/* Intertex 28k8 33k6 Voice EXT PnP */
|
||||
{ "IXDC801", 0 },
|
||||
/* Intertex 33k6 56k Voice EXT PnP */
|
||||
{ "IXDC901", 0 },
|
||||
/* Intertex 28k8 33k6 Voice SP EXT PnP */
|
||||
{ "IXDD801", 0 },
|
||||
/* Intertex 33k6 56k Voice SP EXT PnP */
|
||||
{ "IXDD901", 0 },
|
||||
/* Intertex 28k8 33k6 Voice SP INT PnP */
|
||||
{ "IXDF401", 0 },
|
||||
/* Intertex 28k8 33k6 Voice SP EXT PnP */
|
||||
{ "IXDF801", 0 },
|
||||
/* Intertex 33k6 56k Voice SP EXT PnP */
|
||||
{ "IXDF901", 0 },
|
||||
/* Kortex International */
|
||||
/* KORTEX 28800 Externe PnP */
|
||||
{ "KOR4522", 0 },
|
||||
/* KXPro 33.6 Vocal ASVD PnP */
|
||||
{ "KORF661", 0 },
|
||||
/* Lasat */
|
||||
/* LASAT Internet 33600 PnP */
|
||||
{ "LAS4040", 0 },
|
||||
/* Lasat Safire 560 PnP */
|
||||
{ "LAS4540", 0 },
|
||||
/* Lasat Safire 336 PnP */
|
||||
{ "LAS5440", 0 },
|
||||
/* Microcom, Inc. */
|
||||
/* Microcom TravelPorte FAST V.34 Plug & Play */
|
||||
{ "MNP0281", 0 },
|
||||
/* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */
|
||||
{ "MNP0336", 0 },
|
||||
/* Microcom DeskPorte FAST EP 28.8 Plug & Play */
|
||||
{ "MNP0339", 0 },
|
||||
/* Microcom DeskPorte 28.8P Plug & Play */
|
||||
{ "MNP0342", 0 },
|
||||
/* Microcom DeskPorte FAST ES 28.8 Plug & Play */
|
||||
{ "MNP0500", 0 },
|
||||
/* Microcom DeskPorte FAST ES 28.8 Plug & Play */
|
||||
{ "MNP0501", 0 },
|
||||
/* Microcom DeskPorte 28.8S Internal Plug & Play */
|
||||
{ "MNP0502", 0 },
|
||||
/* Motorola */
|
||||
/* Motorola BitSURFR Plug & Play */
|
||||
{ "MOT1105", 0 },
|
||||
/* Motorola TA210 Plug & Play */
|
||||
{ "MOT1111", 0 },
|
||||
/* Motorola HMTA 200 (ISDN) Plug & Play */
|
||||
{ "MOT1114", 0 },
|
||||
/* Motorola BitSURFR Plug & Play */
|
||||
{ "MOT1115", 0 },
|
||||
/* Motorola Lifestyle 28.8 Internal */
|
||||
{ "MOT1190", 0 },
|
||||
/* Motorola V.3400 Plug & Play */
|
||||
{ "MOT1501", 0 },
|
||||
/* Motorola Lifestyle 28.8 V.34 Plug & Play */
|
||||
{ "MOT1502", 0 },
|
||||
/* Motorola Power 28.8 V.34 Plug & Play */
|
||||
{ "MOT1505", 0 },
|
||||
/* Motorola ModemSURFR External 28.8 Plug & Play */
|
||||
{ "MOT1509", 0 },
|
||||
/* Motorola Premier 33.6 Desktop Plug & Play */
|
||||
{ "MOT150A", 0 },
|
||||
/* Motorola VoiceSURFR 56K External PnP */
|
||||
{ "MOT150F", 0 },
|
||||
/* Motorola ModemSURFR 56K External PnP */
|
||||
{ "MOT1510", 0 },
|
||||
/* Motorola ModemSURFR 56K Internal PnP */
|
||||
{ "MOT1550", 0 },
|
||||
/* Motorola ModemSURFR Internal 28.8 Plug & Play */
|
||||
{ "MOT1560", 0 },
|
||||
/* Motorola Premier 33.6 Internal Plug & Play */
|
||||
{ "MOT1580", 0 },
|
||||
/* Motorola OnlineSURFR 28.8 Internal Plug & Play */
|
||||
{ "MOT15B0", 0 },
|
||||
/* Motorola VoiceSURFR 56K Internal PnP */
|
||||
{ "MOT15F0", 0 },
|
||||
/* Com 1 */
|
||||
/* Deskline K56 Phone System PnP */
|
||||
{ "MVX00A1", 0 },
|
||||
/* PC Rider K56 Phone System PnP */
|
||||
{ "MVX00F2", 0 },
|
||||
/* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */
|
||||
{ "nEC8241", 0 },
|
||||
/* Pace 56 Voice Internal Plug & Play Modem */
|
||||
{ "PMC2430", 0 },
|
||||
/* Generic */
|
||||
/* Generic standard PC COM port */
|
||||
{ "PNP0500", 0 },
|
||||
/* Generic 16550A-compatible COM port */
|
||||
{ "PNP0501", 0 },
|
||||
/* Compaq 14400 Modem */
|
||||
{ "PNPC000", 0 },
|
||||
/* Compaq 2400/9600 Modem */
|
||||
{ "PNPC001", 0 },
|
||||
/* Dial-Up Networking Serial Cable between 2 PCs */
|
||||
{ "PNPC031", 0 },
|
||||
/* Dial-Up Networking Parallel Cable between 2 PCs */
|
||||
{ "PNPC032", 0 },
|
||||
/* Standard 9600 bps Modem */
|
||||
{ "PNPC100", 0 },
|
||||
/* Standard 14400 bps Modem */
|
||||
{ "PNPC101", 0 },
|
||||
/* Standard 28800 bps Modem*/
|
||||
{ "PNPC102", 0 },
|
||||
/* Standard Modem*/
|
||||
{ "PNPC103", 0 },
|
||||
/* Standard 9600 bps Modem*/
|
||||
{ "PNPC104", 0 },
|
||||
/* Standard 14400 bps Modem*/
|
||||
{ "PNPC105", 0 },
|
||||
/* Standard 28800 bps Modem*/
|
||||
{ "PNPC106", 0 },
|
||||
/* Standard Modem */
|
||||
{ "PNPC107", 0 },
|
||||
/* Standard 9600 bps Modem */
|
||||
{ "PNPC108", 0 },
|
||||
/* Standard 14400 bps Modem */
|
||||
{ "PNPC109", 0 },
|
||||
/* Standard 28800 bps Modem */
|
||||
{ "PNPC10A", 0 },
|
||||
/* Standard Modem */
|
||||
{ "PNPC10B", 0 },
|
||||
/* Standard 9600 bps Modem */
|
||||
{ "PNPC10C", 0 },
|
||||
/* Standard 14400 bps Modem */
|
||||
{ "PNPC10D", 0 },
|
||||
/* Standard 28800 bps Modem */
|
||||
{ "PNPC10E", 0 },
|
||||
/* Standard Modem */
|
||||
{ "PNPC10F", 0 },
|
||||
/* Standard PCMCIA Card Modem */
|
||||
{ "PNP2000", 0 },
|
||||
/* Rockwell */
|
||||
/* Modular Technology */
|
||||
/* Rockwell 33.6 DPF Internal PnP */
|
||||
/* Modular Technology 33.6 Internal PnP */
|
||||
{ "ROK0030", 0 },
|
||||
/* Kortex International */
|
||||
/* KORTEX 14400 Externe PnP */
|
||||
{ "ROK0100", 0 },
|
||||
/* Rockwell 28.8 */
|
||||
{ "ROK4120", 0 },
|
||||
/* Viking Components, Inc */
|
||||
/* Viking 28.8 INTERNAL Fax+Data+Voice PnP */
|
||||
{ "ROK4920", 0 },
|
||||
/* Rockwell */
|
||||
/* British Telecom */
|
||||
/* Modular Technology */
|
||||
/* Rockwell 33.6 DPF External PnP */
|
||||
/* BT Prologue 33.6 External PnP */
|
||||
/* Modular Technology 33.6 External PnP */
|
||||
{ "RSS00A0", 0 },
|
||||
/* Viking 56K FAX INT */
|
||||
{ "RSS0262", 0 },
|
||||
/* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */
|
||||
{ "RSS0250", 0 },
|
||||
/* SupraExpress 28.8 Data/Fax PnP modem */
|
||||
{ "SUP1310", 0 },
|
||||
/* SupraExpress 33.6 Data/Fax PnP modem */
|
||||
{ "SUP1421", 0 },
|
||||
/* SupraExpress 33.6 Data/Fax PnP modem */
|
||||
{ "SUP1590", 0 },
|
||||
/* SupraExpress 336i Sp ASVD */
|
||||
{ "SUP1620", 0 },
|
||||
/* SupraExpress 33.6 Data/Fax PnP modem */
|
||||
{ "SUP1760", 0 },
|
||||
/* SupraExpress 56i Sp Intl */
|
||||
{ "SUP2171", 0 },
|
||||
/* Phoebe Micro */
|
||||
/* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */
|
||||
{ "TEX0011", 0 },
|
||||
/* Archtek America Corp. */
|
||||
/* Archtek SmartLink Modem 3334BT Plug & Play */
|
||||
{ "UAC000F", 0 },
|
||||
/* 3Com Corp. */
|
||||
/* Gateway Telepath IIvi 33.6 */
|
||||
{ "USR0000", 0 },
|
||||
/* U.S. Robotics Sporster 33.6K Fax INT PnP */
|
||||
{ "USR0002", 0 },
|
||||
/* Sportster Vi 14.4 PnP FAX Voicemail */
|
||||
{ "USR0004", 0 },
|
||||
/* U.S. Robotics 33.6K Voice INT PnP */
|
||||
{ "USR0006", 0 },
|
||||
/* U.S. Robotics 33.6K Voice EXT PnP */
|
||||
{ "USR0007", 0 },
|
||||
/* U.S. Robotics Courier V.Everything INT PnP */
|
||||
{ "USR0009", 0 },
|
||||
/* U.S. Robotics 33.6K Voice INT PnP */
|
||||
{ "USR2002", 0 },
|
||||
/* U.S. Robotics 56K Voice INT PnP */
|
||||
{ "USR2070", 0 },
|
||||
/* U.S. Robotics 56K Voice EXT PnP */
|
||||
{ "USR2080", 0 },
|
||||
/* U.S. Robotics 56K FAX INT */
|
||||
{ "USR3031", 0 },
|
||||
/* U.S. Robotics 56K FAX INT */
|
||||
{ "USR3050", 0 },
|
||||
/* U.S. Robotics 56K Voice INT PnP */
|
||||
{ "USR3070", 0 },
|
||||
/* U.S. Robotics 56K Voice EXT PnP */
|
||||
{ "USR3080", 0 },
|
||||
/* U.S. Robotics 56K Voice INT PnP */
|
||||
{ "USR3090", 0 },
|
||||
/* U.S. Robotics 56K Message */
|
||||
{ "USR9100", 0 },
|
||||
/* U.S. Robotics 56K FAX EXT PnP*/
|
||||
{ "USR9160", 0 },
|
||||
/* U.S. Robotics 56K FAX INT PnP*/
|
||||
{ "USR9170", 0 },
|
||||
/* U.S. Robotics 56K Voice EXT PnP*/
|
||||
{ "USR9180", 0 },
|
||||
/* U.S. Robotics 56K Voice INT PnP*/
|
||||
{ "USR9190", 0 },
|
||||
/* Wacom tablets */
|
||||
{ "WACF004", 0 },
|
||||
{ "WACF005", 0 },
|
||||
{ "WACF006", 0 },
|
||||
/* Compaq touchscreen */
|
||||
{ "FPI2002", 0 },
|
||||
/* Fujitsu Stylistic touchscreens */
|
||||
{ "FUJ02B2", 0 },
|
||||
{ "FUJ02B3", 0 },
|
||||
/* Fujitsu Stylistic LT touchscreens */
|
||||
{ "FUJ02B4", 0 },
|
||||
/* Passive Fujitsu Stylistic touchscreens */
|
||||
{ "FUJ02B6", 0 },
|
||||
{ "FUJ02B7", 0 },
|
||||
{ "FUJ02B8", 0 },
|
||||
{ "FUJ02B9", 0 },
|
||||
{ "FUJ02BC", 0 },
|
||||
/* Fujitsu Wacom Tablet PC devices */
|
||||
{ "FUJ02E5", 0 },
|
||||
{ "FUJ02E6", 0 },
|
||||
/* Rockwell's (PORALiNK) 33600 INT PNP */
|
||||
{ "WCI0003", 0 },
|
||||
/* Unkown PnP modems */
|
||||
{ "PNPCXXX", UNKNOWN_DEV },
|
||||
/* More unkown PnP modems */
|
||||
{ "PNPDXXX", UNKNOWN_DEV },
|
||||
{ "", 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
|
||||
|
||||
static char *modem_names[] __devinitdata = {
|
||||
"MODEM", "Modem", "modem", "FAX", "Fax", "fax",
|
||||
"56K", "56k", "K56", "33.6", "28.8", "14.4",
|
||||
"33,600", "28,800", "14,400", "33.600", "28.800", "14.400",
|
||||
"33600", "28800", "14400", "V.90", "V.34", "V.32", NULL
|
||||
};
|
||||
|
||||
static int __devinit check_name(char *name)
|
||||
{
|
||||
char **tmp;
|
||||
|
||||
for (tmp = modem_names; *tmp; tmp++)
|
||||
if (strstr(name, *tmp))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit check_resources(struct pnp_option *option)
|
||||
{
|
||||
struct pnp_option *tmp;
|
||||
if (!option)
|
||||
return 0;
|
||||
|
||||
for (tmp = option; tmp; tmp = tmp->next) {
|
||||
struct pnp_port *port;
|
||||
for (port = tmp->port; port; port = port->next)
|
||||
if ((port->size == 8) &&
|
||||
((port->min == 0x2f8) ||
|
||||
(port->min == 0x3f8) ||
|
||||
(port->min == 0x2e8) ||
|
||||
(port->min == 0x3e8)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a complete unknown PnP device, try to use some heuristics to
|
||||
* detect modems. Currently use such heuristic set:
|
||||
* - dev->name or dev->bus->name must contain "modem" substring;
|
||||
* - device must have only one IO region (8 byte long) with base address
|
||||
* 0x2e8, 0x3e8, 0x2f8 or 0x3f8.
|
||||
*
|
||||
* Such detection looks very ugly, but can detect at least some of numerous
|
||||
* PnP modems, alternatively we must hardcode all modems in pnp_devices[]
|
||||
* table.
|
||||
*/
|
||||
static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
|
||||
{
|
||||
if (!(check_name(pnp_dev_name(dev)) || (dev->card && check_name(dev->card->name))))
|
||||
return -ENODEV;
|
||||
|
||||
if (check_resources(dev->independent))
|
||||
return 0;
|
||||
|
||||
if (check_resources(dev->dependent))
|
||||
return 0;
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
|
||||
{
|
||||
struct uart_port port;
|
||||
int ret, line, flags = dev_id->driver_data;
|
||||
|
||||
if (flags & UNKNOWN_DEV) {
|
||||
ret = serial_pnp_guess_board(dev, &flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&port, 0, sizeof(struct uart_port));
|
||||
port.irq = pnp_irq(dev, 0);
|
||||
if (pnp_port_valid(dev, 0)) {
|
||||
port.iobase = pnp_port_start(dev, 0);
|
||||
port.iotype = UPIO_PORT;
|
||||
} else if (pnp_mem_valid(dev, 0)) {
|
||||
port.mapbase = pnp_mem_start(dev, 0);
|
||||
port.iotype = UPIO_MEM;
|
||||
port.flags = UPF_IOREMAP;
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
#ifdef SERIAL_DEBUG_PNP
|
||||
printk("Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n",
|
||||
port.iobase, port.mapbase, port.irq, port.iotype);
|
||||
#endif
|
||||
|
||||
port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
|
||||
if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
|
||||
port.flags |= UPF_SHARE_IRQ;
|
||||
port.uartclk = 1843200;
|
||||
port.dev = &dev->dev;
|
||||
|
||||
line = serial8250_register_port(&port);
|
||||
if (line < 0)
|
||||
return -ENODEV;
|
||||
|
||||
pnp_set_drvdata(dev, (void *)((long)line + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit serial_pnp_remove(struct pnp_dev *dev)
|
||||
{
|
||||
long line = (long)pnp_get_drvdata(dev);
|
||||
if (line)
|
||||
serial8250_unregister_port(line - 1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
|
||||
{
|
||||
long line = (long)pnp_get_drvdata(dev);
|
||||
|
||||
if (!line)
|
||||
return -ENODEV;
|
||||
serial8250_suspend_port(line - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_pnp_resume(struct pnp_dev *dev)
|
||||
{
|
||||
long line = (long)pnp_get_drvdata(dev);
|
||||
|
||||
if (!line)
|
||||
return -ENODEV;
|
||||
serial8250_resume_port(line - 1);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define serial_pnp_suspend NULL
|
||||
#define serial_pnp_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct pnp_driver serial_pnp_driver = {
|
||||
.name = "serial",
|
||||
.probe = serial_pnp_probe,
|
||||
.remove = __devexit_p(serial_pnp_remove),
|
||||
.suspend = serial_pnp_suspend,
|
||||
.resume = serial_pnp_resume,
|
||||
.id_table = pnp_dev_table,
|
||||
};
|
||||
|
||||
static int __init serial8250_pnp_init(void)
|
||||
{
|
||||
return pnp_register_driver(&serial_pnp_driver);
|
||||
}
|
||||
|
||||
static void __exit serial8250_pnp_exit(void)
|
||||
{
|
||||
pnp_unregister_driver(&serial_pnp_driver);
|
||||
}
|
||||
|
||||
module_init(serial8250_pnp_init);
|
||||
module_exit(serial8250_pnp_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver");
|
||||
1063
drivers/serial/Kconfig
Normal file
1063
drivers/serial/Kconfig
Normal file
File diff suppressed because it is too large
Load Diff
63
drivers/serial/Makefile
Normal file
63
drivers/serial/Makefile
Normal file
@@ -0,0 +1,63 @@
|
||||
#
|
||||
# Makefile for the kernel serial device drivers.
|
||||
#
|
||||
# $Id: Makefile,v 1.2 2007/06/14 09:39:46 eyryu Exp $
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
|
||||
obj-$(CONFIG_SERIAL_21285) += 21285.o
|
||||
obj-$(CONFIG_SERIAL_8250) += 8250.o
|
||||
obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
|
||||
obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o
|
||||
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
|
||||
obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
|
||||
obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o
|
||||
obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o
|
||||
obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o
|
||||
obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o
|
||||
obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o
|
||||
obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
|
||||
obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
|
||||
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
|
||||
obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
|
||||
obj-$(CONFIG_SERIAL_8250_AU1X00) += 8250_au1x00.o
|
||||
obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
|
||||
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
|
||||
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
|
||||
obj-$(CONFIG_SERIAL_PXA) += pxa.o
|
||||
obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
|
||||
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
|
||||
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
|
||||
obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
|
||||
obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
|
||||
obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
|
||||
obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o
|
||||
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
|
||||
obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o
|
||||
obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
|
||||
obj-$(CONFIG_SERIAL_MUX) += mux.o
|
||||
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
|
||||
obj-$(CONFIG_SERIAL_68360) += 68360serial.o
|
||||
obj-$(CONFIG_SERIAL_COLDFIRE) += mcfserial.o
|
||||
obj-$(CONFIG_V850E_UART) += v850e_uart.o
|
||||
obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o
|
||||
obj-$(CONFIG_SERIAL_LH7A40X) += serial_lh7a40x.o
|
||||
obj-$(CONFIG_SERIAL_DZ) += dz.o
|
||||
obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o
|
||||
obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o
|
||||
obj-$(CONFIG_SERIAL_CPM) += cpm_uart/
|
||||
obj-$(CONFIG_SERIAL_IMX) += imx.o
|
||||
obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
|
||||
obj-$(CONFIG_SERIAL_ICOM) += icom.o
|
||||
obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
|
||||
obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
|
||||
obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
|
||||
obj-$(CONFIG_SERIAL_JSM) += jsm/
|
||||
obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
|
||||
obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
|
||||
obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
|
||||
obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
|
||||
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
|
||||
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
|
||||
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
|
||||
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
|
||||
770
drivers/serial/amba-pl010.c
Normal file
770
drivers/serial/amba-pl010.c
Normal file
@@ -0,0 +1,770 @@
|
||||
/*
|
||||
* linux/drivers/char/amba.c
|
||||
*
|
||||
* Driver for AMBA serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* Copyright 1999 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Id: amba-pl010.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*
|
||||
* This is a generic driver for ARM AMBA-type serial ports. They
|
||||
* have a lot of 16550-like features, but are not register compatible.
|
||||
* Note that although they do have CTS, DCD and DSR inputs, they do
|
||||
* not have an RI input, nor do they have DTR or RTS outputs. If
|
||||
* required, these have to be supplied via some other means (eg, GPIO)
|
||||
* and hooked into this driver.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_AMBA_PL010_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/serial.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define UART_NR 8
|
||||
|
||||
#define SERIAL_AMBA_MAJOR 204
|
||||
#define SERIAL_AMBA_MINOR 16
|
||||
#define SERIAL_AMBA_NR UART_NR
|
||||
|
||||
#define AMBA_ISR_PASS_LIMIT 256
|
||||
|
||||
#define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0)
|
||||
#define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0)
|
||||
|
||||
#define UART_DUMMY_RSR_RX 256
|
||||
#define UART_PORT_SIZE 64
|
||||
|
||||
/*
|
||||
* We wrap our port structure around the generic uart_port.
|
||||
*/
|
||||
struct uart_amba_port {
|
||||
struct uart_port port;
|
||||
struct amba_device *dev;
|
||||
struct amba_pl010_data *data;
|
||||
unsigned int old_status;
|
||||
};
|
||||
|
||||
static void pl010_stop_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned int cr;
|
||||
|
||||
cr = readb(port->membase + UART010_CR);
|
||||
cr &= ~UART010_CR_TIE;
|
||||
writel(cr, port->membase + UART010_CR);
|
||||
}
|
||||
|
||||
static void pl010_start_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned int cr;
|
||||
|
||||
cr = readb(port->membase + UART010_CR);
|
||||
cr |= UART010_CR_TIE;
|
||||
writel(cr, port->membase + UART010_CR);
|
||||
}
|
||||
|
||||
static void pl010_stop_rx(struct uart_port *port)
|
||||
{
|
||||
unsigned int cr;
|
||||
|
||||
cr = readb(port->membase + UART010_CR);
|
||||
cr &= ~(UART010_CR_RIE | UART010_CR_RTIE);
|
||||
writel(cr, port->membase + UART010_CR);
|
||||
}
|
||||
|
||||
static void pl010_enable_ms(struct uart_port *port)
|
||||
{
|
||||
unsigned int cr;
|
||||
|
||||
cr = readb(port->membase + UART010_CR);
|
||||
cr |= UART010_CR_MSIE;
|
||||
writel(cr, port->membase + UART010_CR);
|
||||
}
|
||||
|
||||
static void pl010_rx_chars(struct uart_port *port)
|
||||
{
|
||||
struct tty_struct *tty = port->info->tty;
|
||||
unsigned int status, ch, flag, rsr, max_count = 256;
|
||||
|
||||
status = readb(port->membase + UART01x_FR);
|
||||
while (UART_RX_DATA(status) && max_count--) {
|
||||
ch = readb(port->membase + UART01x_DR);
|
||||
flag = TTY_NORMAL;
|
||||
|
||||
port->icount.rx++;
|
||||
|
||||
/*
|
||||
* Note that the error handling code is
|
||||
* out of the main execution path
|
||||
*/
|
||||
rsr = readb(port->membase + UART01x_RSR) | UART_DUMMY_RSR_RX;
|
||||
if (unlikely(rsr & UART01x_RSR_ANY)) {
|
||||
writel(0, port->membase + UART01x_ECR);
|
||||
|
||||
if (rsr & UART01x_RSR_BE) {
|
||||
rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE);
|
||||
port->icount.brk++;
|
||||
if (uart_handle_break(port))
|
||||
goto ignore_char;
|
||||
} else if (rsr & UART01x_RSR_PE)
|
||||
port->icount.parity++;
|
||||
else if (rsr & UART01x_RSR_FE)
|
||||
port->icount.frame++;
|
||||
if (rsr & UART01x_RSR_OE)
|
||||
port->icount.overrun++;
|
||||
|
||||
rsr &= port->read_status_mask;
|
||||
|
||||
if (rsr & UART01x_RSR_BE)
|
||||
flag = TTY_BREAK;
|
||||
else if (rsr & UART01x_RSR_PE)
|
||||
flag = TTY_PARITY;
|
||||
else if (rsr & UART01x_RSR_FE)
|
||||
flag = TTY_FRAME;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(port, ch))
|
||||
goto ignore_char;
|
||||
|
||||
uart_insert_char(port, rsr, UART01x_RSR_OE, ch, flag);
|
||||
|
||||
ignore_char:
|
||||
status = readb(port->membase + UART01x_FR);
|
||||
}
|
||||
tty_flip_buffer_push(tty);
|
||||
return;
|
||||
}
|
||||
|
||||
static void pl010_tx_chars(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
int count;
|
||||
|
||||
if (port->x_char) {
|
||||
writel(port->x_char, port->membase + UART01x_DR);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return;
|
||||
}
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
pl010_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
count = port->fifosize >> 1;
|
||||
do {
|
||||
writel(xmit->buf[xmit->tail], port->membase + UART01x_DR);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (--count > 0);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
pl010_stop_tx(port);
|
||||
}
|
||||
|
||||
static void pl010_modem_status(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
unsigned int status, delta;
|
||||
|
||||
writel(0, uap->port.membase + UART010_ICR);
|
||||
|
||||
status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
|
||||
|
||||
delta = status ^ uap->old_status;
|
||||
uap->old_status = status;
|
||||
|
||||
if (!delta)
|
||||
return;
|
||||
|
||||
if (delta & UART01x_FR_DCD)
|
||||
uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD);
|
||||
|
||||
if (delta & UART01x_FR_DSR)
|
||||
uap->port.icount.dsr++;
|
||||
|
||||
if (delta & UART01x_FR_CTS)
|
||||
uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
|
||||
|
||||
wake_up_interruptible(&uap->port.info->delta_msr_wait);
|
||||
}
|
||||
|
||||
static irqreturn_t pl010_int(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
|
||||
int handled = 0;
|
||||
|
||||
spin_lock(&port->lock);
|
||||
|
||||
status = readb(port->membase + UART010_IIR);
|
||||
if (status) {
|
||||
do {
|
||||
if (status & (UART010_IIR_RTIS | UART010_IIR_RIS))
|
||||
pl010_rx_chars(port);
|
||||
if (status & UART010_IIR_MIS)
|
||||
pl010_modem_status(port);
|
||||
if (status & UART010_IIR_TIS)
|
||||
pl010_tx_chars(port);
|
||||
|
||||
if (pass_counter-- == 0)
|
||||
break;
|
||||
|
||||
status = readb(port->membase + UART010_IIR);
|
||||
} while (status & (UART010_IIR_RTIS | UART010_IIR_RIS |
|
||||
UART010_IIR_TIS));
|
||||
handled = 1;
|
||||
}
|
||||
|
||||
spin_unlock(&port->lock);
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static unsigned int pl010_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return readb(port->membase + UART01x_FR) & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static unsigned int pl010_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
unsigned int status;
|
||||
|
||||
status = readb(port->membase + UART01x_FR);
|
||||
if (status & UART01x_FR_DCD)
|
||||
result |= TIOCM_CAR;
|
||||
if (status & UART01x_FR_DSR)
|
||||
result |= TIOCM_DSR;
|
||||
if (status & UART01x_FR_CTS)
|
||||
result |= TIOCM_CTS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
|
||||
if (uap->data)
|
||||
uap->data->set_mctrl(uap->dev, uap->port.membase, mctrl);
|
||||
}
|
||||
|
||||
static void pl010_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int lcr_h;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
lcr_h = readb(port->membase + UART010_LCRH);
|
||||
if (break_state == -1)
|
||||
lcr_h |= UART01x_LCRH_BRK;
|
||||
else
|
||||
lcr_h &= ~UART01x_LCRH_BRK;
|
||||
writel(lcr_h, port->membase + UART010_LCRH);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int pl010_startup(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Allocate the IRQ
|
||||
*/
|
||||
retval = request_irq(port->irq, pl010_int, 0, "uart-pl010", port);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/*
|
||||
* initialise the old status of the modem signals
|
||||
*/
|
||||
uap->old_status = readb(port->membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
|
||||
|
||||
/*
|
||||
* Finally, enable interrupts
|
||||
*/
|
||||
writel(UART01x_CR_UARTEN | UART010_CR_RIE | UART010_CR_RTIE,
|
||||
port->membase + UART010_CR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pl010_shutdown(struct uart_port *port)
|
||||
{
|
||||
/*
|
||||
* Free the interrupt
|
||||
*/
|
||||
free_irq(port->irq, port);
|
||||
|
||||
/*
|
||||
* disable all interrupts, disable the port
|
||||
*/
|
||||
writel(0, port->membase + UART010_CR);
|
||||
|
||||
/* disable break condition and fifos */
|
||||
writel(readb(port->membase + UART010_LCRH) &
|
||||
~(UART01x_LCRH_BRK | UART01x_LCRH_FEN),
|
||||
port->membase + UART010_LCRH);
|
||||
}
|
||||
|
||||
static void
|
||||
pl010_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int lcr_h, old_cr;
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot;
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
lcr_h = UART01x_LCRH_WLEN_5;
|
||||
break;
|
||||
case CS6:
|
||||
lcr_h = UART01x_LCRH_WLEN_6;
|
||||
break;
|
||||
case CS7:
|
||||
lcr_h = UART01x_LCRH_WLEN_7;
|
||||
break;
|
||||
default: // CS8
|
||||
lcr_h = UART01x_LCRH_WLEN_8;
|
||||
break;
|
||||
}
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
lcr_h |= UART01x_LCRH_STP2;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
lcr_h |= UART01x_LCRH_PEN;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
lcr_h |= UART01x_LCRH_EPS;
|
||||
}
|
||||
if (port->fifosize > 1)
|
||||
lcr_h |= UART01x_LCRH_FEN;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
port->read_status_mask = UART01x_RSR_OE;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
port->read_status_mask |= UART01x_RSR_BE;
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
port->ignore_status_mask |= UART01x_RSR_BE;
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= UART01x_RSR_OE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore all characters if CREAD is not set.
|
||||
*/
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |= UART_DUMMY_RSR_RX;
|
||||
|
||||
/* first, disable everything */
|
||||
old_cr = readb(port->membase + UART010_CR) & ~UART010_CR_MSIE;
|
||||
|
||||
if (UART_ENABLE_MS(port, termios->c_cflag))
|
||||
old_cr |= UART010_CR_MSIE;
|
||||
|
||||
writel(0, port->membase + UART010_CR);
|
||||
|
||||
/* Set baud rate */
|
||||
quot -= 1;
|
||||
writel((quot & 0xf00) >> 8, port->membase + UART010_LCRM);
|
||||
writel(quot & 0xff, port->membase + UART010_LCRL);
|
||||
|
||||
/*
|
||||
* ----------v----------v----------v----------v-----
|
||||
* NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
|
||||
* ----------^----------^----------^----------^-----
|
||||
*/
|
||||
writel(lcr_h, port->membase + UART010_LCRH);
|
||||
writel(old_cr, port->membase + UART010_CR);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *pl010_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_AMBA ? "AMBA" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory region(s) being used by 'port'
|
||||
*/
|
||||
static void pl010_release_port(struct uart_port *port)
|
||||
{
|
||||
release_mem_region(port->mapbase, UART_PORT_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Request the memory region(s) being used by 'port'
|
||||
*/
|
||||
static int pl010_request_port(struct uart_port *port)
|
||||
{
|
||||
return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010")
|
||||
!= NULL ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void pl010_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE) {
|
||||
port->type = PORT_AMBA;
|
||||
pl010_request_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* verify the new serial_struct (for TIOCSSERIAL).
|
||||
*/
|
||||
static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
int ret = 0;
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
|
||||
ret = -EINVAL;
|
||||
if (ser->irq < 0 || ser->irq >= NR_IRQS)
|
||||
ret = -EINVAL;
|
||||
if (ser->baud_base < 9600)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops amba_pl010_pops = {
|
||||
.tx_empty = pl010_tx_empty,
|
||||
.set_mctrl = pl010_set_mctrl,
|
||||
.get_mctrl = pl010_get_mctrl,
|
||||
.stop_tx = pl010_stop_tx,
|
||||
.start_tx = pl010_start_tx,
|
||||
.stop_rx = pl010_stop_rx,
|
||||
.enable_ms = pl010_enable_ms,
|
||||
.break_ctl = pl010_break_ctl,
|
||||
.startup = pl010_startup,
|
||||
.shutdown = pl010_shutdown,
|
||||
.set_termios = pl010_set_termios,
|
||||
.type = pl010_type,
|
||||
.release_port = pl010_release_port,
|
||||
.request_port = pl010_request_port,
|
||||
.config_port = pl010_config_port,
|
||||
.verify_port = pl010_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_amba_port *amba_ports[UART_NR];
|
||||
|
||||
#ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE
|
||||
|
||||
static void pl010_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
do {
|
||||
status = readb(port->membase + UART01x_FR);
|
||||
barrier();
|
||||
} while (!UART_TX_READY(status));
|
||||
writel(ch, port->membase + UART01x_DR);
|
||||
}
|
||||
|
||||
static void
|
||||
pl010_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct uart_port *port = &amba_ports[co->index]->port;
|
||||
unsigned int status, old_cr;
|
||||
|
||||
/*
|
||||
* First save the CR then disable the interrupts
|
||||
*/
|
||||
old_cr = readb(port->membase + UART010_CR);
|
||||
writel(UART01x_CR_UARTEN, port->membase + UART010_CR);
|
||||
|
||||
uart_console_write(port, s, count, pl010_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore the TCR
|
||||
*/
|
||||
do {
|
||||
status = readb(port->membase + UART01x_FR);
|
||||
barrier();
|
||||
} while (status & UART01x_FR_BUSY);
|
||||
writel(old_cr, port->membase + UART010_CR);
|
||||
}
|
||||
|
||||
static void __init
|
||||
pl010_console_get_options(struct uart_port *port, int *baud,
|
||||
int *parity, int *bits)
|
||||
{
|
||||
if (readb(port->membase + UART010_CR) & UART01x_CR_UARTEN) {
|
||||
unsigned int lcr_h, quot;
|
||||
lcr_h = readb(port->membase + UART010_LCRH);
|
||||
|
||||
*parity = 'n';
|
||||
if (lcr_h & UART01x_LCRH_PEN) {
|
||||
if (lcr_h & UART01x_LCRH_EPS)
|
||||
*parity = 'e';
|
||||
else
|
||||
*parity = 'o';
|
||||
}
|
||||
|
||||
if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7)
|
||||
*bits = 7;
|
||||
else
|
||||
*bits = 8;
|
||||
|
||||
quot = readb(port->membase + UART010_LCRL) | readb(port->membase + UART010_LCRM) << 8;
|
||||
*baud = port->uartclk / (16 * (quot + 1));
|
||||
}
|
||||
}
|
||||
|
||||
static int __init pl010_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int baud = 38400;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
if (co->index >= UART_NR)
|
||||
co->index = 0;
|
||||
if (!amba_ports[co->index])
|
||||
return -ENODEV;
|
||||
port = &amba_ports[co->index]->port;
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
pl010_console_get_options(port, &baud, &parity, &bits);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver amba_reg;
|
||||
static struct console amba_console = {
|
||||
.name = "ttyAM",
|
||||
.write = pl010_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = pl010_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &amba_reg,
|
||||
};
|
||||
|
||||
#define AMBA_CONSOLE &amba_console
|
||||
#else
|
||||
#define AMBA_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver amba_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "ttyAM",
|
||||
.dev_name = "ttyAM",
|
||||
.major = SERIAL_AMBA_MAJOR,
|
||||
.minor = SERIAL_AMBA_MINOR,
|
||||
.nr = UART_NR,
|
||||
.cons = AMBA_CONSOLE,
|
||||
};
|
||||
|
||||
static int pl010_probe(struct amba_device *dev, void *id)
|
||||
{
|
||||
struct uart_amba_port *port;
|
||||
void __iomem *base;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
|
||||
if (amba_ports[i] == NULL)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(amba_ports)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL);
|
||||
if (!port) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
base = ioremap(dev->res.start, PAGE_SIZE);
|
||||
if (!base) {
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
|
||||
port->port.dev = &dev->dev;
|
||||
port->port.mapbase = dev->res.start;
|
||||
port->port.membase = base;
|
||||
port->port.iotype = UPIO_MEM;
|
||||
port->port.irq = dev->irq[0];
|
||||
port->port.uartclk = 14745600;
|
||||
port->port.fifosize = 16;
|
||||
port->port.ops = &amba_pl010_pops;
|
||||
port->port.flags = UPF_BOOT_AUTOCONF;
|
||||
port->port.line = i;
|
||||
port->dev = dev;
|
||||
port->data = dev->dev.platform_data;
|
||||
|
||||
amba_ports[i] = port;
|
||||
|
||||
amba_set_drvdata(dev, port);
|
||||
ret = uart_add_one_port(&amba_reg, &port->port);
|
||||
if (ret) {
|
||||
amba_set_drvdata(dev, NULL);
|
||||
amba_ports[i] = NULL;
|
||||
iounmap(base);
|
||||
free:
|
||||
kfree(port);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pl010_remove(struct amba_device *dev)
|
||||
{
|
||||
struct uart_amba_port *port = amba_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
amba_set_drvdata(dev, NULL);
|
||||
|
||||
uart_remove_one_port(&amba_reg, &port->port);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
|
||||
if (amba_ports[i] == port)
|
||||
amba_ports[i] = NULL;
|
||||
|
||||
iounmap(port->port.membase);
|
||||
kfree(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pl010_suspend(struct amba_device *dev, pm_message_t state)
|
||||
{
|
||||
struct uart_amba_port *uap = amba_get_drvdata(dev);
|
||||
|
||||
if (uap)
|
||||
uart_suspend_port(&amba_reg, &uap->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pl010_resume(struct amba_device *dev)
|
||||
{
|
||||
struct uart_amba_port *uap = amba_get_drvdata(dev);
|
||||
|
||||
if (uap)
|
||||
uart_resume_port(&amba_reg, &uap->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amba_id pl010_ids[] __initdata = {
|
||||
{
|
||||
.id = 0x00041010,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static struct amba_driver pl010_driver = {
|
||||
.drv = {
|
||||
.name = "uart-pl010",
|
||||
},
|
||||
.id_table = pl010_ids,
|
||||
.probe = pl010_probe,
|
||||
.remove = pl010_remove,
|
||||
.suspend = pl010_suspend,
|
||||
.resume = pl010_resume,
|
||||
};
|
||||
|
||||
static int __init pl010_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "Serial: AMBA driver $Revision: 1.1.1.1 $\n");
|
||||
|
||||
ret = uart_register_driver(&amba_reg);
|
||||
if (ret == 0) {
|
||||
ret = amba_driver_register(&pl010_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&amba_reg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit pl010_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&pl010_driver);
|
||||
uart_unregister_driver(&amba_reg);
|
||||
}
|
||||
|
||||
module_init(pl010_init);
|
||||
module_exit(pl010_exit);
|
||||
|
||||
MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
|
||||
MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.1.1.1 $");
|
||||
MODULE_LICENSE("GPL");
|
||||
825
drivers/serial/amba-pl011.c
Normal file
825
drivers/serial/amba-pl011.c
Normal file
@@ -0,0 +1,825 @@
|
||||
/*
|
||||
* linux/drivers/char/amba.c
|
||||
*
|
||||
* Driver for AMBA serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* Copyright 1999 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Id: amba-pl011.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*
|
||||
* This is a generic driver for ARM AMBA-type serial ports. They
|
||||
* have a lot of 16550-like features, but are not register compatible.
|
||||
* Note that although they do have CTS, DCD and DSR inputs, they do
|
||||
* not have an RI input, nor do they have DTR or RTS outputs. If
|
||||
* required, these have to be supplied via some other means (eg, GPIO)
|
||||
* and hooked into this driver.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/serial.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#define UART_NR 14
|
||||
|
||||
#define SERIAL_AMBA_MAJOR 204
|
||||
#define SERIAL_AMBA_MINOR 64
|
||||
#define SERIAL_AMBA_NR UART_NR
|
||||
|
||||
#define AMBA_ISR_PASS_LIMIT 256
|
||||
|
||||
#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
|
||||
#define UART_DUMMY_DR_RX (1 << 16)
|
||||
|
||||
/*
|
||||
* We wrap our port structure around the generic uart_port.
|
||||
*/
|
||||
struct uart_amba_port {
|
||||
struct uart_port port;
|
||||
struct clk *clk;
|
||||
unsigned int im; /* interrupt mask */
|
||||
unsigned int old_status;
|
||||
};
|
||||
|
||||
static void pl011_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
|
||||
uap->im &= ~UART011_TXIM;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
}
|
||||
|
||||
static void pl011_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
|
||||
uap->im |= UART011_TXIM;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
}
|
||||
|
||||
static void pl011_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
|
||||
uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM|
|
||||
UART011_PEIM|UART011_BEIM|UART011_OEIM);
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
}
|
||||
|
||||
static void pl011_enable_ms(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
|
||||
uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
}
|
||||
|
||||
static void pl011_rx_chars(struct uart_amba_port *uap)
|
||||
{
|
||||
struct tty_struct *tty = uap->port.info->tty;
|
||||
unsigned int status, ch, flag, max_count = 256;
|
||||
|
||||
status = readw(uap->port.membase + UART01x_FR);
|
||||
while ((status & UART01x_FR_RXFE) == 0 && max_count--) {
|
||||
ch = readw(uap->port.membase + UART01x_DR) | UART_DUMMY_DR_RX;
|
||||
flag = TTY_NORMAL;
|
||||
uap->port.icount.rx++;
|
||||
|
||||
/*
|
||||
* Note that the error handling code is
|
||||
* out of the main execution path
|
||||
*/
|
||||
if (unlikely(ch & UART_DR_ERROR)) {
|
||||
if (ch & UART011_DR_BE) {
|
||||
ch &= ~(UART011_DR_FE | UART011_DR_PE);
|
||||
uap->port.icount.brk++;
|
||||
if (uart_handle_break(&uap->port))
|
||||
goto ignore_char;
|
||||
} else if (ch & UART011_DR_PE)
|
||||
uap->port.icount.parity++;
|
||||
else if (ch & UART011_DR_FE)
|
||||
uap->port.icount.frame++;
|
||||
if (ch & UART011_DR_OE)
|
||||
uap->port.icount.overrun++;
|
||||
|
||||
ch &= uap->port.read_status_mask;
|
||||
|
||||
if (ch & UART011_DR_BE)
|
||||
flag = TTY_BREAK;
|
||||
else if (ch & UART011_DR_PE)
|
||||
flag = TTY_PARITY;
|
||||
else if (ch & UART011_DR_FE)
|
||||
flag = TTY_FRAME;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(&uap->port, ch & 255))
|
||||
goto ignore_char;
|
||||
|
||||
uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag);
|
||||
|
||||
ignore_char:
|
||||
status = readw(uap->port.membase + UART01x_FR);
|
||||
}
|
||||
tty_flip_buffer_push(tty);
|
||||
return;
|
||||
}
|
||||
|
||||
static void pl011_tx_chars(struct uart_amba_port *uap)
|
||||
{
|
||||
struct circ_buf *xmit = &uap->port.info->xmit;
|
||||
int count;
|
||||
|
||||
if (uap->port.x_char) {
|
||||
writew(uap->port.x_char, uap->port.membase + UART01x_DR);
|
||||
uap->port.icount.tx++;
|
||||
uap->port.x_char = 0;
|
||||
return;
|
||||
}
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
|
||||
pl011_stop_tx(&uap->port);
|
||||
return;
|
||||
}
|
||||
|
||||
count = uap->port.fifosize >> 1;
|
||||
do {
|
||||
writew(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
uap->port.icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (--count > 0);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&uap->port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
pl011_stop_tx(&uap->port);
|
||||
}
|
||||
|
||||
static void pl011_modem_status(struct uart_amba_port *uap)
|
||||
{
|
||||
unsigned int status, delta;
|
||||
|
||||
status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
|
||||
|
||||
delta = status ^ uap->old_status;
|
||||
uap->old_status = status;
|
||||
|
||||
if (!delta)
|
||||
return;
|
||||
|
||||
if (delta & UART01x_FR_DCD)
|
||||
uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD);
|
||||
|
||||
if (delta & UART01x_FR_DSR)
|
||||
uap->port.icount.dsr++;
|
||||
|
||||
if (delta & UART01x_FR_CTS)
|
||||
uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
|
||||
|
||||
wake_up_interruptible(&uap->port.info->delta_msr_wait);
|
||||
}
|
||||
|
||||
static irqreturn_t pl011_int(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_amba_port *uap = dev_id;
|
||||
unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
|
||||
int handled = 0;
|
||||
|
||||
spin_lock(&uap->port.lock);
|
||||
|
||||
status = readw(uap->port.membase + UART011_MIS);
|
||||
if (status) {
|
||||
do {
|
||||
writew(status & ~(UART011_TXIS|UART011_RTIS|
|
||||
UART011_RXIS),
|
||||
uap->port.membase + UART011_ICR);
|
||||
|
||||
if (status & (UART011_RTIS|UART011_RXIS))
|
||||
pl011_rx_chars(uap);
|
||||
if (status & (UART011_DSRMIS|UART011_DCDMIS|
|
||||
UART011_CTSMIS|UART011_RIMIS))
|
||||
pl011_modem_status(uap);
|
||||
if (status & UART011_TXIS)
|
||||
pl011_tx_chars(uap);
|
||||
|
||||
if (pass_counter-- == 0)
|
||||
break;
|
||||
|
||||
status = readw(uap->port.membase + UART011_MIS);
|
||||
} while (status != 0);
|
||||
handled = 1;
|
||||
}
|
||||
|
||||
spin_unlock(&uap->port.lock);
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static unsigned int pl01x_tx_empty(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
unsigned int status = readw(uap->port.membase + UART01x_FR);
|
||||
return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static unsigned int pl01x_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
unsigned int result = 0;
|
||||
unsigned int status = readw(uap->port.membase + UART01x_FR);
|
||||
|
||||
#define BIT(uartbit, tiocmbit) \
|
||||
if (status & uartbit) \
|
||||
result |= tiocmbit
|
||||
|
||||
BIT(UART01x_FR_DCD, TIOCM_CAR);
|
||||
BIT(UART01x_FR_DSR, TIOCM_DSR);
|
||||
BIT(UART01x_FR_CTS, TIOCM_CTS);
|
||||
BIT(UART011_FR_RI, TIOCM_RNG);
|
||||
#undef BIT
|
||||
return result;
|
||||
}
|
||||
|
||||
static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
unsigned int cr;
|
||||
|
||||
cr = readw(uap->port.membase + UART011_CR);
|
||||
|
||||
#define BIT(tiocmbit, uartbit) \
|
||||
if (mctrl & tiocmbit) \
|
||||
cr |= uartbit; \
|
||||
else \
|
||||
cr &= ~uartbit
|
||||
|
||||
BIT(TIOCM_RTS, UART011_CR_RTS);
|
||||
BIT(TIOCM_DTR, UART011_CR_DTR);
|
||||
BIT(TIOCM_OUT1, UART011_CR_OUT1);
|
||||
BIT(TIOCM_OUT2, UART011_CR_OUT2);
|
||||
BIT(TIOCM_LOOP, UART011_CR_LBE);
|
||||
#undef BIT
|
||||
|
||||
writew(cr, uap->port.membase + UART011_CR);
|
||||
}
|
||||
|
||||
static void pl011_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
unsigned long flags;
|
||||
unsigned int lcr_h;
|
||||
|
||||
spin_lock_irqsave(&uap->port.lock, flags);
|
||||
lcr_h = readw(uap->port.membase + UART011_LCRH);
|
||||
if (break_state == -1)
|
||||
lcr_h |= UART01x_LCRH_BRK;
|
||||
else
|
||||
lcr_h &= ~UART01x_LCRH_BRK;
|
||||
writew(lcr_h, uap->port.membase + UART011_LCRH);
|
||||
spin_unlock_irqrestore(&uap->port.lock, flags);
|
||||
}
|
||||
|
||||
static int pl011_startup(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
unsigned int cr;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Try to enable the clock producer.
|
||||
*/
|
||||
retval = clk_enable(uap->clk);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
uap->port.uartclk = clk_get_rate(uap->clk);
|
||||
|
||||
/*
|
||||
* Allocate the IRQ
|
||||
*/
|
||||
retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
|
||||
if (retval)
|
||||
goto clk_dis;
|
||||
|
||||
writew(UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
|
||||
uap->port.membase + UART011_IFLS);
|
||||
|
||||
/*
|
||||
* Provoke TX FIFO interrupt into asserting.
|
||||
*/
|
||||
cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
|
||||
writew(cr, uap->port.membase + UART011_CR);
|
||||
writew(0, uap->port.membase + UART011_FBRD);
|
||||
writew(1, uap->port.membase + UART011_IBRD);
|
||||
writew(0, uap->port.membase + UART011_LCRH);
|
||||
writew(0, uap->port.membase + UART01x_DR);
|
||||
while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
|
||||
barrier();
|
||||
|
||||
cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
|
||||
writew(cr, uap->port.membase + UART011_CR);
|
||||
|
||||
/*
|
||||
* initialise the old status of the modem signals
|
||||
*/
|
||||
uap->old_status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
|
||||
|
||||
/*
|
||||
* Finally, enable interrupts
|
||||
*/
|
||||
spin_lock_irq(&uap->port.lock);
|
||||
uap->im = UART011_RXIM | UART011_RTIM;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
spin_unlock_irq(&uap->port.lock);
|
||||
|
||||
return 0;
|
||||
|
||||
clk_dis:
|
||||
clk_disable(uap->clk);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void pl011_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
unsigned long val;
|
||||
|
||||
/*
|
||||
* disable all interrupts
|
||||
*/
|
||||
spin_lock_irq(&uap->port.lock);
|
||||
uap->im = 0;
|
||||
writew(uap->im, uap->port.membase + UART011_IMSC);
|
||||
writew(0xffff, uap->port.membase + UART011_ICR);
|
||||
spin_unlock_irq(&uap->port.lock);
|
||||
|
||||
/*
|
||||
* Free the interrupt
|
||||
*/
|
||||
free_irq(uap->port.irq, uap);
|
||||
|
||||
/*
|
||||
* disable the port
|
||||
*/
|
||||
writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR);
|
||||
|
||||
/*
|
||||
* disable break condition and fifos
|
||||
*/
|
||||
val = readw(uap->port.membase + UART011_LCRH);
|
||||
val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
|
||||
writew(val, uap->port.membase + UART011_LCRH);
|
||||
|
||||
/*
|
||||
* Shut down the clock producer
|
||||
*/
|
||||
clk_disable(uap->clk);
|
||||
}
|
||||
|
||||
static void
|
||||
pl011_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int lcr_h, old_cr;
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot;
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = port->uartclk * 4 / baud;
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
lcr_h = UART01x_LCRH_WLEN_5;
|
||||
break;
|
||||
case CS6:
|
||||
lcr_h = UART01x_LCRH_WLEN_6;
|
||||
break;
|
||||
case CS7:
|
||||
lcr_h = UART01x_LCRH_WLEN_7;
|
||||
break;
|
||||
default: // CS8
|
||||
lcr_h = UART01x_LCRH_WLEN_8;
|
||||
break;
|
||||
}
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
lcr_h |= UART01x_LCRH_STP2;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
lcr_h |= UART01x_LCRH_PEN;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
lcr_h |= UART01x_LCRH_EPS;
|
||||
}
|
||||
if (port->fifosize > 1)
|
||||
lcr_h |= UART01x_LCRH_FEN;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
port->read_status_mask = UART011_DR_OE | 255;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= UART011_DR_FE | UART011_DR_PE;
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
port->read_status_mask |= UART011_DR_BE;
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= UART011_DR_FE | UART011_DR_PE;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
port->ignore_status_mask |= UART011_DR_BE;
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= UART011_DR_OE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore all characters if CREAD is not set.
|
||||
*/
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |= UART_DUMMY_DR_RX;
|
||||
|
||||
if (UART_ENABLE_MS(port, termios->c_cflag))
|
||||
pl011_enable_ms(port);
|
||||
|
||||
/* first, disable everything */
|
||||
old_cr = readw(port->membase + UART011_CR);
|
||||
writew(0, port->membase + UART011_CR);
|
||||
|
||||
/* Set baud rate */
|
||||
writew(quot & 0x3f, port->membase + UART011_FBRD);
|
||||
writew(quot >> 6, port->membase + UART011_IBRD);
|
||||
|
||||
/*
|
||||
* ----------v----------v----------v----------v-----
|
||||
* NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
|
||||
* ----------^----------^----------^----------^-----
|
||||
*/
|
||||
writew(lcr_h, port->membase + UART011_LCRH);
|
||||
writew(old_cr, port->membase + UART011_CR);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *pl011_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_AMBA ? "AMBA/PL011" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory region(s) being used by 'port'
|
||||
*/
|
||||
static void pl010_release_port(struct uart_port *port)
|
||||
{
|
||||
release_mem_region(port->mapbase, SZ_4K);
|
||||
}
|
||||
|
||||
/*
|
||||
* Request the memory region(s) being used by 'port'
|
||||
*/
|
||||
static int pl010_request_port(struct uart_port *port)
|
||||
{
|
||||
return request_mem_region(port->mapbase, SZ_4K, "uart-pl011")
|
||||
!= NULL ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void pl010_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE) {
|
||||
port->type = PORT_AMBA;
|
||||
pl010_request_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* verify the new serial_struct (for TIOCSSERIAL).
|
||||
*/
|
||||
static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
int ret = 0;
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
|
||||
ret = -EINVAL;
|
||||
if (ser->irq < 0 || ser->irq >= NR_IRQS)
|
||||
ret = -EINVAL;
|
||||
if (ser->baud_base < 9600)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops amba_pl011_pops = {
|
||||
.tx_empty = pl01x_tx_empty,
|
||||
.set_mctrl = pl011_set_mctrl,
|
||||
.get_mctrl = pl01x_get_mctrl,
|
||||
.stop_tx = pl011_stop_tx,
|
||||
.start_tx = pl011_start_tx,
|
||||
.stop_rx = pl011_stop_rx,
|
||||
.enable_ms = pl011_enable_ms,
|
||||
.break_ctl = pl011_break_ctl,
|
||||
.startup = pl011_startup,
|
||||
.shutdown = pl011_shutdown,
|
||||
.set_termios = pl011_set_termios,
|
||||
.type = pl011_type,
|
||||
.release_port = pl010_release_port,
|
||||
.request_port = pl010_request_port,
|
||||
.config_port = pl010_config_port,
|
||||
.verify_port = pl010_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_amba_port *amba_ports[UART_NR];
|
||||
|
||||
#ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE
|
||||
|
||||
static void pl011_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||
|
||||
while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
|
||||
barrier();
|
||||
writew(ch, uap->port.membase + UART01x_DR);
|
||||
}
|
||||
|
||||
static void
|
||||
pl011_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct uart_amba_port *uap = amba_ports[co->index];
|
||||
unsigned int status, old_cr, new_cr;
|
||||
|
||||
clk_enable(uap->clk);
|
||||
|
||||
/*
|
||||
* First save the CR then disable the interrupts
|
||||
*/
|
||||
old_cr = readw(uap->port.membase + UART011_CR);
|
||||
new_cr = old_cr & ~UART011_CR_CTSEN;
|
||||
new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
|
||||
writew(new_cr, uap->port.membase + UART011_CR);
|
||||
|
||||
uart_console_write(&uap->port, s, count, pl011_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore the TCR
|
||||
*/
|
||||
do {
|
||||
status = readw(uap->port.membase + UART01x_FR);
|
||||
} while (status & UART01x_FR_BUSY);
|
||||
writew(old_cr, uap->port.membase + UART011_CR);
|
||||
|
||||
clk_disable(uap->clk);
|
||||
}
|
||||
|
||||
static void __init
|
||||
pl011_console_get_options(struct uart_amba_port *uap, int *baud,
|
||||
int *parity, int *bits)
|
||||
{
|
||||
if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) {
|
||||
unsigned int lcr_h, ibrd, fbrd;
|
||||
|
||||
lcr_h = readw(uap->port.membase + UART011_LCRH);
|
||||
|
||||
*parity = 'n';
|
||||
if (lcr_h & UART01x_LCRH_PEN) {
|
||||
if (lcr_h & UART01x_LCRH_EPS)
|
||||
*parity = 'e';
|
||||
else
|
||||
*parity = 'o';
|
||||
}
|
||||
|
||||
if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7)
|
||||
*bits = 7;
|
||||
else
|
||||
*bits = 8;
|
||||
|
||||
ibrd = readw(uap->port.membase + UART011_IBRD);
|
||||
fbrd = readw(uap->port.membase + UART011_FBRD);
|
||||
|
||||
*baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init pl011_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_amba_port *uap;
|
||||
int baud = 38400;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
if (co->index >= UART_NR)
|
||||
co->index = 0;
|
||||
uap = amba_ports[co->index];
|
||||
if (!uap)
|
||||
return -ENODEV;
|
||||
|
||||
uap->port.uartclk = clk_get_rate(uap->clk);
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
pl011_console_get_options(uap, &baud, &parity, &bits);
|
||||
|
||||
return uart_set_options(&uap->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver amba_reg;
|
||||
static struct console amba_console = {
|
||||
.name = "ttyAMA",
|
||||
.write = pl011_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = pl011_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &amba_reg,
|
||||
};
|
||||
|
||||
#define AMBA_CONSOLE (&amba_console)
|
||||
#else
|
||||
#define AMBA_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver amba_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "ttyAMA",
|
||||
.dev_name = "ttyAMA",
|
||||
.major = SERIAL_AMBA_MAJOR,
|
||||
.minor = SERIAL_AMBA_MINOR,
|
||||
.nr = UART_NR,
|
||||
.cons = AMBA_CONSOLE,
|
||||
};
|
||||
|
||||
static int pl011_probe(struct amba_device *dev, void *id)
|
||||
{
|
||||
struct uart_amba_port *uap;
|
||||
void __iomem *base;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
|
||||
if (amba_ports[i] == NULL)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(amba_ports)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
uap = kmalloc(sizeof(struct uart_amba_port), GFP_KERNEL);
|
||||
if (uap == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
base = ioremap(dev->res.start, PAGE_SIZE);
|
||||
if (!base) {
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
|
||||
memset(uap, 0, sizeof(struct uart_amba_port));
|
||||
uap->clk = clk_get(&dev->dev, "UARTCLK");
|
||||
if (IS_ERR(uap->clk)) {
|
||||
ret = PTR_ERR(uap->clk);
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
uap->port.dev = &dev->dev;
|
||||
uap->port.mapbase = dev->res.start;
|
||||
uap->port.membase = base;
|
||||
uap->port.iotype = UPIO_MEM;
|
||||
uap->port.irq = dev->irq[0];
|
||||
uap->port.fifosize = 16;
|
||||
uap->port.ops = &amba_pl011_pops;
|
||||
uap->port.flags = UPF_BOOT_AUTOCONF;
|
||||
uap->port.line = i;
|
||||
|
||||
amba_ports[i] = uap;
|
||||
|
||||
amba_set_drvdata(dev, uap);
|
||||
ret = uart_add_one_port(&amba_reg, &uap->port);
|
||||
if (ret) {
|
||||
amba_set_drvdata(dev, NULL);
|
||||
amba_ports[i] = NULL;
|
||||
clk_put(uap->clk);
|
||||
unmap:
|
||||
iounmap(base);
|
||||
free:
|
||||
kfree(uap);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pl011_remove(struct amba_device *dev)
|
||||
{
|
||||
struct uart_amba_port *uap = amba_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
amba_set_drvdata(dev, NULL);
|
||||
|
||||
uart_remove_one_port(&amba_reg, &uap->port);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
|
||||
if (amba_ports[i] == uap)
|
||||
amba_ports[i] = NULL;
|
||||
|
||||
iounmap(uap->port.membase);
|
||||
clk_put(uap->clk);
|
||||
kfree(uap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amba_id pl011_ids[] __initdata = {
|
||||
{
|
||||
.id = 0x00041011,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static struct amba_driver pl011_driver = {
|
||||
.drv = {
|
||||
.name = "uart-pl011",
|
||||
},
|
||||
.id_table = pl011_ids,
|
||||
.probe = pl011_probe,
|
||||
.remove = pl011_remove,
|
||||
};
|
||||
|
||||
static int __init pl011_init(void)
|
||||
{
|
||||
int ret;
|
||||
printk(KERN_INFO "Serial: AMBA PL011 UART driver\n");
|
||||
|
||||
ret = uart_register_driver(&amba_reg);
|
||||
if (ret == 0) {
|
||||
ret = amba_driver_register(&pl011_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&amba_reg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit pl011_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&pl011_driver);
|
||||
uart_unregister_driver(&amba_reg);
|
||||
}
|
||||
|
||||
module_init(pl011_init);
|
||||
module_exit(pl011_exit);
|
||||
|
||||
MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
|
||||
MODULE_DESCRIPTION("ARM AMBA serial port driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
994
drivers/serial/atmel_serial.c
Normal file
994
drivers/serial/atmel_serial.c
Normal file
@@ -0,0 +1,994 @@
|
||||
/*
|
||||
* linux/drivers/char/atmel_serial.c
|
||||
*
|
||||
* Driver for Atmel AT91 / AT32 Serial ports
|
||||
* Copyright (C) 2003 Rick Bronson
|
||||
*
|
||||
* Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd.
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/mach/serial_at91.h>
|
||||
#include <asm/arch/board.h>
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#endif
|
||||
|
||||
#include "atmel_serial.h"
|
||||
|
||||
#if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
#ifdef CONFIG_SERIAL_ATMEL_TTYAT
|
||||
|
||||
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
|
||||
* should coexist with the 8250 driver, such as if we have an external 16C550
|
||||
* UART. */
|
||||
#define SERIAL_ATMEL_MAJOR 204
|
||||
#define MINOR_START 154
|
||||
#define ATMEL_DEVICENAME "ttyAT"
|
||||
|
||||
#else
|
||||
|
||||
/* Use device name ttyS, major 4, minor 64-68. This is the usual serial port
|
||||
* name, but it is legally reserved for the 8250 driver. */
|
||||
#define SERIAL_ATMEL_MAJOR TTY_MAJOR
|
||||
#define MINOR_START 64
|
||||
#define ATMEL_DEVICENAME "ttyS"
|
||||
|
||||
#endif
|
||||
|
||||
#define ATMEL_ISR_PASS_LIMIT 256
|
||||
|
||||
#define UART_PUT_CR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_CR)
|
||||
#define UART_GET_MR(port) __raw_readl((port)->membase + ATMEL_US_MR)
|
||||
#define UART_PUT_MR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_MR)
|
||||
#define UART_PUT_IER(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IER)
|
||||
#define UART_PUT_IDR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IDR)
|
||||
#define UART_GET_IMR(port) __raw_readl((port)->membase + ATMEL_US_IMR)
|
||||
#define UART_GET_CSR(port) __raw_readl((port)->membase + ATMEL_US_CSR)
|
||||
#define UART_GET_CHAR(port) __raw_readl((port)->membase + ATMEL_US_RHR)
|
||||
#define UART_PUT_CHAR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_THR)
|
||||
#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
|
||||
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
|
||||
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
|
||||
|
||||
// #define UART_GET_CR(port) __raw_readl((port)->membase + ATMEL_US_CR) // is write-only
|
||||
|
||||
/* PDC registers */
|
||||
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
|
||||
#define UART_GET_PTSR(port) __raw_readl((port)->membase + ATMEL_PDC_PTSR)
|
||||
|
||||
#define UART_PUT_RPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RPR)
|
||||
#define UART_GET_RPR(port) __raw_readl((port)->membase + ATMEL_PDC_RPR)
|
||||
#define UART_PUT_RCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RCR)
|
||||
#define UART_PUT_RNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNPR)
|
||||
#define UART_PUT_RNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNCR)
|
||||
|
||||
#define UART_PUT_TPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TPR)
|
||||
#define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
|
||||
//#define UART_PUT_TNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TNPR)
|
||||
//#define UART_PUT_TNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TNCR)
|
||||
|
||||
static int (*atmel_open_hook)(struct uart_port *);
|
||||
static void (*atmel_close_hook)(struct uart_port *);
|
||||
|
||||
/*
|
||||
* We wrap our port structure around the generic uart_port.
|
||||
*/
|
||||
struct atmel_uart_port {
|
||||
struct uart_port uart; /* uart */
|
||||
struct clk *clk; /* uart clock */
|
||||
unsigned short suspended; /* is port suspended? */
|
||||
};
|
||||
|
||||
static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
|
||||
|
||||
#ifdef SUPPORT_SYSRQ
|
||||
static struct console atmel_console;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
|
||||
*/
|
||||
static u_int atmel_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return (UART_GET_CSR(port) & ATMEL_US_TXEMPTY) ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set state of the modem control output lines
|
||||
*/
|
||||
static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
|
||||
{
|
||||
unsigned int control = 0;
|
||||
unsigned int mode;
|
||||
|
||||
#ifdef CONFIG_ARCH_AT91RM9200
|
||||
if (cpu_is_at91rm9200()) {
|
||||
/*
|
||||
* AT91RM9200 Errata #39: RTS0 is not internally connected to PA21.
|
||||
* We need to drive the pin manually.
|
||||
*/
|
||||
if (port->mapbase == AT91RM9200_BASE_US0) {
|
||||
if (mctrl & TIOCM_RTS)
|
||||
at91_set_gpio_value(AT91_PIN_PA21, 0);
|
||||
else
|
||||
at91_set_gpio_value(AT91_PIN_PA21, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mctrl & TIOCM_RTS)
|
||||
control |= ATMEL_US_RTSEN;
|
||||
else
|
||||
control |= ATMEL_US_RTSDIS;
|
||||
|
||||
if (mctrl & TIOCM_DTR)
|
||||
control |= ATMEL_US_DTREN;
|
||||
else
|
||||
control |= ATMEL_US_DTRDIS;
|
||||
|
||||
UART_PUT_CR(port, control);
|
||||
|
||||
/* Local loopback mode? */
|
||||
mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
|
||||
if (mctrl & TIOCM_LOOP)
|
||||
mode |= ATMEL_US_CHMODE_LOC_LOOP;
|
||||
else
|
||||
mode |= ATMEL_US_CHMODE_NORMAL;
|
||||
UART_PUT_MR(port, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get state of the modem control input lines
|
||||
*/
|
||||
static u_int atmel_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
unsigned int status, ret = 0;
|
||||
|
||||
status = UART_GET_CSR(port);
|
||||
|
||||
/*
|
||||
* The control signals are active low.
|
||||
*/
|
||||
if (!(status & ATMEL_US_DCD))
|
||||
ret |= TIOCM_CD;
|
||||
if (!(status & ATMEL_US_CTS))
|
||||
ret |= TIOCM_CTS;
|
||||
if (!(status & ATMEL_US_DSR))
|
||||
ret |= TIOCM_DSR;
|
||||
if (!(status & ATMEL_US_RI))
|
||||
ret |= TIOCM_RI;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop transmitting.
|
||||
*/
|
||||
static void atmel_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
|
||||
UART_PUT_IDR(port, ATMEL_US_TXRDY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start transmitting.
|
||||
*/
|
||||
static void atmel_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
|
||||
UART_PUT_IER(port, ATMEL_US_TXRDY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop receiving - port is in process of being closed.
|
||||
*/
|
||||
static void atmel_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
|
||||
UART_PUT_IDR(port, ATMEL_US_RXRDY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable modem status interrupts
|
||||
*/
|
||||
static void atmel_enable_ms(struct uart_port *port)
|
||||
{
|
||||
UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Control the transmission of a break signal
|
||||
*/
|
||||
static void atmel_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
if (break_state != 0)
|
||||
UART_PUT_CR(port, ATMEL_US_STTBRK); /* start break */
|
||||
else
|
||||
UART_PUT_CR(port, ATMEL_US_STPBRK); /* stop break */
|
||||
}
|
||||
|
||||
/*
|
||||
* Characters received (called from interrupt handler)
|
||||
*/
|
||||
static void atmel_rx_chars(struct uart_port *port)
|
||||
{
|
||||
struct tty_struct *tty = port->info->tty;
|
||||
unsigned int status, ch, flg;
|
||||
|
||||
status = UART_GET_CSR(port);
|
||||
while (status & ATMEL_US_RXRDY) {
|
||||
ch = UART_GET_CHAR(port);
|
||||
|
||||
port->icount.rx++;
|
||||
|
||||
flg = TTY_NORMAL;
|
||||
|
||||
/*
|
||||
* note that the error handling code is
|
||||
* out of the main execution path
|
||||
*/
|
||||
if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME | ATMEL_US_OVRE | ATMEL_US_RXBRK))) {
|
||||
UART_PUT_CR(port, ATMEL_US_RSTSTA); /* clear error */
|
||||
if (status & ATMEL_US_RXBRK) {
|
||||
status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); /* ignore side-effect */
|
||||
port->icount.brk++;
|
||||
if (uart_handle_break(port))
|
||||
goto ignore_char;
|
||||
}
|
||||
if (status & ATMEL_US_PARE)
|
||||
port->icount.parity++;
|
||||
if (status & ATMEL_US_FRAME)
|
||||
port->icount.frame++;
|
||||
if (status & ATMEL_US_OVRE)
|
||||
port->icount.overrun++;
|
||||
|
||||
status &= port->read_status_mask;
|
||||
|
||||
if (status & ATMEL_US_RXBRK)
|
||||
flg = TTY_BREAK;
|
||||
else if (status & ATMEL_US_PARE)
|
||||
flg = TTY_PARITY;
|
||||
else if (status & ATMEL_US_FRAME)
|
||||
flg = TTY_FRAME;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(port, ch))
|
||||
goto ignore_char;
|
||||
|
||||
uart_insert_char(port, status, ATMEL_US_OVRE, ch, flg);
|
||||
|
||||
ignore_char:
|
||||
status = UART_GET_CSR(port);
|
||||
}
|
||||
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transmit characters (called from interrupt handler)
|
||||
*/
|
||||
static void atmel_tx_chars(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
if (port->x_char) {
|
||||
UART_PUT_CHAR(port, port->x_char);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return;
|
||||
}
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
atmel_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
while (UART_GET_CSR(port) & ATMEL_US_TXRDY) {
|
||||
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
atmel_stop_tx(port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler
|
||||
*/
|
||||
static irqreturn_t atmel_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
unsigned int status, pending, pass_counter = 0;
|
||||
|
||||
status = UART_GET_CSR(port);
|
||||
pending = status & UART_GET_IMR(port);
|
||||
while (pending) {
|
||||
/* Interrupt receive */
|
||||
if (pending & ATMEL_US_RXRDY)
|
||||
atmel_rx_chars(port);
|
||||
|
||||
// TODO: All reads to CSR will clear these interrupts!
|
||||
if (pending & ATMEL_US_RIIC) port->icount.rng++;
|
||||
if (pending & ATMEL_US_DSRIC) port->icount.dsr++;
|
||||
if (pending & ATMEL_US_DCDIC)
|
||||
uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
|
||||
if (pending & ATMEL_US_CTSIC)
|
||||
uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
|
||||
if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC))
|
||||
wake_up_interruptible(&port->info->delta_msr_wait);
|
||||
|
||||
/* Interrupt transmit */
|
||||
if (pending & ATMEL_US_TXRDY)
|
||||
atmel_tx_chars(port);
|
||||
|
||||
if (pass_counter++ > ATMEL_ISR_PASS_LIMIT)
|
||||
break;
|
||||
|
||||
status = UART_GET_CSR(port);
|
||||
pending = status & UART_GET_IMR(port);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform initialization and enable port for reception
|
||||
*/
|
||||
static int atmel_startup(struct uart_port *port)
|
||||
{
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Ensure that no interrupts are enabled otherwise when
|
||||
* request_irq() is called we could get stuck trying to
|
||||
* handle an unexpected interrupt
|
||||
*/
|
||||
UART_PUT_IDR(port, -1);
|
||||
|
||||
/*
|
||||
* Allocate the IRQ
|
||||
*/
|
||||
retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, "atmel_serial", port);
|
||||
if (retval) {
|
||||
printk("atmel_serial: atmel_startup - Can't get irq\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a specific "open" function (to register
|
||||
* control line interrupts)
|
||||
*/
|
||||
if (atmel_open_hook) {
|
||||
retval = atmel_open_hook(port);
|
||||
if (retval) {
|
||||
free_irq(port->irq, port);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, enable the serial port
|
||||
*/
|
||||
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
|
||||
UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); /* enable xmit & rcvr */
|
||||
|
||||
UART_PUT_IER(port, ATMEL_US_RXRDY); /* enable receive only */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the port
|
||||
*/
|
||||
static void atmel_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
|
||||
/*
|
||||
* Disable all interrupts, port and break condition.
|
||||
*/
|
||||
UART_PUT_CR(port, ATMEL_US_RSTSTA);
|
||||
UART_PUT_IDR(port, -1);
|
||||
|
||||
/*
|
||||
* Free the interrupt
|
||||
*/
|
||||
free_irq(port->irq, port);
|
||||
|
||||
/*
|
||||
* If there is a specific "close" function (to unregister
|
||||
* control line interrupts)
|
||||
*/
|
||||
if (atmel_close_hook)
|
||||
atmel_close_hook(port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Power / Clock management.
|
||||
*/
|
||||
static void atmel_serial_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
|
||||
{
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
|
||||
switch (state) {
|
||||
case 0:
|
||||
/*
|
||||
* Enable the peripheral clock for this serial port.
|
||||
* This is called on uart_open() or a resume event.
|
||||
*/
|
||||
clk_enable(atmel_port->clk);
|
||||
break;
|
||||
case 3:
|
||||
/*
|
||||
* Disable the peripheral clock for this serial port.
|
||||
* This is called on uart_close() or a suspend event.
|
||||
*/
|
||||
clk_disable(atmel_port->clk);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "atmel_serial: unknown pm %d\n", state);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the port parameters
|
||||
*/
|
||||
static void atmel_set_termios(struct uart_port *port, struct ktermios * termios, struct ktermios * old)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int mode, imr, quot, baud;
|
||||
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
/* Get current mode register */
|
||||
mode = UART_GET_MR(port) & ~(ATMEL_US_CHRL | ATMEL_US_NBSTOP | ATMEL_US_PAR);
|
||||
|
||||
/* byte size */
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
mode |= ATMEL_US_CHRL_5;
|
||||
break;
|
||||
case CS6:
|
||||
mode |= ATMEL_US_CHRL_6;
|
||||
break;
|
||||
case CS7:
|
||||
mode |= ATMEL_US_CHRL_7;
|
||||
break;
|
||||
default:
|
||||
mode |= ATMEL_US_CHRL_8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* stop bits */
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
mode |= ATMEL_US_NBSTOP_2;
|
||||
|
||||
/* parity */
|
||||
if (termios->c_cflag & PARENB) {
|
||||
if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */
|
||||
if (termios->c_cflag & PARODD)
|
||||
mode |= ATMEL_US_PAR_MARK;
|
||||
else
|
||||
mode |= ATMEL_US_PAR_SPACE;
|
||||
}
|
||||
else if (termios->c_cflag & PARODD)
|
||||
mode |= ATMEL_US_PAR_ODD;
|
||||
else
|
||||
mode |= ATMEL_US_PAR_EVEN;
|
||||
}
|
||||
else
|
||||
mode |= ATMEL_US_PAR_NONE;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
port->read_status_mask = ATMEL_US_OVRE;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE);
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
port->read_status_mask |= ATMEL_US_RXBRK;
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE);
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
port->ignore_status_mask |= ATMEL_US_RXBRK;
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= ATMEL_US_OVRE;
|
||||
}
|
||||
|
||||
// TODO: Ignore all characters if CREAD is set.
|
||||
|
||||
/* update the per-port timeout */
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/* disable interrupts and drain transmitter */
|
||||
imr = UART_GET_IMR(port); /* get interrupt mask */
|
||||
UART_PUT_IDR(port, -1); /* disable all interrupts */
|
||||
while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) { barrier(); }
|
||||
|
||||
/* disable receiver and transmitter */
|
||||
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
|
||||
|
||||
/* set the parity, stop bits and data size */
|
||||
UART_PUT_MR(port, mode);
|
||||
|
||||
/* set the baud rate */
|
||||
UART_PUT_BRGR(port, quot);
|
||||
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
|
||||
UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
|
||||
|
||||
/* restore interrupts */
|
||||
UART_PUT_IER(port, imr);
|
||||
|
||||
/* CTS flow-control and modem-status interrupts */
|
||||
if (UART_ENABLE_MS(port, termios->c_cflag))
|
||||
port->ops->enable_ms(port);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return string describing the specified port
|
||||
*/
|
||||
static const char *atmel_type(struct uart_port *port)
|
||||
{
|
||||
return (port->type == PORT_ATMEL) ? "ATMEL_SERIAL" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory region(s) being used by 'port'.
|
||||
*/
|
||||
static void atmel_release_port(struct uart_port *port)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(port->dev);
|
||||
int size = pdev->resource[0].end - pdev->resource[0].start + 1;
|
||||
|
||||
release_mem_region(port->mapbase, size);
|
||||
|
||||
if (port->flags & UPF_IOREMAP) {
|
||||
iounmap(port->membase);
|
||||
port->membase = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Request the memory region(s) being used by 'port'.
|
||||
*/
|
||||
static int atmel_request_port(struct uart_port *port)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(port->dev);
|
||||
int size = pdev->resource[0].end - pdev->resource[0].start + 1;
|
||||
|
||||
if (!request_mem_region(port->mapbase, size, "atmel_serial"))
|
||||
return -EBUSY;
|
||||
|
||||
if (port->flags & UPF_IOREMAP) {
|
||||
port->membase = ioremap(port->mapbase, size);
|
||||
if (port->membase == NULL) {
|
||||
release_mem_region(port->mapbase, size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void atmel_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE) {
|
||||
port->type = PORT_ATMEL;
|
||||
atmel_request_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the new serial_struct (for TIOCSSERIAL).
|
||||
*/
|
||||
static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
int ret = 0;
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_ATMEL)
|
||||
ret = -EINVAL;
|
||||
if (port->irq != ser->irq)
|
||||
ret = -EINVAL;
|
||||
if (ser->io_type != SERIAL_IO_MEM)
|
||||
ret = -EINVAL;
|
||||
if (port->uartclk / 16 != ser->baud_base)
|
||||
ret = -EINVAL;
|
||||
if ((void *)port->mapbase != ser->iomem_base)
|
||||
ret = -EINVAL;
|
||||
if (port->iobase != ser->port)
|
||||
ret = -EINVAL;
|
||||
if (ser->hub6 != 0)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops atmel_pops = {
|
||||
.tx_empty = atmel_tx_empty,
|
||||
.set_mctrl = atmel_set_mctrl,
|
||||
.get_mctrl = atmel_get_mctrl,
|
||||
.stop_tx = atmel_stop_tx,
|
||||
.start_tx = atmel_start_tx,
|
||||
.stop_rx = atmel_stop_rx,
|
||||
.enable_ms = atmel_enable_ms,
|
||||
.break_ctl = atmel_break_ctl,
|
||||
.startup = atmel_startup,
|
||||
.shutdown = atmel_shutdown,
|
||||
.set_termios = atmel_set_termios,
|
||||
.type = atmel_type,
|
||||
.release_port = atmel_release_port,
|
||||
.request_port = atmel_request_port,
|
||||
.config_port = atmel_config_port,
|
||||
.verify_port = atmel_verify_port,
|
||||
.pm = atmel_serial_pm,
|
||||
};
|
||||
|
||||
/*
|
||||
* Configure the port from the platform device resource info.
|
||||
*/
|
||||
static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *port = &atmel_port->uart;
|
||||
struct atmel_uart_data *data = pdev->dev.platform_data;
|
||||
|
||||
port->iotype = UPIO_MEM;
|
||||
port->flags = UPF_BOOT_AUTOCONF;
|
||||
port->ops = &atmel_pops;
|
||||
port->fifosize = 1;
|
||||
port->line = pdev->id;
|
||||
port->dev = &pdev->dev;
|
||||
|
||||
port->mapbase = pdev->resource[0].start;
|
||||
port->irq = pdev->resource[1].start;
|
||||
|
||||
if (data->regs)
|
||||
/* Already mapped by setup code */
|
||||
port->membase = data->regs;
|
||||
else {
|
||||
port->flags |= UPF_IOREMAP;
|
||||
port->membase = NULL;
|
||||
}
|
||||
|
||||
if (!atmel_port->clk) { /* for console, the clock could already be configured */
|
||||
atmel_port->clk = clk_get(&pdev->dev, "usart");
|
||||
clk_enable(atmel_port->clk);
|
||||
port->uartclk = clk_get_rate(atmel_port->clk);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Register board-specific modem-control line handlers.
|
||||
*/
|
||||
void __init atmel_register_uart_fns(struct atmel_port_fns *fns)
|
||||
{
|
||||
if (fns->enable_ms)
|
||||
atmel_pops.enable_ms = fns->enable_ms;
|
||||
if (fns->get_mctrl)
|
||||
atmel_pops.get_mctrl = fns->get_mctrl;
|
||||
if (fns->set_mctrl)
|
||||
atmel_pops.set_mctrl = fns->set_mctrl;
|
||||
atmel_open_hook = fns->open;
|
||||
atmel_close_hook = fns->close;
|
||||
atmel_pops.pm = fns->pm;
|
||||
atmel_pops.set_wake = fns->set_wake;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
|
||||
static void atmel_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY))
|
||||
barrier();
|
||||
UART_PUT_CHAR(port, ch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts are disabled on entering
|
||||
*/
|
||||
static void atmel_console_write(struct console *co, const char *s, u_int count)
|
||||
{
|
||||
struct uart_port *port = &atmel_ports[co->index].uart;
|
||||
unsigned int status, imr;
|
||||
|
||||
/*
|
||||
* First, save IMR and then disable interrupts
|
||||
*/
|
||||
imr = UART_GET_IMR(port); /* get interrupt mask */
|
||||
UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
|
||||
|
||||
uart_console_write(port, s, count, atmel_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore IMR
|
||||
*/
|
||||
do {
|
||||
status = UART_GET_CSR(port);
|
||||
} while (!(status & ATMEL_US_TXRDY));
|
||||
UART_PUT_IER(port, imr); /* set interrupts back the way they were */
|
||||
}
|
||||
|
||||
/*
|
||||
* If the port was already initialised (eg, by a boot loader), try to determine
|
||||
* the current setup.
|
||||
*/
|
||||
static void __init atmel_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
|
||||
{
|
||||
unsigned int mr, quot;
|
||||
|
||||
// TODO: CR is a write-only register
|
||||
// unsigned int cr;
|
||||
//
|
||||
// cr = UART_GET_CR(port) & (ATMEL_US_RXEN | ATMEL_US_TXEN);
|
||||
// if (cr == (ATMEL_US_RXEN | ATMEL_US_TXEN)) {
|
||||
// /* ok, the port was enabled */
|
||||
// }
|
||||
|
||||
mr = UART_GET_MR(port) & ATMEL_US_CHRL;
|
||||
if (mr == ATMEL_US_CHRL_8)
|
||||
*bits = 8;
|
||||
else
|
||||
*bits = 7;
|
||||
|
||||
mr = UART_GET_MR(port) & ATMEL_US_PAR;
|
||||
if (mr == ATMEL_US_PAR_EVEN)
|
||||
*parity = 'e';
|
||||
else if (mr == ATMEL_US_PAR_ODD)
|
||||
*parity = 'o';
|
||||
|
||||
/*
|
||||
* The serial core only rounds down when matching this to a
|
||||
* supported baud rate. Make sure we don't end up slightly
|
||||
* lower than one of those, as it would make us fall through
|
||||
* to a much lower baud rate than we really want.
|
||||
*/
|
||||
quot = UART_GET_BRGR(port);
|
||||
*baud = port->uartclk / (16 * (quot - 1));
|
||||
}
|
||||
|
||||
static int __init atmel_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port = &atmel_ports[co->index].uart;
|
||||
int baud = 115200;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (port->membase == 0) /* Port not initialized yet - delay setup */
|
||||
return -ENODEV;
|
||||
|
||||
UART_PUT_IDR(port, -1); /* disable interrupts */
|
||||
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
|
||||
UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
atmel_console_get_options(port, &baud, &parity, &bits);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver atmel_uart;
|
||||
|
||||
static struct console atmel_console = {
|
||||
.name = ATMEL_DEVICENAME,
|
||||
.write = atmel_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = atmel_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &atmel_uart,
|
||||
};
|
||||
|
||||
#define ATMEL_CONSOLE_DEVICE &atmel_console
|
||||
|
||||
/*
|
||||
* Early console initialization (before VM subsystem initialized).
|
||||
*/
|
||||
static int __init atmel_console_init(void)
|
||||
{
|
||||
if (atmel_default_console_device) {
|
||||
add_preferred_console(ATMEL_DEVICENAME, atmel_default_console_device->id, NULL);
|
||||
atmel_init_port(&(atmel_ports[atmel_default_console_device->id]), atmel_default_console_device);
|
||||
register_console(&atmel_console);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
console_initcall(atmel_console_init);
|
||||
|
||||
/*
|
||||
* Late console initialization.
|
||||
*/
|
||||
static int __init atmel_late_console_init(void)
|
||||
{
|
||||
if (atmel_default_console_device && !(atmel_console.flags & CON_ENABLED))
|
||||
register_console(&atmel_console);
|
||||
|
||||
return 0;
|
||||
}
|
||||
core_initcall(atmel_late_console_init);
|
||||
|
||||
#else
|
||||
#define ATMEL_CONSOLE_DEVICE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver atmel_uart = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "atmel_serial",
|
||||
.dev_name = ATMEL_DEVICENAME,
|
||||
.major = SERIAL_ATMEL_MAJOR,
|
||||
.minor = MINOR_START,
|
||||
.nr = ATMEL_MAX_UART,
|
||||
.cons = ATMEL_CONSOLE_DEVICE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int atmel_serial_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct uart_port *port = platform_get_drvdata(pdev);
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
|
||||
if (device_may_wakeup(&pdev->dev) && !at91_suspend_entering_slow_clock())
|
||||
enable_irq_wake(port->irq);
|
||||
else {
|
||||
uart_suspend_port(&atmel_uart, port);
|
||||
atmel_port->suspended = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_serial_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *port = platform_get_drvdata(pdev);
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
|
||||
if (atmel_port->suspended) {
|
||||
uart_resume_port(&atmel_uart, port);
|
||||
atmel_port->suspended = 0;
|
||||
}
|
||||
else
|
||||
disable_irq_wake(port->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define atmel_serial_suspend NULL
|
||||
#define atmel_serial_resume NULL
|
||||
#endif
|
||||
|
||||
static int __devinit atmel_serial_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct atmel_uart_port *port;
|
||||
int ret;
|
||||
|
||||
port = &atmel_ports[pdev->id];
|
||||
atmel_init_port(port, pdev);
|
||||
|
||||
ret = uart_add_one_port(&atmel_uart, &port->uart);
|
||||
if (!ret) {
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
platform_set_drvdata(pdev, port);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit atmel_serial_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *port = platform_get_drvdata(pdev);
|
||||
struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
|
||||
int ret = 0;
|
||||
|
||||
clk_disable(atmel_port->clk);
|
||||
clk_put(atmel_port->clk);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (port) {
|
||||
ret = uart_remove_one_port(&atmel_uart, port);
|
||||
kfree(port);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver atmel_serial_driver = {
|
||||
.probe = atmel_serial_probe,
|
||||
.remove = __devexit_p(atmel_serial_remove),
|
||||
.suspend = atmel_serial_suspend,
|
||||
.resume = atmel_serial_resume,
|
||||
.driver = {
|
||||
.name = "atmel_usart",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init atmel_serial_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uart_register_driver(&atmel_uart);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&atmel_serial_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&atmel_uart);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit atmel_serial_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&atmel_serial_driver);
|
||||
uart_unregister_driver(&atmel_uart);
|
||||
}
|
||||
|
||||
module_init(atmel_serial_init);
|
||||
module_exit(atmel_serial_exit);
|
||||
|
||||
MODULE_AUTHOR("Rick Bronson");
|
||||
MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
124
drivers/serial/atmel_serial.h
Normal file
124
drivers/serial/atmel_serial.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* drivers/serial/atmel_serial.h
|
||||
*
|
||||
* Copyright (C) 2005 Ivan Kokshaysky
|
||||
* Copyright (C) SAN People
|
||||
*
|
||||
* USART registers.
|
||||
* Based on AT91RM9200 datasheet revision E.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef ATMEL_SERIAL_H
|
||||
#define ATMEL_SERIAL_H
|
||||
|
||||
#define ATMEL_US_CR 0x00 /* Control Register */
|
||||
#define ATMEL_US_RSTRX (1 << 2) /* Reset Receiver */
|
||||
#define ATMEL_US_RSTTX (1 << 3) /* Reset Transmitter */
|
||||
#define ATMEL_US_RXEN (1 << 4) /* Receiver Enable */
|
||||
#define ATMEL_US_RXDIS (1 << 5) /* Receiver Disable */
|
||||
#define ATMEL_US_TXEN (1 << 6) /* Transmitter Enable */
|
||||
#define ATMEL_US_TXDIS (1 << 7) /* Transmitter Disable */
|
||||
#define ATMEL_US_RSTSTA (1 << 8) /* Reset Status Bits */
|
||||
#define ATMEL_US_STTBRK (1 << 9) /* Start Break */
|
||||
#define ATMEL_US_STPBRK (1 << 10) /* Stop Break */
|
||||
#define ATMEL_US_STTTO (1 << 11) /* Start Time-out */
|
||||
#define ATMEL_US_SENDA (1 << 12) /* Send Address */
|
||||
#define ATMEL_US_RSTIT (1 << 13) /* Reset Iterations */
|
||||
#define ATMEL_US_RSTNACK (1 << 14) /* Reset Non Acknowledge */
|
||||
#define ATMEL_US_RETTO (1 << 15) /* Rearm Time-out */
|
||||
#define ATMEL_US_DTREN (1 << 16) /* Data Terminal Ready Enable [AT91RM9200 only] */
|
||||
#define ATMEL_US_DTRDIS (1 << 17) /* Data Terminal Ready Disable [AT91RM9200 only] */
|
||||
#define ATMEL_US_RTSEN (1 << 18) /* Request To Send Enable */
|
||||
#define ATMEL_US_RTSDIS (1 << 19) /* Request To Send Disable */
|
||||
|
||||
#define ATMEL_US_MR 0x04 /* Mode Register */
|
||||
#define ATMEL_US_USMODE (0xf << 0) /* Mode of the USART */
|
||||
#define ATMEL_US_USMODE_NORMAL 0
|
||||
#define ATMEL_US_USMODE_RS485 1
|
||||
#define ATMEL_US_USMODE_HWHS 2
|
||||
#define ATMEL_US_USMODE_MODEM 3
|
||||
#define ATMEL_US_USMODE_ISO7816_T0 4
|
||||
#define ATMEL_US_USMODE_ISO7816_T1 6
|
||||
#define ATMEL_US_USMODE_IRDA 8
|
||||
#define ATMEL_US_USCLKS (3 << 4) /* Clock Selection */
|
||||
#define ATMEL_US_CHRL (3 << 6) /* Character Length */
|
||||
#define ATMEL_US_CHRL_5 (0 << 6)
|
||||
#define ATMEL_US_CHRL_6 (1 << 6)
|
||||
#define ATMEL_US_CHRL_7 (2 << 6)
|
||||
#define ATMEL_US_CHRL_8 (3 << 6)
|
||||
#define ATMEL_US_SYNC (1 << 8) /* Synchronous Mode Select */
|
||||
#define ATMEL_US_PAR (7 << 9) /* Parity Type */
|
||||
#define ATMEL_US_PAR_EVEN (0 << 9)
|
||||
#define ATMEL_US_PAR_ODD (1 << 9)
|
||||
#define ATMEL_US_PAR_SPACE (2 << 9)
|
||||
#define ATMEL_US_PAR_MARK (3 << 9)
|
||||
#define ATMEL_US_PAR_NONE (4 << 9)
|
||||
#define ATMEL_US_PAR_MULTI_DROP (6 << 9)
|
||||
#define ATMEL_US_NBSTOP (3 << 12) /* Number of Stop Bits */
|
||||
#define ATMEL_US_NBSTOP_1 (0 << 12)
|
||||
#define ATMEL_US_NBSTOP_1_5 (1 << 12)
|
||||
#define ATMEL_US_NBSTOP_2 (2 << 12)
|
||||
#define ATMEL_US_CHMODE (3 << 14) /* Channel Mode */
|
||||
#define ATMEL_US_CHMODE_NORMAL (0 << 14)
|
||||
#define ATMEL_US_CHMODE_ECHO (1 << 14)
|
||||
#define ATMEL_US_CHMODE_LOC_LOOP (2 << 14)
|
||||
#define ATMEL_US_CHMODE_REM_LOOP (3 << 14)
|
||||
#define ATMEL_US_MSBF (1 << 16) /* Bit Order */
|
||||
#define ATMEL_US_MODE9 (1 << 17) /* 9-bit Character Length */
|
||||
#define ATMEL_US_CLKO (1 << 18) /* Clock Output Select */
|
||||
#define ATMEL_US_OVER (1 << 19) /* Oversampling Mode */
|
||||
#define ATMEL_US_INACK (1 << 20) /* Inhibit Non Acknowledge */
|
||||
#define ATMEL_US_DSNACK (1 << 21) /* Disable Successive NACK */
|
||||
#define ATMEL_US_MAX_ITER (7 << 24) /* Max Iterations */
|
||||
#define ATMEL_US_FILTER (1 << 28) /* Infrared Receive Line Filter */
|
||||
|
||||
#define ATMEL_US_IER 0x08 /* Interrupt Enable Register */
|
||||
#define ATMEL_US_RXRDY (1 << 0) /* Receiver Ready */
|
||||
#define ATMEL_US_TXRDY (1 << 1) /* Transmitter Ready */
|
||||
#define ATMEL_US_RXBRK (1 << 2) /* Break Received / End of Break */
|
||||
#define ATMEL_US_ENDRX (1 << 3) /* End of Receiver Transfer */
|
||||
#define ATMEL_US_ENDTX (1 << 4) /* End of Transmitter Transfer */
|
||||
#define ATMEL_US_OVRE (1 << 5) /* Overrun Error */
|
||||
#define ATMEL_US_FRAME (1 << 6) /* Framing Error */
|
||||
#define ATMEL_US_PARE (1 << 7) /* Parity Error */
|
||||
#define ATMEL_US_TIMEOUT (1 << 8) /* Receiver Time-out */
|
||||
#define ATMEL_US_TXEMPTY (1 << 9) /* Transmitter Empty */
|
||||
#define ATMEL_US_ITERATION (1 << 10) /* Max number of Repetitions Reached */
|
||||
#define ATMEL_US_TXBUFE (1 << 11) /* Transmission Buffer Empty */
|
||||
#define ATMEL_US_RXBUFF (1 << 12) /* Reception Buffer Full */
|
||||
#define ATMEL_US_NACK (1 << 13) /* Non Acknowledge */
|
||||
#define ATMEL_US_RIIC (1 << 16) /* Ring Indicator Input Change [AT91RM9200 only] */
|
||||
#define ATMEL_US_DSRIC (1 << 17) /* Data Set Ready Input Change [AT91RM9200 only] */
|
||||
#define ATMEL_US_DCDIC (1 << 18) /* Data Carrier Detect Input Change [AT91RM9200 only] */
|
||||
#define ATMEL_US_CTSIC (1 << 19) /* Clear to Send Input Change */
|
||||
#define ATMEL_US_RI (1 << 20) /* RI */
|
||||
#define ATMEL_US_DSR (1 << 21) /* DSR */
|
||||
#define ATMEL_US_DCD (1 << 22) /* DCD */
|
||||
#define ATMEL_US_CTS (1 << 23) /* CTS */
|
||||
|
||||
#define ATMEL_US_IDR 0x0c /* Interrupt Disable Register */
|
||||
#define ATMEL_US_IMR 0x10 /* Interrupt Mask Register */
|
||||
#define ATMEL_US_CSR 0x14 /* Channel Status Register */
|
||||
#define ATMEL_US_RHR 0x18 /* Receiver Holding Register */
|
||||
#define ATMEL_US_THR 0x1c /* Transmitter Holding Register */
|
||||
#define ATMEL_US_SYNH (1 << 15) /* Transmit/Receive Sync [AT91SAM9261 only] */
|
||||
|
||||
#define ATMEL_US_BRGR 0x20 /* Baud Rate Generator Register */
|
||||
#define ATMEL_US_CD (0xffff << 0) /* Clock Divider */
|
||||
|
||||
#define ATMEL_US_RTOR 0x24 /* Receiver Time-out Register */
|
||||
#define ATMEL_US_TO (0xffff << 0) /* Time-out Value */
|
||||
|
||||
#define ATMEL_US_TTGR 0x28 /* Transmitter Timeguard Register */
|
||||
#define ATMEL_US_TG (0xff << 0) /* Timeguard Value */
|
||||
|
||||
#define ATMEL_US_FIDI 0x40 /* FI DI Ratio Register */
|
||||
#define ATMEL_US_NER 0x44 /* Number of Errors Register */
|
||||
#define ATMEL_US_IF 0x4c /* IrDA Filter Register */
|
||||
|
||||
#endif
|
||||
582
drivers/serial/clps711x.c
Normal file
582
drivers/serial/clps711x.c
Normal file
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* linux/drivers/char/clps711x.c
|
||||
*
|
||||
* Driver for CLPS711x serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* Copyright 1999 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Id: clps711x.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/hardware/clps7111.h>
|
||||
|
||||
#define UART_NR 2
|
||||
|
||||
#define SERIAL_CLPS711X_MAJOR 204
|
||||
#define SERIAL_CLPS711X_MINOR 40
|
||||
#define SERIAL_CLPS711X_NR UART_NR
|
||||
|
||||
/*
|
||||
* We use the relevant SYSCON register as a base address for these ports.
|
||||
*/
|
||||
#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1)
|
||||
#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1)
|
||||
#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1)
|
||||
#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1)
|
||||
|
||||
#define TX_IRQ(port) ((port)->irq)
|
||||
#define RX_IRQ(port) ((port)->irq + 1)
|
||||
|
||||
#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)
|
||||
|
||||
#define tx_enabled(port) ((port)->unused[0])
|
||||
|
||||
static void clps711xuart_stop_tx(struct uart_port *port)
|
||||
{
|
||||
if (tx_enabled(port)) {
|
||||
disable_irq(TX_IRQ(port));
|
||||
tx_enabled(port) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void clps711xuart_start_tx(struct uart_port *port)
|
||||
{
|
||||
if (!tx_enabled(port)) {
|
||||
enable_irq(TX_IRQ(port));
|
||||
tx_enabled(port) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void clps711xuart_stop_rx(struct uart_port *port)
|
||||
{
|
||||
disable_irq(RX_IRQ(port));
|
||||
}
|
||||
|
||||
static void clps711xuart_enable_ms(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
struct tty_struct *tty = port->info->tty;
|
||||
unsigned int status, ch, flg;
|
||||
|
||||
status = clps_readl(SYSFLG(port));
|
||||
while (!(status & SYSFLG_URXFE)) {
|
||||
ch = clps_readl(UARTDR(port));
|
||||
|
||||
port->icount.rx++;
|
||||
|
||||
flg = TTY_NORMAL;
|
||||
|
||||
/*
|
||||
* Note that the error handling code is
|
||||
* out of the main execution path
|
||||
*/
|
||||
if (unlikely(ch & UART_ANY_ERR)) {
|
||||
if (ch & UARTDR_PARERR)
|
||||
port->icount.parity++;
|
||||
else if (ch & UARTDR_FRMERR)
|
||||
port->icount.frame++;
|
||||
if (ch & UARTDR_OVERR)
|
||||
port->icount.overrun++;
|
||||
|
||||
ch &= port->read_status_mask;
|
||||
|
||||
if (ch & UARTDR_PARERR)
|
||||
flg = TTY_PARITY;
|
||||
else if (ch & UARTDR_FRMERR)
|
||||
flg = TTY_FRAME;
|
||||
|
||||
#ifdef SUPPORT_SYSRQ
|
||||
port->sysrq = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(port, ch))
|
||||
goto ignore_char;
|
||||
|
||||
/*
|
||||
* CHECK: does overrun affect the current character?
|
||||
* ASSUMPTION: it does not.
|
||||
*/
|
||||
uart_insert_char(port, ch, UARTDR_OVERR, ch, flg);
|
||||
|
||||
ignore_char:
|
||||
status = clps_readl(SYSFLG(port));
|
||||
}
|
||||
tty_flip_buffer_push(tty);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
int count;
|
||||
|
||||
if (port->x_char) {
|
||||
clps_writel(port->x_char, UARTDR(port));
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
clps711xuart_stop_tx(port);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
count = port->fifosize >> 1;
|
||||
do {
|
||||
clps_writel(xmit->buf[xmit->tail], UARTDR(port));
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (--count > 0);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
clps711xuart_stop_tx(port);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int clps711xuart_tx_empty(struct uart_port *port)
|
||||
{
|
||||
unsigned int status = clps_readl(SYSFLG(port));
|
||||
return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
unsigned int port_addr;
|
||||
unsigned int result = 0;
|
||||
unsigned int status;
|
||||
|
||||
port_addr = SYSFLG(port);
|
||||
if (port_addr == SYSFLG1) {
|
||||
status = clps_readl(SYSFLG1);
|
||||
if (status & SYSFLG1_DCD)
|
||||
result |= TIOCM_CAR;
|
||||
if (status & SYSFLG1_DSR)
|
||||
result |= TIOCM_DSR;
|
||||
if (status & SYSFLG1_CTS)
|
||||
result |= TIOCM_CTS;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
static void clps711xuart_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int ubrlcr;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
ubrlcr = clps_readl(UBRLCR(port));
|
||||
if (break_state == -1)
|
||||
ubrlcr |= UBRLCR_BREAK;
|
||||
else
|
||||
ubrlcr &= ~UBRLCR_BREAK;
|
||||
clps_writel(ubrlcr, UBRLCR(port));
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int clps711xuart_startup(struct uart_port *port)
|
||||
{
|
||||
unsigned int syscon;
|
||||
int retval;
|
||||
|
||||
tx_enabled(port) = 1;
|
||||
|
||||
/*
|
||||
* Allocate the IRQs
|
||||
*/
|
||||
retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,
|
||||
"clps711xuart_tx", port);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,
|
||||
"clps711xuart_rx", port);
|
||||
if (retval) {
|
||||
free_irq(TX_IRQ(port), port);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* enable the port
|
||||
*/
|
||||
syscon = clps_readl(SYSCON(port));
|
||||
syscon |= SYSCON_UARTEN;
|
||||
clps_writel(syscon, SYSCON(port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clps711xuart_shutdown(struct uart_port *port)
|
||||
{
|
||||
unsigned int ubrlcr, syscon;
|
||||
|
||||
/*
|
||||
* Free the interrupt
|
||||
*/
|
||||
free_irq(TX_IRQ(port), port); /* TX interrupt */
|
||||
free_irq(RX_IRQ(port), port); /* RX interrupt */
|
||||
|
||||
/*
|
||||
* disable the port
|
||||
*/
|
||||
syscon = clps_readl(SYSCON(port));
|
||||
syscon &= ~SYSCON_UARTEN;
|
||||
clps_writel(syscon, SYSCON(port));
|
||||
|
||||
/*
|
||||
* disable break condition and fifos
|
||||
*/
|
||||
ubrlcr = clps_readl(UBRLCR(port));
|
||||
ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);
|
||||
clps_writel(ubrlcr, UBRLCR(port));
|
||||
}
|
||||
|
||||
static void
|
||||
clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int ubrlcr, baud, quot;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* We don't implement CREAD.
|
||||
*/
|
||||
termios->c_cflag |= CREAD;
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
ubrlcr = UBRLCR_WRDLEN5;
|
||||
break;
|
||||
case CS6:
|
||||
ubrlcr = UBRLCR_WRDLEN6;
|
||||
break;
|
||||
case CS7:
|
||||
ubrlcr = UBRLCR_WRDLEN7;
|
||||
break;
|
||||
default: // CS8
|
||||
ubrlcr = UBRLCR_WRDLEN8;
|
||||
break;
|
||||
}
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
ubrlcr |= UBRLCR_XSTOP;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
ubrlcr |= UBRLCR_PRTEN;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
ubrlcr |= UBRLCR_EVENPRT;
|
||||
}
|
||||
if (port->fifosize > 1)
|
||||
ubrlcr |= UBRLCR_FIFOEN;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
port->read_status_mask = UARTDR_OVERR;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns to (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= UARTDR_OVERR;
|
||||
}
|
||||
|
||||
quot -= 1;
|
||||
|
||||
clps_writel(ubrlcr | quot, UBRLCR(port));
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *clps711xuart_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void clps711xuart_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE)
|
||||
port->type = PORT_CLPS711X;
|
||||
}
|
||||
|
||||
static void clps711xuart_release_port(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static int clps711xuart_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct uart_ops clps711x_pops = {
|
||||
.tx_empty = clps711xuart_tx_empty,
|
||||
.set_mctrl = clps711xuart_set_mctrl_null,
|
||||
.get_mctrl = clps711xuart_get_mctrl,
|
||||
.stop_tx = clps711xuart_stop_tx,
|
||||
.start_tx = clps711xuart_start_tx,
|
||||
.stop_rx = clps711xuart_stop_rx,
|
||||
.enable_ms = clps711xuart_enable_ms,
|
||||
.break_ctl = clps711xuart_break_ctl,
|
||||
.startup = clps711xuart_startup,
|
||||
.shutdown = clps711xuart_shutdown,
|
||||
.set_termios = clps711xuart_set_termios,
|
||||
.type = clps711xuart_type,
|
||||
.config_port = clps711xuart_config_port,
|
||||
.release_port = clps711xuart_release_port,
|
||||
.request_port = clps711xuart_request_port,
|
||||
};
|
||||
|
||||
static struct uart_port clps711x_ports[UART_NR] = {
|
||||
{
|
||||
.iobase = SYSCON1,
|
||||
.irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */
|
||||
.uartclk = 3686400,
|
||||
.fifosize = 16,
|
||||
.ops = &clps711x_pops,
|
||||
.line = 0,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
},
|
||||
{
|
||||
.iobase = SYSCON2,
|
||||
.irq = IRQ_UTXINT2, /* IRQ_URXINT2 */
|
||||
.uartclk = 3686400,
|
||||
.fifosize = 16,
|
||||
.ops = &clps711x_pops,
|
||||
.line = 1,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
|
||||
static void clps711xuart_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF)
|
||||
barrier();
|
||||
clps_writel(ch, UARTDR(port));
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a string to the serial port trying not to disturb
|
||||
* any possible real use of the port...
|
||||
*
|
||||
* The console_lock must be held when we get here.
|
||||
*
|
||||
* Note that this is called with interrupts already disabled
|
||||
*/
|
||||
static void
|
||||
clps711xuart_console_write(struct console *co, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
struct uart_port *port = clps711x_ports + co->index;
|
||||
unsigned int status, syscon;
|
||||
|
||||
/*
|
||||
* Ensure that the port is enabled.
|
||||
*/
|
||||
syscon = clps_readl(SYSCON(port));
|
||||
clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
|
||||
|
||||
uart_console_write(port, s, count, clps711xuart_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore the uart state.
|
||||
*/
|
||||
do {
|
||||
status = clps_readl(SYSFLG(port));
|
||||
} while (status & SYSFLG_UBUSY);
|
||||
|
||||
clps_writel(syscon, SYSCON(port));
|
||||
}
|
||||
|
||||
static void __init
|
||||
clps711xuart_console_get_options(struct uart_port *port, int *baud,
|
||||
int *parity, int *bits)
|
||||
{
|
||||
if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
|
||||
unsigned int ubrlcr, quot;
|
||||
|
||||
ubrlcr = clps_readl(UBRLCR(port));
|
||||
|
||||
*parity = 'n';
|
||||
if (ubrlcr & UBRLCR_PRTEN) {
|
||||
if (ubrlcr & UBRLCR_EVENPRT)
|
||||
*parity = 'e';
|
||||
else
|
||||
*parity = 'o';
|
||||
}
|
||||
|
||||
if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
|
||||
*bits = 7;
|
||||
else
|
||||
*bits = 8;
|
||||
|
||||
quot = ubrlcr & UBRLCR_BAUD_MASK;
|
||||
*baud = port->uartclk / (16 * (quot + 1));
|
||||
}
|
||||
}
|
||||
|
||||
static int __init clps711xuart_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int baud = 38400;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
port = uart_get_console(clps711x_ports, UART_NR, co);
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
clps711xuart_console_get_options(port, &baud, &parity, &bits);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver clps711x_reg;
|
||||
static struct console clps711x_console = {
|
||||
.name = "ttyCL",
|
||||
.write = clps711xuart_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = clps711xuart_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &clps711x_reg,
|
||||
};
|
||||
|
||||
static int __init clps711xuart_console_init(void)
|
||||
{
|
||||
register_console(&clps711x_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(clps711xuart_console_init);
|
||||
|
||||
#define CLPS711X_CONSOLE &clps711x_console
|
||||
#else
|
||||
#define CLPS711X_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver clps711x_reg = {
|
||||
.driver_name = "ttyCL",
|
||||
.dev_name = "ttyCL",
|
||||
.major = SERIAL_CLPS711X_MAJOR,
|
||||
.minor = SERIAL_CLPS711X_MINOR,
|
||||
.nr = UART_NR,
|
||||
|
||||
.cons = CLPS711X_CONSOLE,
|
||||
};
|
||||
|
||||
static int __init clps711xuart_init(void)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.1.1.1 $\n");
|
||||
|
||||
ret = uart_register_driver(&clps711x_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < UART_NR; i++)
|
||||
uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit clps711xuart_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < UART_NR; i++)
|
||||
uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
|
||||
|
||||
uart_unregister_driver(&clps711x_reg);
|
||||
}
|
||||
|
||||
module_init(clps711xuart_init);
|
||||
module_exit(clps711xuart_exit);
|
||||
|
||||
MODULE_AUTHOR("Deep Blue Solutions Ltd");
|
||||
MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.1.1.1 $");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);
|
||||
11
drivers/serial/cpm_uart/Makefile
Normal file
11
drivers/serial/cpm_uart/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# Makefile for the Motorola 8xx FEC ethernet controller
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o
|
||||
|
||||
# Select the correct platform objects.
|
||||
cpm_uart-objs-$(CONFIG_CPM2) += cpm_uart_cpm2.o
|
||||
cpm_uart-objs-$(CONFIG_8xx) += cpm_uart_cpm1.o
|
||||
|
||||
cpm_uart-objs := cpm_uart_core.o $(cpm_uart-objs-y)
|
||||
136
drivers/serial/cpm_uart/cpm_uart.h
Normal file
136
drivers/serial/cpm_uart/cpm_uart.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* linux/drivers/serial/cpm_uart.h
|
||||
*
|
||||
* Driver for CPM (SCC/SMC) serial ports
|
||||
*
|
||||
* Copyright (C) 2004 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 2006 (c) MontaVista Software, Inc.
|
||||
* Vitaly Bordug <vbordug@ru.mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*
|
||||
*/
|
||||
#ifndef CPM_UART_H
|
||||
#define CPM_UART_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fs_uart_pd.h>
|
||||
|
||||
#if defined(CONFIG_CPM2)
|
||||
#include "cpm_uart_cpm2.h"
|
||||
#elif defined(CONFIG_8xx)
|
||||
#include "cpm_uart_cpm1.h"
|
||||
#endif
|
||||
|
||||
#define SERIAL_CPM_MAJOR 204
|
||||
#define SERIAL_CPM_MINOR 46
|
||||
|
||||
#define IS_SMC(pinfo) (pinfo->flags & FLAG_SMC)
|
||||
#define IS_DISCARDING(pinfo) (pinfo->flags & FLAG_DISCARDING)
|
||||
#define FLAG_DISCARDING 0x00000004 /* when set, don't discard */
|
||||
#define FLAG_SMC 0x00000002
|
||||
#define FLAG_CONSOLE 0x00000001
|
||||
|
||||
#define UART_SMC1 fsid_smc1_uart
|
||||
#define UART_SMC2 fsid_smc2_uart
|
||||
#define UART_SCC1 fsid_scc1_uart
|
||||
#define UART_SCC2 fsid_scc2_uart
|
||||
#define UART_SCC3 fsid_scc3_uart
|
||||
#define UART_SCC4 fsid_scc4_uart
|
||||
|
||||
#define UART_NR fs_uart_nr
|
||||
|
||||
#define RX_NUM_FIFO 4
|
||||
#define RX_BUF_SIZE 32
|
||||
#define TX_NUM_FIFO 4
|
||||
#define TX_BUF_SIZE 32
|
||||
|
||||
#define SCC_WAIT_CLOSING 100
|
||||
|
||||
struct uart_cpm_port {
|
||||
struct uart_port port;
|
||||
u16 rx_nrfifos;
|
||||
u16 rx_fifosize;
|
||||
u16 tx_nrfifos;
|
||||
u16 tx_fifosize;
|
||||
smc_t *smcp;
|
||||
smc_uart_t *smcup;
|
||||
scc_t *sccp;
|
||||
scc_uart_t *sccup;
|
||||
volatile cbd_t *rx_bd_base;
|
||||
volatile cbd_t *rx_cur;
|
||||
volatile cbd_t *tx_bd_base;
|
||||
volatile cbd_t *tx_cur;
|
||||
unsigned char *tx_buf;
|
||||
unsigned char *rx_buf;
|
||||
u32 flags;
|
||||
void (*set_lineif)(struct uart_cpm_port *);
|
||||
u8 brg;
|
||||
uint dp_addr;
|
||||
void *mem_addr;
|
||||
dma_addr_t dma_addr;
|
||||
u32 mem_size;
|
||||
/* helpers */
|
||||
int baud;
|
||||
int bits;
|
||||
/* Keep track of 'odd' SMC2 wirings */
|
||||
int is_portb;
|
||||
/* wait on close if needed */
|
||||
int wait_closing;
|
||||
};
|
||||
|
||||
extern int cpm_uart_port_map[UART_NR];
|
||||
extern int cpm_uart_nr;
|
||||
extern struct uart_cpm_port cpm_uart_ports[UART_NR];
|
||||
|
||||
/* these are located in their respective files */
|
||||
void cpm_line_cr_cmd(int line, int cmd);
|
||||
int __init cpm_uart_init_portdesc(void);
|
||||
int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con);
|
||||
void cpm_uart_freebuf(struct uart_cpm_port *pinfo);
|
||||
|
||||
void smc1_lineif(struct uart_cpm_port *pinfo);
|
||||
void smc2_lineif(struct uart_cpm_port *pinfo);
|
||||
void scc1_lineif(struct uart_cpm_port *pinfo);
|
||||
void scc2_lineif(struct uart_cpm_port *pinfo);
|
||||
void scc3_lineif(struct uart_cpm_port *pinfo);
|
||||
void scc4_lineif(struct uart_cpm_port *pinfo);
|
||||
|
||||
/*
|
||||
virtual to phys transtalion
|
||||
*/
|
||||
static inline unsigned long cpu2cpm_addr(void* addr, struct uart_cpm_port *pinfo)
|
||||
{
|
||||
int offset;
|
||||
u32 val = (u32)addr;
|
||||
/* sane check */
|
||||
if (likely((val >= (u32)pinfo->mem_addr)) &&
|
||||
(val<((u32)pinfo->mem_addr + pinfo->mem_size))) {
|
||||
offset = val - (u32)pinfo->mem_addr;
|
||||
return pinfo->dma_addr+offset;
|
||||
}
|
||||
/* something nasty happened */
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void *cpm2cpu_addr(unsigned long addr, struct uart_cpm_port *pinfo)
|
||||
{
|
||||
int offset;
|
||||
u32 val = addr;
|
||||
/* sane check */
|
||||
if (likely((val >= pinfo->dma_addr) &&
|
||||
(val<(pinfo->dma_addr + pinfo->mem_size)))) {
|
||||
offset = val - (u32)pinfo->dma_addr;
|
||||
return (void*)(pinfo->mem_addr+offset);
|
||||
}
|
||||
/* something nasty happened */
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif /* CPM_UART_H */
|
||||
1389
drivers/serial/cpm_uart/cpm_uart_core.c
Normal file
1389
drivers/serial/cpm_uart/cpm_uart_core.c
Normal file
File diff suppressed because it is too large
Load Diff
281
drivers/serial/cpm_uart/cpm_uart_cpm1.c
Normal file
281
drivers/serial/cpm_uart/cpm_uart_cpm1.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* linux/drivers/serial/cpm_uart.c
|
||||
*
|
||||
* Driver for CPM (SCC/SMC) serial ports; CPM1 definitions
|
||||
*
|
||||
* Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
|
||||
* Pantelis Antoniou (panto@intracom.gr) (CPM1)
|
||||
*
|
||||
* Copyright (C) 2004 Freescale Semiconductor, Inc.
|
||||
* (C) 2004 Intracom, S.A.
|
||||
* (C) 2006 MontaVista Software, Inc.
|
||||
* Vitaly Bordug <vbordug@ru.mvista.com>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/fs_pd.h>
|
||||
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "cpm_uart.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
void cpm_line_cr_cmd(int line, int cmd)
|
||||
{
|
||||
ushort val;
|
||||
volatile cpm8xx_t *cp = cpmp;
|
||||
|
||||
switch (line) {
|
||||
case UART_SMC1:
|
||||
val = mk_cr_cmd(CPM_CR_CH_SMC1, cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SMC2:
|
||||
val = mk_cr_cmd(CPM_CR_CH_SMC2, cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SCC1:
|
||||
val = mk_cr_cmd(CPM_CR_CH_SCC1, cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SCC2:
|
||||
val = mk_cr_cmd(CPM_CR_CH_SCC2, cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SCC3:
|
||||
val = mk_cr_cmd(CPM_CR_CH_SCC3, cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SCC4:
|
||||
val = mk_cr_cmd(CPM_CR_CH_SCC4, cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
}
|
||||
cp->cp_cpcr = val;
|
||||
while (cp->cp_cpcr & CPM_CR_FLG) ;
|
||||
}
|
||||
|
||||
void smc1_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
pinfo->brg = 1;
|
||||
}
|
||||
|
||||
void smc2_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
pinfo->brg = 2;
|
||||
}
|
||||
|
||||
void scc1_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
/* XXX SCC1: insert port configuration here */
|
||||
pinfo->brg = 1;
|
||||
}
|
||||
|
||||
void scc2_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
/* XXX SCC2: insert port configuration here */
|
||||
pinfo->brg = 2;
|
||||
}
|
||||
|
||||
void scc3_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
/* XXX SCC3: insert port configuration here */
|
||||
pinfo->brg = 3;
|
||||
}
|
||||
|
||||
void scc4_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
/* XXX SCC4: insert port configuration here */
|
||||
pinfo->brg = 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate DP-Ram and memory buffers. We need to allocate a transmit and
|
||||
* receive buffer descriptors from dual port ram, and a character
|
||||
* buffer area from host mem. If we are allocating for the console we need
|
||||
* to do it from bootmem
|
||||
*/
|
||||
int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con)
|
||||
{
|
||||
int dpmemsz, memsz;
|
||||
u8 *dp_mem;
|
||||
uint dp_offset;
|
||||
u8 *mem_addr;
|
||||
dma_addr_t dma_addr = 0;
|
||||
|
||||
pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line);
|
||||
|
||||
dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos);
|
||||
dp_offset = cpm_dpalloc(dpmemsz, 8);
|
||||
if (IS_DPERR(dp_offset)) {
|
||||
printk(KERN_ERR
|
||||
"cpm_uart_cpm1.c: could not allocate buffer descriptors\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dp_mem = cpm_dpram_addr(dp_offset);
|
||||
|
||||
memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
|
||||
L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
|
||||
if (is_con) {
|
||||
/* was hostalloc but changed cause it blows away the */
|
||||
/* large tlb mapping when pinning the kernel area */
|
||||
mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8));
|
||||
dma_addr = (u32)cpm_dpram_phys(mem_addr);
|
||||
} else
|
||||
mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (mem_addr == NULL) {
|
||||
cpm_dpfree(dp_offset);
|
||||
printk(KERN_ERR
|
||||
"cpm_uart_cpm1.c: could not allocate coherent memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pinfo->dp_addr = dp_offset;
|
||||
pinfo->mem_addr = mem_addr; /* virtual address*/
|
||||
pinfo->dma_addr = dma_addr; /* physical address*/
|
||||
pinfo->mem_size = memsz;
|
||||
|
||||
pinfo->rx_buf = mem_addr;
|
||||
pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos
|
||||
* pinfo->rx_fifosize);
|
||||
|
||||
pinfo->rx_bd_base = (volatile cbd_t *)dp_mem;
|
||||
pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cpm_uart_freebuf(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
dma_free_coherent(NULL, L1_CACHE_ALIGN(pinfo->rx_nrfifos *
|
||||
pinfo->rx_fifosize) +
|
||||
L1_CACHE_ALIGN(pinfo->tx_nrfifos *
|
||||
pinfo->tx_fifosize), pinfo->mem_addr,
|
||||
pinfo->dma_addr);
|
||||
|
||||
cpm_dpfree(pinfo->dp_addr);
|
||||
}
|
||||
|
||||
/* Setup any dynamic params in the uart desc */
|
||||
int __init cpm_uart_init_portdesc(void)
|
||||
{
|
||||
pr_debug("CPM uart[-]:init portdesc\n");
|
||||
|
||||
cpm_uart_nr = 0;
|
||||
#ifdef CONFIG_SERIAL_CPM_SMC1
|
||||
cpm_uart_ports[UART_SMC1].smcp = &cpmp->cp_smc[0];
|
||||
/*
|
||||
* Is SMC1 being relocated?
|
||||
*/
|
||||
# ifdef CONFIG_I2C_SPI_SMC1_UCODE_PATCH
|
||||
cpm_uart_ports[UART_SMC1].smcup =
|
||||
(smc_uart_t *) & cpmp->cp_dparam[0x3C0];
|
||||
# else
|
||||
cpm_uart_ports[UART_SMC1].smcup =
|
||||
(smc_uart_t *) & cpmp->cp_dparam[PROFF_SMC1];
|
||||
# endif
|
||||
cpm_uart_ports[UART_SMC1].port.mapbase =
|
||||
(unsigned long)&cpmp->cp_smc[0];
|
||||
cpm_uart_ports[UART_SMC1].smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
|
||||
cpm_uart_ports[UART_SMC1].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
|
||||
cpm_uart_ports[UART_SMC1].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SMC1;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SMC2
|
||||
cpm_uart_ports[UART_SMC2].smcp = &cpmp->cp_smc[1];
|
||||
cpm_uart_ports[UART_SMC2].smcup =
|
||||
(smc_uart_t *) & cpmp->cp_dparam[PROFF_SMC2];
|
||||
cpm_uart_ports[UART_SMC2].port.mapbase =
|
||||
(unsigned long)&cpmp->cp_smc[1];
|
||||
cpm_uart_ports[UART_SMC2].smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
|
||||
cpm_uart_ports[UART_SMC2].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
|
||||
cpm_uart_ports[UART_SMC2].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SMC2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SCC1
|
||||
cpm_uart_ports[UART_SCC1].sccp = &cpmp->cp_scc[0];
|
||||
cpm_uart_ports[UART_SCC1].sccup =
|
||||
(scc_uart_t *) & cpmp->cp_dparam[PROFF_SCC1];
|
||||
cpm_uart_ports[UART_SCC1].port.mapbase =
|
||||
(unsigned long)&cpmp->cp_scc[0];
|
||||
cpm_uart_ports[UART_SCC1].sccp->scc_sccm &=
|
||||
~(UART_SCCM_TX | UART_SCCM_RX);
|
||||
cpm_uart_ports[UART_SCC1].sccp->scc_gsmrl &=
|
||||
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
cpm_uart_ports[UART_SCC1].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC1;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SCC2
|
||||
cpm_uart_ports[UART_SCC2].sccp = &cpmp->cp_scc[1];
|
||||
cpm_uart_ports[UART_SCC2].sccup =
|
||||
(scc_uart_t *) & cpmp->cp_dparam[PROFF_SCC2];
|
||||
cpm_uart_ports[UART_SCC2].port.mapbase =
|
||||
(unsigned long)&cpmp->cp_scc[1];
|
||||
cpm_uart_ports[UART_SCC2].sccp->scc_sccm &=
|
||||
~(UART_SCCM_TX | UART_SCCM_RX);
|
||||
cpm_uart_ports[UART_SCC2].sccp->scc_gsmrl &=
|
||||
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
cpm_uart_ports[UART_SCC2].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SCC3
|
||||
cpm_uart_ports[UART_SCC3].sccp = &cpmp->cp_scc[2];
|
||||
cpm_uart_ports[UART_SCC3].sccup =
|
||||
(scc_uart_t *) & cpmp->cp_dparam[PROFF_SCC3];
|
||||
cpm_uart_ports[UART_SCC3].port.mapbase =
|
||||
(unsigned long)&cpmp->cp_scc[2];
|
||||
cpm_uart_ports[UART_SCC3].sccp->scc_sccm &=
|
||||
~(UART_SCCM_TX | UART_SCCM_RX);
|
||||
cpm_uart_ports[UART_SCC3].sccp->scc_gsmrl &=
|
||||
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
cpm_uart_ports[UART_SCC3].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC3;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SCC4
|
||||
cpm_uart_ports[UART_SCC4].sccp = &cpmp->cp_scc[3];
|
||||
cpm_uart_ports[UART_SCC4].sccup =
|
||||
(scc_uart_t *) & cpmp->cp_dparam[PROFF_SCC4];
|
||||
cpm_uart_ports[UART_SCC4].port.mapbase =
|
||||
(unsigned long)&cpmp->cp_scc[3];
|
||||
cpm_uart_ports[UART_SCC4].sccp->scc_sccm &=
|
||||
~(UART_SCCM_TX | UART_SCCM_RX);
|
||||
cpm_uart_ports[UART_SCC4].sccp->scc_gsmrl &=
|
||||
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
cpm_uart_ports[UART_SCC4].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC4;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
42
drivers/serial/cpm_uart/cpm_uart_cpm1.h
Normal file
42
drivers/serial/cpm_uart/cpm_uart_cpm1.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* linux/drivers/serial/cpm_uart/cpm_uart_cpm1.h
|
||||
*
|
||||
* Driver for CPM (SCC/SMC) serial ports
|
||||
*
|
||||
* definitions for cpm1
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPM_UART_CPM1_H
|
||||
#define CPM_UART_CPM1_H
|
||||
|
||||
#include <asm/commproc.h>
|
||||
|
||||
/* defines for IRQs */
|
||||
#define SMC1_IRQ (CPM_IRQ_OFFSET + CPMVEC_SMC1)
|
||||
#define SMC2_IRQ (CPM_IRQ_OFFSET + CPMVEC_SMC2)
|
||||
#define SCC1_IRQ (CPM_IRQ_OFFSET + CPMVEC_SCC1)
|
||||
#define SCC2_IRQ (CPM_IRQ_OFFSET + CPMVEC_SCC2)
|
||||
#define SCC3_IRQ (CPM_IRQ_OFFSET + CPMVEC_SCC3)
|
||||
#define SCC4_IRQ (CPM_IRQ_OFFSET + CPMVEC_SCC4)
|
||||
|
||||
static inline void cpm_set_brg(int brg, int baud)
|
||||
{
|
||||
cpm_setbrg(brg, baud);
|
||||
}
|
||||
|
||||
static inline void cpm_set_scc_fcr(volatile scc_uart_t * sup)
|
||||
{
|
||||
sup->scc_genscc.scc_rfcr = SMC_EB;
|
||||
sup->scc_genscc.scc_tfcr = SMC_EB;
|
||||
}
|
||||
|
||||
static inline void cpm_set_smc_fcr(volatile smc_uart_t * up)
|
||||
{
|
||||
up->smc_rfcr = SMC_EB;
|
||||
up->smc_tfcr = SMC_EB;
|
||||
}
|
||||
|
||||
#define DPRAM_BASE ((unsigned char *)&cpmp->cp_dpmem[0])
|
||||
|
||||
#endif
|
||||
388
drivers/serial/cpm_uart/cpm_uart_cpm2.c
Normal file
388
drivers/serial/cpm_uart/cpm_uart_cpm2.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* linux/drivers/serial/cpm_uart_cpm2.c
|
||||
*
|
||||
* Driver for CPM (SCC/SMC) serial ports; CPM2 definitions
|
||||
*
|
||||
* Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
|
||||
* Pantelis Antoniou (panto@intracom.gr) (CPM1)
|
||||
*
|
||||
* Copyright (C) 2004 Freescale Semiconductor, Inc.
|
||||
* (C) 2004 Intracom, S.A.
|
||||
* (C) 2006 MontaVista Software, Inc.
|
||||
* Vitaly Bordug <vbordug@ru.mvista.com>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/fs_pd.h>
|
||||
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "cpm_uart.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
void cpm_line_cr_cmd(int line, int cmd)
|
||||
{
|
||||
ulong val;
|
||||
volatile cpm_cpm2_t *cp = cpm2_map(im_cpm);
|
||||
|
||||
|
||||
switch (line) {
|
||||
case UART_SMC1:
|
||||
val = mk_cr_cmd(CPM_CR_SMC1_PAGE, CPM_CR_SMC1_SBLOCK, 0,
|
||||
cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SMC2:
|
||||
val = mk_cr_cmd(CPM_CR_SMC2_PAGE, CPM_CR_SMC2_SBLOCK, 0,
|
||||
cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SCC1:
|
||||
val = mk_cr_cmd(CPM_CR_SCC1_PAGE, CPM_CR_SCC1_SBLOCK, 0,
|
||||
cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SCC2:
|
||||
val = mk_cr_cmd(CPM_CR_SCC2_PAGE, CPM_CR_SCC2_SBLOCK, 0,
|
||||
cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SCC3:
|
||||
val = mk_cr_cmd(CPM_CR_SCC3_PAGE, CPM_CR_SCC3_SBLOCK, 0,
|
||||
cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
case UART_SCC4:
|
||||
val = mk_cr_cmd(CPM_CR_SCC4_PAGE, CPM_CR_SCC4_SBLOCK, 0,
|
||||
cmd) | CPM_CR_FLG;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
}
|
||||
cp->cp_cpcr = val;
|
||||
while (cp->cp_cpcr & CPM_CR_FLG) ;
|
||||
|
||||
cpm2_unmap(cp);
|
||||
}
|
||||
|
||||
void smc1_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
volatile iop_cpm2_t *io = cpm2_map(im_ioport);
|
||||
volatile cpmux_t *cpmux = cpm2_map(im_cpmux);
|
||||
|
||||
/* SMC1 is only on port D */
|
||||
io->iop_ppard |= 0x00c00000;
|
||||
io->iop_pdird |= 0x00400000;
|
||||
io->iop_pdird &= ~0x00800000;
|
||||
io->iop_psord &= ~0x00c00000;
|
||||
|
||||
/* Wire BRG1 to SMC1 */
|
||||
cpmux->cmx_smr &= 0x0f;
|
||||
pinfo->brg = 1;
|
||||
|
||||
cpm2_unmap(cpmux);
|
||||
cpm2_unmap(io);
|
||||
}
|
||||
|
||||
void smc2_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
volatile iop_cpm2_t *io = cpm2_map(im_ioport);
|
||||
volatile cpmux_t *cpmux = cpm2_map(im_cpmux);
|
||||
|
||||
/* SMC2 is only on port A */
|
||||
io->iop_ppara |= 0x00c00000;
|
||||
io->iop_pdira |= 0x00400000;
|
||||
io->iop_pdira &= ~0x00800000;
|
||||
io->iop_psora &= ~0x00c00000;
|
||||
|
||||
/* Wire BRG2 to SMC2 */
|
||||
cpmux->cmx_smr &= 0xf0;
|
||||
pinfo->brg = 2;
|
||||
|
||||
cpm2_unmap(cpmux);
|
||||
cpm2_unmap(io);
|
||||
}
|
||||
|
||||
void scc1_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
volatile iop_cpm2_t *io = cpm2_map(im_ioport);
|
||||
volatile cpmux_t *cpmux = cpm2_map(im_cpmux);
|
||||
|
||||
/* Use Port D for SCC1 instead of other functions. */
|
||||
io->iop_ppard |= 0x00000003;
|
||||
io->iop_psord &= ~0x00000001; /* Rx */
|
||||
io->iop_psord |= 0x00000002; /* Tx */
|
||||
io->iop_pdird &= ~0x00000001; /* Rx */
|
||||
io->iop_pdird |= 0x00000002; /* Tx */
|
||||
|
||||
/* Wire BRG1 to SCC1 */
|
||||
cpmux->cmx_scr &= 0x00ffffff;
|
||||
cpmux->cmx_scr |= 0x00000000;
|
||||
pinfo->brg = 1;
|
||||
|
||||
cpm2_unmap(cpmux);
|
||||
cpm2_unmap(io);
|
||||
}
|
||||
|
||||
void scc2_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
/*
|
||||
* STx GP3 uses the SCC2 secondary option pin assignment
|
||||
* which this driver doesn't account for in the static
|
||||
* pin assignments. This kind of board specific info
|
||||
* really has to get out of the driver so boards can
|
||||
* be supported in a sane fashion.
|
||||
*/
|
||||
#ifndef CONFIG_STX_GP3
|
||||
volatile iop_cpm2_t *io = cpm2_map(im_ioport);
|
||||
volatile cpmux_t *cpmux = cpm2_map(im_cpmux);
|
||||
|
||||
io->iop_pparb |= 0x008b0000;
|
||||
io->iop_pdirb |= 0x00880000;
|
||||
io->iop_psorb |= 0x00880000;
|
||||
io->iop_pdirb &= ~0x00030000;
|
||||
io->iop_psorb &= ~0x00030000;
|
||||
#endif
|
||||
cpmux->cmx_scr &= 0xff00ffff;
|
||||
cpmux->cmx_scr |= 0x00090000;
|
||||
pinfo->brg = 2;
|
||||
|
||||
cpm2_unmap(cpmux);
|
||||
cpm2_unmap(io);
|
||||
}
|
||||
|
||||
void scc3_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
volatile iop_cpm2_t *io = cpm2_map(im_ioport);
|
||||
volatile cpmux_t *cpmux = cpm2_map(im_cpmux);
|
||||
|
||||
io->iop_pparb |= 0x008b0000;
|
||||
io->iop_pdirb |= 0x00880000;
|
||||
io->iop_psorb |= 0x00880000;
|
||||
io->iop_pdirb &= ~0x00030000;
|
||||
io->iop_psorb &= ~0x00030000;
|
||||
cpmux->cmx_scr &= 0xffff00ff;
|
||||
cpmux->cmx_scr |= 0x00001200;
|
||||
pinfo->brg = 3;
|
||||
|
||||
cpm2_unmap(cpmux);
|
||||
cpm2_unmap(io);
|
||||
}
|
||||
|
||||
void scc4_lineif(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
volatile iop_cpm2_t *io = cpm2_map(im_ioport);
|
||||
volatile cpmux_t *cpmux = cpm2_map(im_cpmux);
|
||||
|
||||
io->iop_ppard |= 0x00000600;
|
||||
io->iop_psord &= ~0x00000600; /* Tx/Rx */
|
||||
io->iop_pdird &= ~0x00000200; /* Rx */
|
||||
io->iop_pdird |= 0x00000400; /* Tx */
|
||||
|
||||
cpmux->cmx_scr &= 0xffffff00;
|
||||
cpmux->cmx_scr |= 0x0000001b;
|
||||
pinfo->brg = 4;
|
||||
|
||||
cpm2_unmap(cpmux);
|
||||
cpm2_unmap(io);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate DP-Ram and memory buffers. We need to allocate a transmit and
|
||||
* receive buffer descriptors from dual port ram, and a character
|
||||
* buffer area from host mem. If we are allocating for the console we need
|
||||
* to do it from bootmem
|
||||
*/
|
||||
int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con)
|
||||
{
|
||||
int dpmemsz, memsz;
|
||||
u8 *dp_mem;
|
||||
uint dp_offset;
|
||||
u8 *mem_addr;
|
||||
dma_addr_t dma_addr = 0;
|
||||
|
||||
pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line);
|
||||
|
||||
dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos);
|
||||
dp_offset = cpm_dpalloc(dpmemsz, 8);
|
||||
if (IS_DPERR(dp_offset)) {
|
||||
printk(KERN_ERR
|
||||
"cpm_uart_cpm.c: could not allocate buffer descriptors\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dp_mem = cpm_dpram_addr(dp_offset);
|
||||
|
||||
memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
|
||||
L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
|
||||
if (is_con) {
|
||||
mem_addr = alloc_bootmem(memsz);
|
||||
dma_addr = virt_to_bus(mem_addr);
|
||||
}
|
||||
else
|
||||
mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (mem_addr == NULL) {
|
||||
cpm_dpfree(dp_offset);
|
||||
printk(KERN_ERR
|
||||
"cpm_uart_cpm.c: could not allocate coherent memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pinfo->dp_addr = dp_offset;
|
||||
pinfo->mem_addr = mem_addr;
|
||||
pinfo->dma_addr = dma_addr;
|
||||
pinfo->mem_size = memsz;
|
||||
|
||||
pinfo->rx_buf = mem_addr;
|
||||
pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos
|
||||
* pinfo->rx_fifosize);
|
||||
|
||||
pinfo->rx_bd_base = (volatile cbd_t *)dp_mem;
|
||||
pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cpm_uart_freebuf(struct uart_cpm_port *pinfo)
|
||||
{
|
||||
dma_free_coherent(NULL, L1_CACHE_ALIGN(pinfo->rx_nrfifos *
|
||||
pinfo->rx_fifosize) +
|
||||
L1_CACHE_ALIGN(pinfo->tx_nrfifos *
|
||||
pinfo->tx_fifosize), pinfo->mem_addr,
|
||||
pinfo->dma_addr);
|
||||
|
||||
cpm_dpfree(pinfo->dp_addr);
|
||||
}
|
||||
|
||||
/* Setup any dynamic params in the uart desc */
|
||||
int __init cpm_uart_init_portdesc(void)
|
||||
{
|
||||
#if defined(CONFIG_SERIAL_CPM_SMC1) || defined(CONFIG_SERIAL_CPM_SMC2)
|
||||
u16 *addr;
|
||||
#endif
|
||||
pr_debug("CPM uart[-]:init portdesc\n");
|
||||
|
||||
cpm_uart_nr = 0;
|
||||
#ifdef CONFIG_SERIAL_CPM_SMC1
|
||||
cpm_uart_ports[UART_SMC1].smcp = (smc_t *) cpm2_map(im_smc[0]);
|
||||
cpm_uart_ports[UART_SMC1].port.mapbase =
|
||||
(unsigned long)cpm_uart_ports[UART_SMC1].smcp;
|
||||
|
||||
cpm_uart_ports[UART_SMC1].smcup =
|
||||
(smc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SMC1], PROFF_SMC_SIZE);
|
||||
addr = (u16 *)cpm2_map_size(im_dprambase[PROFF_SMC1_BASE], 2);
|
||||
*addr = PROFF_SMC1;
|
||||
cpm2_unmap(addr);
|
||||
|
||||
cpm_uart_ports[UART_SMC1].smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
|
||||
cpm_uart_ports[UART_SMC1].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
|
||||
cpm_uart_ports[UART_SMC1].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SMC1;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SMC2
|
||||
cpm_uart_ports[UART_SMC2].smcp = (smc_t *) cpm2_map(im_smc[1]);
|
||||
cpm_uart_ports[UART_SMC2].port.mapbase =
|
||||
(unsigned long)cpm_uart_ports[UART_SMC2].smcp;
|
||||
|
||||
cpm_uart_ports[UART_SMC2].smcup =
|
||||
(smc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SMC2], PROFF_SMC_SIZE);
|
||||
addr = (u16 *)cpm2_map_size(im_dprambase[PROFF_SMC2_BASE], 2);
|
||||
*addr = PROFF_SMC2;
|
||||
cpm2_unmap(addr);
|
||||
|
||||
cpm_uart_ports[UART_SMC2].smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
|
||||
cpm_uart_ports[UART_SMC2].smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
|
||||
cpm_uart_ports[UART_SMC2].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SMC2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SCC1
|
||||
cpm_uart_ports[UART_SCC1].sccp = (scc_t *) cpm2_map(im_scc[0]);
|
||||
cpm_uart_ports[UART_SCC1].port.mapbase =
|
||||
(unsigned long)cpm_uart_ports[UART_SCC1].sccp;
|
||||
cpm_uart_ports[UART_SCC1].sccup =
|
||||
(scc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SCC1], PROFF_SCC_SIZE);
|
||||
|
||||
cpm_uart_ports[UART_SCC1].sccp->scc_sccm &=
|
||||
~(UART_SCCM_TX | UART_SCCM_RX);
|
||||
cpm_uart_ports[UART_SCC1].sccp->scc_gsmrl &=
|
||||
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
cpm_uart_ports[UART_SCC1].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC1;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SCC2
|
||||
cpm_uart_ports[UART_SCC2].sccp = (scc_t *) cpm2_map(im_scc[1]);
|
||||
cpm_uart_ports[UART_SCC2].port.mapbase =
|
||||
(unsigned long)cpm_uart_ports[UART_SCC2].sccp;
|
||||
cpm_uart_ports[UART_SCC2].sccup =
|
||||
(scc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SCC2], PROFF_SCC_SIZE);
|
||||
|
||||
cpm_uart_ports[UART_SCC2].sccp->scc_sccm &=
|
||||
~(UART_SCCM_TX | UART_SCCM_RX);
|
||||
cpm_uart_ports[UART_SCC2].sccp->scc_gsmrl &=
|
||||
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
cpm_uart_ports[UART_SCC2].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SCC3
|
||||
cpm_uart_ports[UART_SCC3].sccp = (scc_t *) cpm2_map(im_scc[2]);
|
||||
cpm_uart_ports[UART_SCC3].port.mapbase =
|
||||
(unsigned long)cpm_uart_ports[UART_SCC3].sccp;
|
||||
cpm_uart_ports[UART_SCC3].sccup =
|
||||
(scc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SCC3], PROFF_SCC_SIZE);
|
||||
|
||||
cpm_uart_ports[UART_SCC3].sccp->scc_sccm &=
|
||||
~(UART_SCCM_TX | UART_SCCM_RX);
|
||||
cpm_uart_ports[UART_SCC3].sccp->scc_gsmrl &=
|
||||
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
cpm_uart_ports[UART_SCC3].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC3;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_CPM_SCC4
|
||||
cpm_uart_ports[UART_SCC4].sccp = (scc_t *) cpm2_map(im_scc[3]);
|
||||
cpm_uart_ports[UART_SCC4].port.mapbase =
|
||||
(unsigned long)cpm_uart_ports[UART_SCC4].sccp;
|
||||
cpm_uart_ports[UART_SCC4].sccup =
|
||||
(scc_uart_t *) cpm2_map_size(im_dprambase[PROFF_SCC4], PROFF_SCC_SIZE);
|
||||
|
||||
cpm_uart_ports[UART_SCC4].sccp->scc_sccm &=
|
||||
~(UART_SCCM_TX | UART_SCCM_RX);
|
||||
cpm_uart_ports[UART_SCC4].sccp->scc_gsmrl &=
|
||||
~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
cpm_uart_ports[UART_SCC4].port.uartclk = uart_clock();
|
||||
cpm_uart_port_map[cpm_uart_nr++] = UART_SCC4;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
42
drivers/serial/cpm_uart/cpm_uart_cpm2.h
Normal file
42
drivers/serial/cpm_uart/cpm_uart_cpm2.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* linux/drivers/serial/cpm_uart/cpm_uart_cpm2.h
|
||||
*
|
||||
* Driver for CPM (SCC/SMC) serial ports
|
||||
*
|
||||
* definitions for cpm2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPM_UART_CPM2_H
|
||||
#define CPM_UART_CPM2_H
|
||||
|
||||
#include <asm/cpm2.h>
|
||||
|
||||
/* defines for IRQs */
|
||||
#define SMC1_IRQ SIU_INT_SMC1
|
||||
#define SMC2_IRQ SIU_INT_SMC2
|
||||
#define SCC1_IRQ SIU_INT_SCC1
|
||||
#define SCC2_IRQ SIU_INT_SCC2
|
||||
#define SCC3_IRQ SIU_INT_SCC3
|
||||
#define SCC4_IRQ SIU_INT_SCC4
|
||||
|
||||
static inline void cpm_set_brg(int brg, int baud)
|
||||
{
|
||||
cpm_setbrg(brg, baud);
|
||||
}
|
||||
|
||||
static inline void cpm_set_scc_fcr(volatile scc_uart_t * sup)
|
||||
{
|
||||
sup->scc_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB;
|
||||
sup->scc_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB;
|
||||
}
|
||||
|
||||
static inline void cpm_set_smc_fcr(volatile smc_uart_t * up)
|
||||
{
|
||||
up->smc_rfcr = CPMFCR_GBL | CPMFCR_EB;
|
||||
up->smc_tfcr = CPMFCR_GBL | CPMFCR_EB;
|
||||
}
|
||||
|
||||
#define DPRAM_BASE ((unsigned char *)cpm_dpram_addr(0))
|
||||
|
||||
#endif
|
||||
5002
drivers/serial/crisv10.c
Normal file
5002
drivers/serial/crisv10.c
Normal file
File diff suppressed because it is too large
Load Diff
136
drivers/serial/crisv10.h
Normal file
136
drivers/serial/crisv10.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* serial.h: Arch-dep definitions for the Etrax100 serial driver.
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2000 Axis Communications AB
|
||||
*/
|
||||
|
||||
#ifndef _ETRAX_SERIAL_H
|
||||
#define _ETRAX_SERIAL_H
|
||||
|
||||
#include <linux/circ_buf.h>
|
||||
#include <asm/termios.h>
|
||||
|
||||
/* Software state per channel */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/*
|
||||
* This is our internal structure for each serial port's state.
|
||||
*
|
||||
* Many fields are paralleled by the structure used by the serial_struct
|
||||
* structure.
|
||||
*
|
||||
* For definitions of the flags field, see tty.h
|
||||
*/
|
||||
|
||||
#define SERIAL_RECV_DESCRIPTORS 8
|
||||
|
||||
struct etrax_recv_buffer {
|
||||
struct etrax_recv_buffer *next;
|
||||
unsigned short length;
|
||||
unsigned char error;
|
||||
unsigned char pad;
|
||||
|
||||
unsigned char buffer[0];
|
||||
};
|
||||
|
||||
struct e100_serial {
|
||||
int baud;
|
||||
volatile u8 *port; /* R_SERIALx_CTRL */
|
||||
u32 irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */
|
||||
|
||||
/* Output registers */
|
||||
volatile u8 *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
|
||||
volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST */
|
||||
volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD */
|
||||
const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS */
|
||||
|
||||
/* Input registers */
|
||||
volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
|
||||
volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST */
|
||||
volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */
|
||||
volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */
|
||||
|
||||
int flags; /* defined in tty.h */
|
||||
|
||||
u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
|
||||
u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
|
||||
u8 iseteop; /* bit number for R_SET_EOP for the input dma */
|
||||
int enabled; /* Set to 1 if the port is enabled in HW config */
|
||||
|
||||
u8 dma_out_enabled:1; /* Set to 1 if DMA should be used */
|
||||
u8 dma_in_enabled:1; /* Set to 1 if DMA should be used */
|
||||
|
||||
/* end of fields defined in rs_table[] in .c-file */
|
||||
u8 uses_dma_in; /* Set to 1 if DMA is used */
|
||||
u8 uses_dma_out; /* Set to 1 if DMA is used */
|
||||
u8 forced_eop; /* a fifo eop has been forced */
|
||||
int baud_base; /* For special baudrates */
|
||||
int custom_divisor; /* For special baudrates */
|
||||
struct etrax_dma_descr tr_descr;
|
||||
struct etrax_dma_descr rec_descr[SERIAL_RECV_DESCRIPTORS];
|
||||
int cur_rec_descr;
|
||||
|
||||
volatile int tr_running; /* 1 if output is running */
|
||||
|
||||
struct tty_struct *tty;
|
||||
int read_status_mask;
|
||||
int ignore_status_mask;
|
||||
int x_char; /* xon/xoff character */
|
||||
int close_delay;
|
||||
unsigned short closing_wait;
|
||||
unsigned short closing_wait2;
|
||||
unsigned long event;
|
||||
unsigned long last_active;
|
||||
int line;
|
||||
int type; /* PORT_ETRAX */
|
||||
int count; /* # of fd on device */
|
||||
int blocked_open; /* # of blocked opens */
|
||||
struct circ_buf xmit;
|
||||
struct etrax_recv_buffer *first_recv_buffer;
|
||||
struct etrax_recv_buffer *last_recv_buffer;
|
||||
unsigned int recv_cnt;
|
||||
unsigned int max_recv_cnt;
|
||||
|
||||
struct work_struct work;
|
||||
struct async_icount icount; /* error-statistics etc.*/
|
||||
struct ktermios normal_termios;
|
||||
struct ktermios callout_termios;
|
||||
#ifdef DECLARE_WAITQUEUE
|
||||
wait_queue_head_t open_wait;
|
||||
wait_queue_head_t close_wait;
|
||||
#else
|
||||
struct wait_queue *open_wait;
|
||||
struct wait_queue *close_wait;
|
||||
#endif
|
||||
|
||||
unsigned long char_time_usec; /* The time for 1 char, in usecs */
|
||||
unsigned long flush_time_usec; /* How often we should flush */
|
||||
unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */
|
||||
unsigned long last_tx_active; /* Last tx time in jiffies */
|
||||
unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */
|
||||
unsigned long last_rx_active; /* Last rx time in jiffies */
|
||||
|
||||
int break_detected_cnt;
|
||||
int errorcode;
|
||||
|
||||
#ifdef CONFIG_ETRAX_RS485
|
||||
struct rs485_control rs485; /* RS-485 support */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* this PORT is not in the standard serial.h. it's not actually used for
|
||||
* anything since we only have one type of async serial-port anyway in this
|
||||
* system.
|
||||
*/
|
||||
|
||||
#define PORT_ETRAX 1
|
||||
|
||||
/*
|
||||
* Events are used to schedule things to happen at timer-interrupt
|
||||
* time, instead of at rs interrupt time.
|
||||
*/
|
||||
#define RS_EVENT_WRITE_WAKEUP 0
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* !_ETRAX_SERIAL_H */
|
||||
814
drivers/serial/dz.c
Normal file
814
drivers/serial/dz.c
Normal file
@@ -0,0 +1,814 @@
|
||||
/*
|
||||
* dz.c: Serial port driver for DECstations equipped
|
||||
* with the DZ chipset.
|
||||
*
|
||||
* Copyright (C) 1998 Olivier A. D. Lebaillif
|
||||
*
|
||||
* Email: olivier.lebaillif@ifrsys.com
|
||||
*
|
||||
* Copyright (C) 2004, 2006 Maciej W. Rozycki
|
||||
*
|
||||
* [31-AUG-98] triemer
|
||||
* Changed IRQ to use Harald's dec internals interrupts.h
|
||||
* removed base_addr code - moving address assignment to setup.c
|
||||
* Changed name of dz_init to rs_init to be consistent with tc code
|
||||
* [13-NOV-98] triemer fixed code to receive characters
|
||||
* after patches by harald to irq code.
|
||||
* [09-JAN-99] triemer minor fix for schedule - due to removal of timeout
|
||||
* field from "current" - somewhere between 2.1.121 and 2.1.131
|
||||
Qua Jun 27 15:02:26 BRT 2001
|
||||
* [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups
|
||||
*
|
||||
* Parts (C) 1999 David Airlie, airlied@linux.ie
|
||||
* [07-SEP-99] Bugfixes
|
||||
*
|
||||
* [06-Jan-2002] Russell King <rmk@arm.linux.org.uk>
|
||||
* Converted to new serial core
|
||||
*/
|
||||
|
||||
#undef DEBUG_DZ
|
||||
|
||||
#if defined(CONFIG_SERIAL_DZ_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/dec/interrupts.h>
|
||||
#include <asm/dec/kn01.h>
|
||||
#include <asm/dec/kn02.h>
|
||||
#include <asm/dec/machtype.h>
|
||||
#include <asm/dec/prom.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "dz.h"
|
||||
|
||||
static char *dz_name = "DECstation DZ serial driver version ";
|
||||
static char *dz_version = "1.03";
|
||||
|
||||
struct dz_port {
|
||||
struct uart_port port;
|
||||
unsigned int cflag;
|
||||
};
|
||||
|
||||
static struct dz_port dz_ports[DZ_NB_PORT];
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* dz_in () and dz_out ()
|
||||
*
|
||||
* These routines are used to access the registers of the DZ
|
||||
* chip, hiding relocation differences between implementation.
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static inline unsigned short dz_in(struct dz_port *dport, unsigned offset)
|
||||
{
|
||||
volatile unsigned short *addr =
|
||||
(volatile unsigned short *) (dport->port.membase + offset);
|
||||
|
||||
return *addr;
|
||||
}
|
||||
|
||||
static inline void dz_out(struct dz_port *dport, unsigned offset,
|
||||
unsigned short value)
|
||||
{
|
||||
volatile unsigned short *addr =
|
||||
(volatile unsigned short *) (dport->port.membase + offset);
|
||||
|
||||
*addr = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* rs_stop () and rs_start ()
|
||||
*
|
||||
* These routines are called before setting or resetting
|
||||
* tty->stopped. They enable or disable transmitter interrupts,
|
||||
* as necessary.
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void dz_stop_tx(struct uart_port *uport)
|
||||
{
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned short tmp, mask = 1 << dport->port.line;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dport->port.lock, flags);
|
||||
tmp = dz_in(dport, DZ_TCR); /* read the TX flag */
|
||||
tmp &= ~mask; /* clear the TX flag */
|
||||
dz_out(dport, DZ_TCR, tmp);
|
||||
spin_unlock_irqrestore(&dport->port.lock, flags);
|
||||
}
|
||||
|
||||
static void dz_start_tx(struct uart_port *uport)
|
||||
{
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned short tmp, mask = 1 << dport->port.line;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dport->port.lock, flags);
|
||||
tmp = dz_in(dport, DZ_TCR); /* read the TX flag */
|
||||
tmp |= mask; /* set the TX flag */
|
||||
dz_out(dport, DZ_TCR, tmp);
|
||||
spin_unlock_irqrestore(&dport->port.lock, flags);
|
||||
}
|
||||
|
||||
static void dz_stop_rx(struct uart_port *uport)
|
||||
{
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dport->port.lock, flags);
|
||||
dport->cflag &= ~DZ_CREAD;
|
||||
dz_out(dport, DZ_LPR, dport->cflag | dport->port.line);
|
||||
spin_unlock_irqrestore(&dport->port.lock, flags);
|
||||
}
|
||||
|
||||
static void dz_enable_ms(struct uart_port *port)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
*
|
||||
* Here start the interrupt handling routines. All of the following
|
||||
* subroutines are declared as inline and are folded into
|
||||
* dz_interrupt. They were separated out for readability's sake.
|
||||
*
|
||||
* Note: dz_interrupt() is a "fast" interrupt, which means that it
|
||||
* runs with interrupts turned off. People who may want to modify
|
||||
* dz_interrupt() should try to keep the interrupt handler as fast as
|
||||
* possible. After you are done making modifications, it is not a bad
|
||||
* idea to do:
|
||||
*
|
||||
* make drivers/serial/dz.s
|
||||
*
|
||||
* and look at the resulting assemble code in dz.s.
|
||||
*
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* receive_char ()
|
||||
*
|
||||
* This routine deals with inputs from any lines.
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
static inline void dz_receive_chars(struct dz_port *dport_in)
|
||||
{
|
||||
struct dz_port *dport;
|
||||
struct tty_struct *tty = NULL;
|
||||
struct uart_icount *icount;
|
||||
int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 };
|
||||
unsigned short status;
|
||||
unsigned char ch, flag;
|
||||
int i;
|
||||
|
||||
while ((status = dz_in(dport_in, DZ_RBUF)) & DZ_DVAL) {
|
||||
dport = &dz_ports[LINE(status)];
|
||||
tty = dport->port.info->tty; /* point to the proper dev */
|
||||
|
||||
ch = UCHAR(status); /* grab the char */
|
||||
|
||||
icount = &dport->port.icount;
|
||||
icount->rx++;
|
||||
|
||||
flag = TTY_NORMAL;
|
||||
if (status & DZ_FERR) { /* frame error */
|
||||
/*
|
||||
* There is no separate BREAK status bit, so
|
||||
* treat framing errors as BREAKs for Magic SysRq
|
||||
* and SAK; normally, otherwise.
|
||||
*/
|
||||
if (uart_handle_break(&dport->port))
|
||||
continue;
|
||||
if (dport->port.flags & UPF_SAK)
|
||||
flag = TTY_BREAK;
|
||||
else
|
||||
flag = TTY_FRAME;
|
||||
} else if (status & DZ_OERR) /* overrun error */
|
||||
flag = TTY_OVERRUN;
|
||||
else if (status & DZ_PERR) /* parity error */
|
||||
flag = TTY_PARITY;
|
||||
|
||||
/* keep track of the statistics */
|
||||
switch (flag) {
|
||||
case TTY_FRAME:
|
||||
icount->frame++;
|
||||
break;
|
||||
case TTY_PARITY:
|
||||
icount->parity++;
|
||||
break;
|
||||
case TTY_OVERRUN:
|
||||
icount->overrun++;
|
||||
break;
|
||||
case TTY_BREAK:
|
||||
icount->brk++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(&dport->port, ch))
|
||||
continue;
|
||||
|
||||
if ((status & dport->port.ignore_status_mask) == 0) {
|
||||
uart_insert_char(&dport->port,
|
||||
status, DZ_OERR, ch, flag);
|
||||
lines_rx[LINE(status)] = 1;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < DZ_NB_PORT; i++)
|
||||
if (lines_rx[i])
|
||||
tty_flip_buffer_push(dz_ports[i].port.info->tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* transmit_char ()
|
||||
*
|
||||
* This routine deals with outputs to any lines.
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
static inline void dz_transmit_chars(struct dz_port *dport_in)
|
||||
{
|
||||
struct dz_port *dport;
|
||||
struct circ_buf *xmit;
|
||||
unsigned short status;
|
||||
unsigned char tmp;
|
||||
|
||||
status = dz_in(dport_in, DZ_CSR);
|
||||
dport = &dz_ports[LINE(status)];
|
||||
xmit = &dport->port.info->xmit;
|
||||
|
||||
if (dport->port.x_char) { /* XON/XOFF chars */
|
||||
dz_out(dport, DZ_TDR, dport->port.x_char);
|
||||
dport->port.icount.tx++;
|
||||
dport->port.x_char = 0;
|
||||
return;
|
||||
}
|
||||
/* If nothing to do or stopped or hardware stopped. */
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) {
|
||||
dz_stop_tx(&dport->port);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If something to do... (remember the dz has no output fifo,
|
||||
* so we go one char at a time) :-<
|
||||
*/
|
||||
tmp = xmit->buf[xmit->tail];
|
||||
xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1);
|
||||
dz_out(dport, DZ_TDR, tmp);
|
||||
dport->port.icount.tx++;
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS)
|
||||
uart_write_wakeup(&dport->port);
|
||||
|
||||
/* Are we are done. */
|
||||
if (uart_circ_empty(xmit))
|
||||
dz_stop_tx(&dport->port);
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* check_modem_status()
|
||||
*
|
||||
* DS 3100 & 5100: Only valid for the MODEM line, duh!
|
||||
* DS 5000/200: Valid for the MODEM and PRINTER line.
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
static inline void check_modem_status(struct dz_port *dport)
|
||||
{
|
||||
/*
|
||||
* FIXME:
|
||||
* 1. No status change interrupt; use a timer.
|
||||
* 2. Handle the 3100/5000 as appropriate. --macro
|
||||
*/
|
||||
unsigned short status;
|
||||
|
||||
/* If not the modem line just return. */
|
||||
if (dport->port.line != DZ_MODEM)
|
||||
return;
|
||||
|
||||
status = dz_in(dport, DZ_MSR);
|
||||
|
||||
/* it's easy, since DSR2 is the only bit in the register */
|
||||
if (status)
|
||||
dport->port.icount.dsr++;
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* dz_interrupt ()
|
||||
*
|
||||
* this is the main interrupt routine for the DZ chip.
|
||||
* It deals with the multiple ports.
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
static irqreturn_t dz_interrupt(int irq, void *dev)
|
||||
{
|
||||
struct dz_port *dport = (struct dz_port *)dev;
|
||||
unsigned short status;
|
||||
|
||||
/* get the reason why we just got an irq */
|
||||
status = dz_in(dport, DZ_CSR);
|
||||
|
||||
if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE))
|
||||
dz_receive_chars(dport);
|
||||
|
||||
if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE))
|
||||
dz_transmit_chars(dport);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------------
|
||||
* Here ends the DZ interrupt routines.
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static unsigned int dz_get_mctrl(struct uart_port *uport)
|
||||
{
|
||||
/*
|
||||
* FIXME: Handle the 3100/5000 as appropriate. --macro
|
||||
*/
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
|
||||
|
||||
if (dport->port.line == DZ_MODEM) {
|
||||
if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR)
|
||||
mctrl &= ~TIOCM_DSR;
|
||||
}
|
||||
|
||||
return mctrl;
|
||||
}
|
||||
|
||||
static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl)
|
||||
{
|
||||
/*
|
||||
* FIXME: Handle the 3100/5000 as appropriate. --macro
|
||||
*/
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned short tmp;
|
||||
|
||||
if (dport->port.line == DZ_MODEM) {
|
||||
tmp = dz_in(dport, DZ_TCR);
|
||||
if (mctrl & TIOCM_DTR)
|
||||
tmp &= ~DZ_MODEM_DTR;
|
||||
else
|
||||
tmp |= DZ_MODEM_DTR;
|
||||
dz_out(dport, DZ_TCR, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------------
|
||||
* startup ()
|
||||
*
|
||||
* various initialization tasks
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
static int dz_startup(struct uart_port *uport)
|
||||
{
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned long flags;
|
||||
unsigned short tmp;
|
||||
|
||||
spin_lock_irqsave(&dport->port.lock, flags);
|
||||
|
||||
/* enable the interrupt and the scanning */
|
||||
tmp = dz_in(dport, DZ_CSR);
|
||||
tmp |= DZ_RIE | DZ_TIE | DZ_MSE;
|
||||
dz_out(dport, DZ_CSR, tmp);
|
||||
|
||||
spin_unlock_irqrestore(&dport->port.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------------
|
||||
* shutdown ()
|
||||
*
|
||||
* This routine will shutdown a serial port; interrupts are disabled, and
|
||||
* DTR is dropped if the hangup on close termio flag is on.
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
static void dz_shutdown(struct uart_port *uport)
|
||||
{
|
||||
dz_stop_tx(uport);
|
||||
}
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------------
|
||||
* dz_tx_empty() -- get the transmitter empty status
|
||||
*
|
||||
* Purpose: Let user call ioctl() to get info when the UART physically
|
||||
* is emptied. On bus types like RS485, the transmitter must
|
||||
* release the bus after transmitting. This must be done when
|
||||
* the transmit shift register is empty, not be done when the
|
||||
* transmit holding register is empty. This functionality
|
||||
* allows an RS485 driver to be written in user space.
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
static unsigned int dz_tx_empty(struct uart_port *uport)
|
||||
{
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned short tmp, mask = 1 << dport->port.line;
|
||||
|
||||
tmp = dz_in(dport, DZ_TCR);
|
||||
tmp &= mask;
|
||||
|
||||
return tmp ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static void dz_break_ctl(struct uart_port *uport, int break_state)
|
||||
{
|
||||
/*
|
||||
* FIXME: Can't access BREAK bits in TDR easily;
|
||||
* reuse the code for polled TX. --macro
|
||||
*/
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned long flags;
|
||||
unsigned short tmp, mask = 1 << dport->port.line;
|
||||
|
||||
spin_lock_irqsave(&uport->lock, flags);
|
||||
tmp = dz_in(dport, DZ_TCR);
|
||||
if (break_state)
|
||||
tmp |= mask;
|
||||
else
|
||||
tmp &= ~mask;
|
||||
dz_out(dport, DZ_TCR, tmp);
|
||||
spin_unlock_irqrestore(&uport->lock, flags);
|
||||
}
|
||||
|
||||
static void dz_set_termios(struct uart_port *uport, struct ktermios *termios,
|
||||
struct ktermios *old_termios)
|
||||
{
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned long flags;
|
||||
unsigned int cflag, baud;
|
||||
|
||||
cflag = dport->port.line;
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
cflag |= DZ_CS5;
|
||||
break;
|
||||
case CS6:
|
||||
cflag |= DZ_CS6;
|
||||
break;
|
||||
case CS7:
|
||||
cflag |= DZ_CS7;
|
||||
break;
|
||||
case CS8:
|
||||
default:
|
||||
cflag |= DZ_CS8;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
cflag |= DZ_CSTOPB;
|
||||
if (termios->c_cflag & PARENB)
|
||||
cflag |= DZ_PARENB;
|
||||
if (termios->c_cflag & PARODD)
|
||||
cflag |= DZ_PARODD;
|
||||
|
||||
baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600);
|
||||
switch (baud) {
|
||||
case 50:
|
||||
cflag |= DZ_B50;
|
||||
break;
|
||||
case 75:
|
||||
cflag |= DZ_B75;
|
||||
break;
|
||||
case 110:
|
||||
cflag |= DZ_B110;
|
||||
break;
|
||||
case 134:
|
||||
cflag |= DZ_B134;
|
||||
break;
|
||||
case 150:
|
||||
cflag |= DZ_B150;
|
||||
break;
|
||||
case 300:
|
||||
cflag |= DZ_B300;
|
||||
break;
|
||||
case 600:
|
||||
cflag |= DZ_B600;
|
||||
break;
|
||||
case 1200:
|
||||
cflag |= DZ_B1200;
|
||||
break;
|
||||
case 1800:
|
||||
cflag |= DZ_B1800;
|
||||
break;
|
||||
case 2000:
|
||||
cflag |= DZ_B2000;
|
||||
break;
|
||||
case 2400:
|
||||
cflag |= DZ_B2400;
|
||||
break;
|
||||
case 3600:
|
||||
cflag |= DZ_B3600;
|
||||
break;
|
||||
case 4800:
|
||||
cflag |= DZ_B4800;
|
||||
break;
|
||||
case 7200:
|
||||
cflag |= DZ_B7200;
|
||||
break;
|
||||
case 9600:
|
||||
default:
|
||||
cflag |= DZ_B9600;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CREAD)
|
||||
cflag |= DZ_RXENAB;
|
||||
|
||||
spin_lock_irqsave(&dport->port.lock, flags);
|
||||
|
||||
dz_out(dport, DZ_LPR, cflag | dport->port.line);
|
||||
dport->cflag = cflag;
|
||||
|
||||
/* setup accept flag */
|
||||
dport->port.read_status_mask = DZ_OERR;
|
||||
if (termios->c_iflag & INPCK)
|
||||
dport->port.read_status_mask |= DZ_FERR | DZ_PERR;
|
||||
|
||||
/* characters to ignore */
|
||||
uport->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR;
|
||||
|
||||
spin_unlock_irqrestore(&dport->port.lock, flags);
|
||||
}
|
||||
|
||||
static const char *dz_type(struct uart_port *port)
|
||||
{
|
||||
return "DZ";
|
||||
}
|
||||
|
||||
static void dz_release_port(struct uart_port *port)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
static int dz_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dz_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE)
|
||||
port->type = PORT_DZ;
|
||||
}
|
||||
|
||||
/*
|
||||
* verify the new serial_struct (for TIOCSSERIAL).
|
||||
*/
|
||||
static int dz_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
int ret = 0;
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ)
|
||||
ret = -EINVAL;
|
||||
if (ser->irq != port->irq)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops dz_ops = {
|
||||
.tx_empty = dz_tx_empty,
|
||||
.get_mctrl = dz_get_mctrl,
|
||||
.set_mctrl = dz_set_mctrl,
|
||||
.stop_tx = dz_stop_tx,
|
||||
.start_tx = dz_start_tx,
|
||||
.stop_rx = dz_stop_rx,
|
||||
.enable_ms = dz_enable_ms,
|
||||
.break_ctl = dz_break_ctl,
|
||||
.startup = dz_startup,
|
||||
.shutdown = dz_shutdown,
|
||||
.set_termios = dz_set_termios,
|
||||
.type = dz_type,
|
||||
.release_port = dz_release_port,
|
||||
.request_port = dz_request_port,
|
||||
.config_port = dz_config_port,
|
||||
.verify_port = dz_verify_port,
|
||||
};
|
||||
|
||||
static void __init dz_init_ports(void)
|
||||
{
|
||||
static int first = 1;
|
||||
struct dz_port *dport;
|
||||
unsigned long base;
|
||||
int i;
|
||||
|
||||
if (!first)
|
||||
return;
|
||||
first = 0;
|
||||
|
||||
if (mips_machtype == MACH_DS23100 ||
|
||||
mips_machtype == MACH_DS5100)
|
||||
base = CKSEG1ADDR(KN01_SLOT_BASE + KN01_DZ11);
|
||||
else
|
||||
base = CKSEG1ADDR(KN02_SLOT_BASE + KN02_DZ11);
|
||||
|
||||
for (i = 0, dport = dz_ports; i < DZ_NB_PORT; i++, dport++) {
|
||||
spin_lock_init(&dport->port.lock);
|
||||
dport->port.membase = (char *) base;
|
||||
dport->port.iotype = UPIO_MEM;
|
||||
dport->port.irq = dec_interrupt[DEC_IRQ_DZ11];
|
||||
dport->port.line = i;
|
||||
dport->port.fifosize = 1;
|
||||
dport->port.ops = &dz_ops;
|
||||
dport->port.flags = UPF_BOOT_AUTOCONF;
|
||||
}
|
||||
}
|
||||
|
||||
static void dz_reset(struct dz_port *dport)
|
||||
{
|
||||
dz_out(dport, DZ_CSR, DZ_CLR);
|
||||
while (dz_in(dport, DZ_CSR) & DZ_CLR);
|
||||
iob();
|
||||
|
||||
/* enable scanning */
|
||||
dz_out(dport, DZ_CSR, DZ_MSE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_DZ_CONSOLE
|
||||
/*
|
||||
* -------------------------------------------------------------------
|
||||
* dz_console_putchar() -- transmit a character
|
||||
*
|
||||
* Polled transmission. This is tricky. We need to mask transmit
|
||||
* interrupts so that they do not interfere, enable the transmitter
|
||||
* for the line requested and then wait till the transmit scanner
|
||||
* requests data for this line. But it may request data for another
|
||||
* line first, in which case we have to disable its transmitter and
|
||||
* repeat waiting till our line pops up. Only then the character may
|
||||
* be transmitted. Finally, the state of the transmitter mask is
|
||||
* restored. Welcome to the world of PDP-11!
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
static void dz_console_putchar(struct uart_port *uport, int ch)
|
||||
{
|
||||
struct dz_port *dport = (struct dz_port *)uport;
|
||||
unsigned long flags;
|
||||
unsigned short csr, tcr, trdy, mask;
|
||||
int loops = 10000;
|
||||
|
||||
spin_lock_irqsave(&dport->port.lock, flags);
|
||||
csr = dz_in(dport, DZ_CSR);
|
||||
dz_out(dport, DZ_CSR, csr & ~DZ_TIE);
|
||||
tcr = dz_in(dport, DZ_TCR);
|
||||
tcr |= 1 << dport->port.line;
|
||||
mask = tcr;
|
||||
dz_out(dport, DZ_TCR, mask);
|
||||
iob();
|
||||
spin_unlock_irqrestore(&dport->port.lock, flags);
|
||||
|
||||
while (loops--) {
|
||||
trdy = dz_in(dport, DZ_CSR);
|
||||
if (!(trdy & DZ_TRDY))
|
||||
continue;
|
||||
trdy = (trdy & DZ_TLINE) >> 8;
|
||||
if (trdy == dport->port.line)
|
||||
break;
|
||||
mask &= ~(1 << trdy);
|
||||
dz_out(dport, DZ_TCR, mask);
|
||||
iob();
|
||||
udelay(2);
|
||||
}
|
||||
|
||||
if (loops) /* Cannot send otherwise. */
|
||||
dz_out(dport, DZ_TDR, ch);
|
||||
|
||||
dz_out(dport, DZ_TCR, tcr);
|
||||
dz_out(dport, DZ_CSR, csr);
|
||||
}
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------------
|
||||
* dz_console_print ()
|
||||
*
|
||||
* dz_console_print is registered for printk.
|
||||
* The console must be locked when we get here.
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
static void dz_console_print(struct console *co,
|
||||
const char *str,
|
||||
unsigned int count)
|
||||
{
|
||||
struct dz_port *dport = &dz_ports[co->index];
|
||||
#ifdef DEBUG_DZ
|
||||
prom_printf((char *) str);
|
||||
#endif
|
||||
uart_console_write(&dport->port, str, count, dz_console_putchar);
|
||||
}
|
||||
|
||||
static int __init dz_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct dz_port *dport = &dz_ports[co->index];
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
dz_reset(dport);
|
||||
|
||||
return uart_set_options(&dport->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver dz_reg;
|
||||
static struct console dz_sercons = {
|
||||
.name = "ttyS",
|
||||
.write = dz_console_print,
|
||||
.device = uart_console_device,
|
||||
.setup = dz_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &dz_reg,
|
||||
};
|
||||
|
||||
static int __init dz_serial_console_init(void)
|
||||
{
|
||||
if (!IOASIC) {
|
||||
dz_init_ports();
|
||||
register_console(&dz_sercons);
|
||||
return 0;
|
||||
} else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
console_initcall(dz_serial_console_init);
|
||||
|
||||
#define SERIAL_DZ_CONSOLE &dz_sercons
|
||||
#else
|
||||
#define SERIAL_DZ_CONSOLE NULL
|
||||
#endif /* CONFIG_SERIAL_DZ_CONSOLE */
|
||||
|
||||
static struct uart_driver dz_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "serial",
|
||||
.dev_name = "ttyS",
|
||||
.major = TTY_MAJOR,
|
||||
.minor = 64,
|
||||
.nr = DZ_NB_PORT,
|
||||
.cons = SERIAL_DZ_CONSOLE,
|
||||
};
|
||||
|
||||
static int __init dz_init(void)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
if (IOASIC)
|
||||
return -ENXIO;
|
||||
|
||||
printk("%s%s\n", dz_name, dz_version);
|
||||
|
||||
dz_init_ports();
|
||||
|
||||
#ifndef CONFIG_SERIAL_DZ_CONSOLE
|
||||
/* reset the chip */
|
||||
dz_reset(&dz_ports[0]);
|
||||
#endif
|
||||
|
||||
if (request_irq(dz_ports[0].port.irq, dz_interrupt,
|
||||
IRQF_DISABLED, "DZ", &dz_ports[0]))
|
||||
panic("Unable to register DZ interrupt");
|
||||
|
||||
ret = uart_register_driver(&dz_reg);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < DZ_NB_PORT; i++)
|
||||
uart_add_one_port(&dz_reg, &dz_ports[i].port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
module_init(dz_init);
|
||||
|
||||
MODULE_DESCRIPTION("DECstation DZ serial driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
132
drivers/serial/dz.h
Normal file
132
drivers/serial/dz.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* dz.h: Serial port driver for DECstations equipped
|
||||
* with the DZ chipset.
|
||||
*
|
||||
* Copyright (C) 1998 Olivier A. D. Lebaillif
|
||||
*
|
||||
* Email: olivier.lebaillif@ifrsys.com
|
||||
*
|
||||
* Copyright (C) 2004, 2006 Maciej W. Rozycki
|
||||
*/
|
||||
#ifndef DZ_SERIAL_H
|
||||
#define DZ_SERIAL_H
|
||||
|
||||
/*
|
||||
* Definitions for the Control and Status Register.
|
||||
*/
|
||||
#define DZ_TRDY 0x8000 /* Transmitter empty */
|
||||
#define DZ_TIE 0x4000 /* Transmitter Interrupt Enbl */
|
||||
#define DZ_TLINE 0x0300 /* Transmitter Line Number */
|
||||
#define DZ_RDONE 0x0080 /* Receiver data ready */
|
||||
#define DZ_RIE 0x0040 /* Receive Interrupt Enable */
|
||||
#define DZ_MSE 0x0020 /* Master Scan Enable */
|
||||
#define DZ_CLR 0x0010 /* Master reset */
|
||||
#define DZ_MAINT 0x0008 /* Loop Back Mode */
|
||||
|
||||
/*
|
||||
* Definitions for the Receiver Buffer Register.
|
||||
*/
|
||||
#define DZ_RBUF_MASK 0x00FF /* Data Mask */
|
||||
#define DZ_LINE_MASK 0x0300 /* Line Mask */
|
||||
#define DZ_DVAL 0x8000 /* Valid Data indicator */
|
||||
#define DZ_OERR 0x4000 /* Overrun error indicator */
|
||||
#define DZ_FERR 0x2000 /* Frame error indicator */
|
||||
#define DZ_PERR 0x1000 /* Parity error indicator */
|
||||
|
||||
#define LINE(x) ((x & DZ_LINE_MASK) >> 8) /* Get the line number
|
||||
from the input buffer */
|
||||
#define UCHAR(x) ((unsigned char)(x & DZ_RBUF_MASK))
|
||||
|
||||
/*
|
||||
* Definitions for the Transmit Control Register.
|
||||
*/
|
||||
#define DZ_LINE_KEYBOARD 0x0001
|
||||
#define DZ_LINE_MOUSE 0x0002
|
||||
#define DZ_LINE_MODEM 0x0004
|
||||
#define DZ_LINE_PRINTER 0x0008
|
||||
|
||||
#define DZ_MODEM_RTS 0x0800 /* RTS for the modem line (2) */
|
||||
#define DZ_MODEM_DTR 0x0400 /* DTR for the modem line (2) */
|
||||
#define DZ_PRINT_RTS 0x0200 /* RTS for the prntr line (3) */
|
||||
#define DZ_PRINT_DTR 0x0100 /* DTR for the prntr line (3) */
|
||||
#define DZ_LNENB 0x000f /* Transmitter Line Enable */
|
||||
|
||||
/*
|
||||
* Definitions for the Modem Status Register.
|
||||
*/
|
||||
#define DZ_MODEM_RI 0x0800 /* RI for the modem line (2) */
|
||||
#define DZ_MODEM_CD 0x0400 /* CD for the modem line (2) */
|
||||
#define DZ_MODEM_DSR 0x0200 /* DSR for the modem line (2) */
|
||||
#define DZ_MODEM_CTS 0x0100 /* CTS for the modem line (2) */
|
||||
#define DZ_PRINT_RI 0x0008 /* RI for the printer line (3) */
|
||||
#define DZ_PRINT_CD 0x0004 /* CD for the printer line (3) */
|
||||
#define DZ_PRINT_DSR 0x0002 /* DSR for the prntr line (3) */
|
||||
#define DZ_PRINT_CTS 0x0001 /* CTS for the prntr line (3) */
|
||||
|
||||
/*
|
||||
* Definitions for the Transmit Data Register.
|
||||
*/
|
||||
#define DZ_BRK0 0x0100 /* Break assertion for line 0 */
|
||||
#define DZ_BRK1 0x0200 /* Break assertion for line 1 */
|
||||
#define DZ_BRK2 0x0400 /* Break assertion for line 2 */
|
||||
#define DZ_BRK3 0x0800 /* Break assertion for line 3 */
|
||||
|
||||
/*
|
||||
* Definitions for the Line Parameter Register.
|
||||
*/
|
||||
#define DZ_KEYBOARD 0x0000 /* line 0 = keyboard */
|
||||
#define DZ_MOUSE 0x0001 /* line 1 = mouse */
|
||||
#define DZ_MODEM 0x0002 /* line 2 = modem */
|
||||
#define DZ_PRINTER 0x0003 /* line 3 = printer */
|
||||
|
||||
#define DZ_CSIZE 0x0018 /* Number of bits per byte (mask) */
|
||||
#define DZ_CS5 0x0000 /* 5 bits per byte */
|
||||
#define DZ_CS6 0x0008 /* 6 bits per byte */
|
||||
#define DZ_CS7 0x0010 /* 7 bits per byte */
|
||||
#define DZ_CS8 0x0018 /* 8 bits per byte */
|
||||
|
||||
#define DZ_CSTOPB 0x0020 /* 2 stop bits instead of one */
|
||||
|
||||
#define DZ_PARENB 0x0040 /* Parity enable */
|
||||
#define DZ_PARODD 0x0080 /* Odd parity instead of even */
|
||||
|
||||
#define DZ_CBAUD 0x0E00 /* Baud Rate (mask) */
|
||||
#define DZ_B50 0x0000
|
||||
#define DZ_B75 0x0100
|
||||
#define DZ_B110 0x0200
|
||||
#define DZ_B134 0x0300
|
||||
#define DZ_B150 0x0400
|
||||
#define DZ_B300 0x0500
|
||||
#define DZ_B600 0x0600
|
||||
#define DZ_B1200 0x0700
|
||||
#define DZ_B1800 0x0800
|
||||
#define DZ_B2000 0x0900
|
||||
#define DZ_B2400 0x0A00
|
||||
#define DZ_B3600 0x0B00
|
||||
#define DZ_B4800 0x0C00
|
||||
#define DZ_B7200 0x0D00
|
||||
#define DZ_B9600 0x0E00
|
||||
|
||||
#define DZ_CREAD 0x1000 /* Enable receiver */
|
||||
#define DZ_RXENAB 0x1000 /* enable receive char */
|
||||
/*
|
||||
* Addresses for the DZ registers
|
||||
*/
|
||||
#define DZ_CSR 0x00 /* Control and Status Register */
|
||||
#define DZ_RBUF 0x08 /* Receive Buffer */
|
||||
#define DZ_LPR 0x08 /* Line Parameters Register */
|
||||
#define DZ_TCR 0x10 /* Transmitter Control Register */
|
||||
#define DZ_MSR 0x18 /* Modem Status Register */
|
||||
#define DZ_TDR 0x18 /* Transmit Data Register */
|
||||
|
||||
#define DZ_NB_PORT 4
|
||||
|
||||
#define DZ_XMIT_SIZE 4096 /* buffer size */
|
||||
#define DZ_WAKEUP_CHARS DZ_XMIT_SIZE/4
|
||||
|
||||
#ifdef MODULE
|
||||
int init_module (void)
|
||||
void cleanup_module (void)
|
||||
#endif
|
||||
|
||||
#endif /* DZ_SERIAL_H */
|
||||
1672
drivers/serial/icom.c
Normal file
1672
drivers/serial/icom.c
Normal file
File diff suppressed because it is too large
Load Diff
287
drivers/serial/icom.h
Normal file
287
drivers/serial/icom.h
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* icom.h
|
||||
*
|
||||
* Copyright (C) 2001 Michael Anderson, IBM Corporation
|
||||
*
|
||||
* Serial device driver include file.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include<linux/serial_core.h>
|
||||
|
||||
#define BAUD_TABLE_LIMIT ((sizeof(icom_acfg_baud)/sizeof(int)) - 1)
|
||||
static int icom_acfg_baud[] = {
|
||||
300,
|
||||
600,
|
||||
900,
|
||||
1200,
|
||||
1800,
|
||||
2400,
|
||||
3600,
|
||||
4800,
|
||||
7200,
|
||||
9600,
|
||||
14400,
|
||||
19200,
|
||||
28800,
|
||||
38400,
|
||||
57600,
|
||||
76800,
|
||||
115200,
|
||||
153600,
|
||||
230400,
|
||||
307200,
|
||||
460800,
|
||||
};
|
||||
|
||||
struct icom_regs {
|
||||
u32 control; /* Adapter Control Register */
|
||||
u32 interrupt; /* Adapter Interrupt Register */
|
||||
u32 int_mask; /* Adapter Interrupt Mask Reg */
|
||||
u32 int_pri; /* Adapter Interrupt Priority r */
|
||||
u32 int_reg_b; /* Adapter non-masked Interrupt */
|
||||
u32 resvd01;
|
||||
u32 resvd02;
|
||||
u32 resvd03;
|
||||
u32 control_2; /* Adapter Control Register 2 */
|
||||
u32 interrupt_2; /* Adapter Interrupt Register 2 */
|
||||
u32 int_mask_2; /* Adapter Interrupt Mask 2 */
|
||||
u32 int_pri_2; /* Adapter Interrupt Prior 2 */
|
||||
u32 int_reg_2b; /* Adapter non-masked 2 */
|
||||
};
|
||||
|
||||
struct func_dram {
|
||||
u32 reserved[108]; /* 0-1B0 reserved by personality code */
|
||||
u32 RcvStatusAddr; /* 1B0-1B3 Status Address for Next rcv */
|
||||
u8 RcvStnAddr; /* 1B4 Receive Station Addr */
|
||||
u8 IdleState; /* 1B5 Idle State */
|
||||
u8 IdleMonitor; /* 1B6 Idle Monitor */
|
||||
u8 FlagFillIdleTimer; /* 1B7 Flag Fill Idle Timer */
|
||||
u32 XmitStatusAddr; /* 1B8-1BB Transmit Status Address */
|
||||
u8 StartXmitCmd; /* 1BC Start Xmit Command */
|
||||
u8 HDLCConfigReg; /* 1BD Reserved */
|
||||
u8 CauseCode; /* 1BE Cause code for fatal error */
|
||||
u8 xchar; /* 1BF High priority send */
|
||||
u32 reserved3; /* 1C0-1C3 Reserved */
|
||||
u8 PrevCmdReg; /* 1C4 Reserved */
|
||||
u8 CmdReg; /* 1C5 Command Register */
|
||||
u8 async_config2; /* 1C6 Async Config Byte 2 */
|
||||
u8 async_config3; /* 1C7 Async Config Byte 3 */
|
||||
u8 dce_resvd[20]; /* 1C8-1DB DCE Rsvd */
|
||||
u8 dce_resvd21; /* 1DC DCE Rsvd (21st byte */
|
||||
u8 misc_flags; /* 1DD misc flags */
|
||||
#define V2_HARDWARE 0x40
|
||||
#define ICOM_HDW_ACTIVE 0x01
|
||||
u8 call_length; /* 1DE Phone #/CFI buff ln */
|
||||
u8 call_length2; /* 1DF Upper byte (unused) */
|
||||
u32 call_addr; /* 1E0-1E3 Phn #/CFI buff addr */
|
||||
u16 timer_value; /* 1E4-1E5 general timer value */
|
||||
u8 timer_command; /* 1E6 general timer cmd */
|
||||
u8 dce_command; /* 1E7 dce command reg */
|
||||
u8 dce_cmd_status; /* 1E8 dce command stat */
|
||||
u8 x21_r1_ioff; /* 1E9 dce ready counter */
|
||||
u8 x21_r0_ioff; /* 1EA dce not ready ctr */
|
||||
u8 x21_ralt_ioff; /* 1EB dce CNR counter */
|
||||
u8 x21_r1_ion; /* 1EC dce ready I on ctr */
|
||||
u8 rsvd_ier; /* 1ED Rsvd for IER (if ne */
|
||||
u8 ier; /* 1EE Interrupt Enable */
|
||||
u8 isr; /* 1EF Input Signal Reg */
|
||||
u8 osr; /* 1F0 Output Signal Reg */
|
||||
u8 reset; /* 1F1 Reset/Reload Reg */
|
||||
u8 disable; /* 1F2 Disable Reg */
|
||||
u8 sync; /* 1F3 Sync Reg */
|
||||
u8 error_stat; /* 1F4 Error Status */
|
||||
u8 cable_id; /* 1F5 Cable ID */
|
||||
u8 cs_length; /* 1F6 CS Load Length */
|
||||
u8 mac_length; /* 1F7 Mac Load Length */
|
||||
u32 cs_load_addr; /* 1F8-1FB Call Load PCI Addr */
|
||||
u32 mac_load_addr; /* 1FC-1FF Mac Load PCI Addr */
|
||||
};
|
||||
|
||||
/*
|
||||
* adapter defines and structures
|
||||
*/
|
||||
#define ICOM_CONTROL_START_A 0x00000008
|
||||
#define ICOM_CONTROL_STOP_A 0x00000004
|
||||
#define ICOM_CONTROL_START_B 0x00000002
|
||||
#define ICOM_CONTROL_STOP_B 0x00000001
|
||||
#define ICOM_CONTROL_START_C 0x00000008
|
||||
#define ICOM_CONTROL_STOP_C 0x00000004
|
||||
#define ICOM_CONTROL_START_D 0x00000002
|
||||
#define ICOM_CONTROL_STOP_D 0x00000001
|
||||
#define ICOM_IRAM_OFFSET 0x1000
|
||||
#define ICOM_IRAM_SIZE 0x0C00
|
||||
#define ICOM_DCE_IRAM_OFFSET 0x0A00
|
||||
#define ICOM_CABLE_ID_VALID 0x01
|
||||
#define ICOM_CABLE_ID_MASK 0xF0
|
||||
#define ICOM_DISABLE 0x80
|
||||
#define CMD_XMIT_RCV_ENABLE 0xC0
|
||||
#define CMD_XMIT_ENABLE 0x40
|
||||
#define CMD_RCV_DISABLE 0x00
|
||||
#define CMD_RCV_ENABLE 0x80
|
||||
#define CMD_RESTART 0x01
|
||||
#define CMD_HOLD_XMIT 0x02
|
||||
#define CMD_SND_BREAK 0x04
|
||||
#define RS232_CABLE 0x06
|
||||
#define V24_CABLE 0x0E
|
||||
#define V35_CABLE 0x0C
|
||||
#define V36_CABLE 0x02
|
||||
#define NO_CABLE 0x00
|
||||
#define START_DOWNLOAD 0x80
|
||||
#define ICOM_INT_MASK_PRC_A 0x00003FFF
|
||||
#define ICOM_INT_MASK_PRC_B 0x3FFF0000
|
||||
#define ICOM_INT_MASK_PRC_C 0x00003FFF
|
||||
#define ICOM_INT_MASK_PRC_D 0x3FFF0000
|
||||
#define INT_RCV_COMPLETED 0x1000
|
||||
#define INT_XMIT_COMPLETED 0x2000
|
||||
#define INT_IDLE_DETECT 0x0800
|
||||
#define INT_RCV_DISABLED 0x0400
|
||||
#define INT_XMIT_DISABLED 0x0200
|
||||
#define INT_RCV_XMIT_SHUTDOWN 0x0100
|
||||
#define INT_FATAL_ERROR 0x0080
|
||||
#define INT_CABLE_PULL 0x0020
|
||||
#define INT_SIGNAL_CHANGE 0x0010
|
||||
#define HDLC_PPP_PURE_ASYNC 0x02
|
||||
#define HDLC_FF_FILL 0x00
|
||||
#define HDLC_HDW_FLOW 0x01
|
||||
#define START_XMIT 0x80
|
||||
#define ICOM_ACFG_DRIVE1 0x20
|
||||
#define ICOM_ACFG_NO_PARITY 0x00
|
||||
#define ICOM_ACFG_PARITY_ENAB 0x02
|
||||
#define ICOM_ACFG_PARITY_ODD 0x01
|
||||
#define ICOM_ACFG_8BPC 0x00
|
||||
#define ICOM_ACFG_7BPC 0x04
|
||||
#define ICOM_ACFG_6BPC 0x08
|
||||
#define ICOM_ACFG_5BPC 0x0C
|
||||
#define ICOM_ACFG_1STOP_BIT 0x00
|
||||
#define ICOM_ACFG_2STOP_BIT 0x10
|
||||
#define ICOM_DTR 0x80
|
||||
#define ICOM_RTS 0x40
|
||||
#define ICOM_RI 0x08
|
||||
#define ICOM_DSR 0x80
|
||||
#define ICOM_DCD 0x20
|
||||
#define ICOM_CTS 0x40
|
||||
|
||||
#define NUM_XBUFFS 1
|
||||
#define NUM_RBUFFS 2
|
||||
#define RCV_BUFF_SZ 0x0200
|
||||
#define XMIT_BUFF_SZ 0x1000
|
||||
struct statusArea {
|
||||
/**********************************************/
|
||||
/* Transmit Status Area */
|
||||
/**********************************************/
|
||||
struct xmit_status_area{
|
||||
u32 leNext; /* Next entry in Little Endian on Adapter */
|
||||
u32 leNextASD;
|
||||
u32 leBuffer; /* Buffer for entry in LE for Adapter */
|
||||
u16 leLengthASD;
|
||||
u16 leOffsetASD;
|
||||
u16 leLength; /* Length of data in segment */
|
||||
u16 flags;
|
||||
#define SA_FLAGS_DONE 0x0080 /* Done with Segment */
|
||||
#define SA_FLAGS_CONTINUED 0x8000 /* More Segments */
|
||||
#define SA_FLAGS_IDLE 0x4000 /* Mark IDLE after frm */
|
||||
#define SA_FLAGS_READY_TO_XMIT 0x0800
|
||||
#define SA_FLAGS_STAT_MASK 0x007F
|
||||
} xmit[NUM_XBUFFS];
|
||||
|
||||
/**********************************************/
|
||||
/* Receive Status Area */
|
||||
/**********************************************/
|
||||
struct {
|
||||
u32 leNext; /* Next entry in Little Endian on Adapter */
|
||||
u32 leNextASD;
|
||||
u32 leBuffer; /* Buffer for entry in LE for Adapter */
|
||||
u16 WorkingLength; /* size of segment */
|
||||
u16 reserv01;
|
||||
u16 leLength; /* Length of data in segment */
|
||||
u16 flags;
|
||||
#define SA_FL_RCV_DONE 0x0010 /* Data ready */
|
||||
#define SA_FLAGS_OVERRUN 0x0040
|
||||
#define SA_FLAGS_PARITY_ERROR 0x0080
|
||||
#define SA_FLAGS_FRAME_ERROR 0x0001
|
||||
#define SA_FLAGS_FRAME_TRUNC 0x0002
|
||||
#define SA_FLAGS_BREAK_DET 0x0004 /* set conditionally by device driver, not hardware */
|
||||
#define SA_FLAGS_RCV_MASK 0xFFE6
|
||||
} rcv[NUM_RBUFFS];
|
||||
};
|
||||
|
||||
struct icom_adapter;
|
||||
|
||||
|
||||
#define ICOM_MAJOR 243
|
||||
#define ICOM_MINOR_START 0
|
||||
|
||||
struct icom_port {
|
||||
struct uart_port uart_port;
|
||||
u8 imbed_modem;
|
||||
#define ICOM_UNKNOWN 1
|
||||
#define ICOM_RVX 2
|
||||
#define ICOM_IMBED_MODEM 3
|
||||
unsigned char cable_id;
|
||||
unsigned char read_status_mask;
|
||||
unsigned char ignore_status_mask;
|
||||
void __iomem * int_reg;
|
||||
struct icom_regs __iomem *global_reg;
|
||||
struct func_dram __iomem *dram;
|
||||
int port;
|
||||
struct statusArea *statStg;
|
||||
dma_addr_t statStg_pci;
|
||||
u32 *xmitRestart;
|
||||
dma_addr_t xmitRestart_pci;
|
||||
unsigned char *xmit_buf;
|
||||
dma_addr_t xmit_buf_pci;
|
||||
unsigned char *recv_buf;
|
||||
dma_addr_t recv_buf_pci;
|
||||
int next_rcv;
|
||||
int put_length;
|
||||
int status;
|
||||
#define ICOM_PORT_ACTIVE 1 /* Port exists. */
|
||||
#define ICOM_PORT_OFF 0 /* Port does not exist. */
|
||||
int load_in_progress;
|
||||
struct icom_adapter *adapter;
|
||||
};
|
||||
|
||||
struct icom_adapter {
|
||||
void __iomem * base_addr;
|
||||
unsigned long base_addr_pci;
|
||||
struct pci_dev *pci_dev;
|
||||
struct icom_port port_info[4];
|
||||
int index;
|
||||
int version;
|
||||
#define ADAPTER_V1 0x0001
|
||||
#define ADAPTER_V2 0x0002
|
||||
u32 subsystem_id;
|
||||
#define FOUR_PORT_MODEL 0x0252
|
||||
#define V2_TWO_PORTS_RVX 0x021A
|
||||
#define V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM 0x0251
|
||||
int numb_ports;
|
||||
struct list_head icom_adapter_entry;
|
||||
struct kobject kobj;
|
||||
};
|
||||
|
||||
/* prototype */
|
||||
extern void iCom_sercons_init(void);
|
||||
|
||||
struct lookup_proc_table {
|
||||
u32 __iomem *global_control_reg;
|
||||
unsigned long processor_id;
|
||||
};
|
||||
|
||||
struct lookup_int_table {
|
||||
u32 __iomem *global_int_mask;
|
||||
unsigned long processor_id;
|
||||
};
|
||||
984
drivers/serial/imx.c
Normal file
984
drivers/serial/imx.c
Normal file
@@ -0,0 +1,984 @@
|
||||
/*
|
||||
* linux/drivers/serial/imx.c
|
||||
*
|
||||
* Driver for Motorola IMX serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* Author: Sascha Hauer <sascha@saschahauer.de>
|
||||
* Copyright (C) 2004 Pengutronix
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* [29-Mar-2005] Mike Lee
|
||||
* Added hardware handshake
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/imx-uart.h>
|
||||
|
||||
/* We've been assigned a range on the "Low-density serial ports" major */
|
||||
#define SERIAL_IMX_MAJOR 204
|
||||
#define MINOR_START 41
|
||||
|
||||
#define NR_PORTS 2
|
||||
|
||||
#define IMX_ISR_PASS_LIMIT 256
|
||||
|
||||
/*
|
||||
* This is the size of our serial port register set.
|
||||
*/
|
||||
#define UART_PORT_SIZE 0x100
|
||||
|
||||
/*
|
||||
* This determines how often we check the modem status signals
|
||||
* for any change. They generally aren't connected to an IRQ
|
||||
* so we have to poll them. We also check immediately before
|
||||
* filling the TX fifo incase CTS has been dropped.
|
||||
*/
|
||||
#define MCTRL_TIMEOUT (250*HZ/1000)
|
||||
|
||||
#define DRIVER_NAME "IMX-uart"
|
||||
|
||||
struct imx_port {
|
||||
struct uart_port port;
|
||||
struct timer_list timer;
|
||||
unsigned int old_status;
|
||||
int txirq,rxirq,rtsirq;
|
||||
int have_rtscts:1;
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle any change of modem status signal since we were last called.
|
||||
*/
|
||||
static void imx_mctrl_check(struct imx_port *sport)
|
||||
{
|
||||
unsigned int status, changed;
|
||||
|
||||
status = sport->port.ops->get_mctrl(&sport->port);
|
||||
changed = status ^ sport->old_status;
|
||||
|
||||
if (changed == 0)
|
||||
return;
|
||||
|
||||
sport->old_status = status;
|
||||
|
||||
if (changed & TIOCM_RI)
|
||||
sport->port.icount.rng++;
|
||||
if (changed & TIOCM_DSR)
|
||||
sport->port.icount.dsr++;
|
||||
if (changed & TIOCM_CAR)
|
||||
uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
|
||||
if (changed & TIOCM_CTS)
|
||||
uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
|
||||
|
||||
wake_up_interruptible(&sport->port.info->delta_msr_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is our per-port timeout handler, for checking the
|
||||
* modem status signals.
|
||||
*/
|
||||
static void imx_timeout(unsigned long data)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)data;
|
||||
unsigned long flags;
|
||||
|
||||
if (sport->port.info) {
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
imx_mctrl_check(sport);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupts disabled on entry
|
||||
*/
|
||||
static void imx_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
UCR1((u32)sport->port.membase) &= ~UCR1_TXMPTYEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupts disabled on entry
|
||||
*/
|
||||
static void imx_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
UCR2((u32)sport->port.membase) &= ~UCR2_RXEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the modem control timer to fire immediately.
|
||||
*/
|
||||
static void imx_enable_ms(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
mod_timer(&sport->timer, jiffies);
|
||||
}
|
||||
|
||||
static inline void imx_transmit_buffer(struct imx_port *sport)
|
||||
{
|
||||
struct circ_buf *xmit = &sport->port.info->xmit;
|
||||
|
||||
while (!(UTS((u32)sport->port.membase) & UTS_TXFULL)) {
|
||||
/* send xmit->buf[xmit->tail]
|
||||
* out the port here */
|
||||
URTX0((u32)sport->port.membase) = xmit->buf[xmit->tail];
|
||||
xmit->tail = (xmit->tail + 1) &
|
||||
(UART_XMIT_SIZE - 1);
|
||||
sport->port.icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
imx_stop_tx(&sport->port);
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupts disabled on entry
|
||||
*/
|
||||
static void imx_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
UCR1((u32)sport->port.membase) |= UCR1_TXMPTYEN;
|
||||
|
||||
imx_transmit_buffer(sport);
|
||||
}
|
||||
|
||||
static irqreturn_t imx_rtsint(int irq, void *dev_id)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)dev_id;
|
||||
unsigned int val = USR1((u32)sport->port.membase)&USR1_RTSS;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
USR1((u32)sport->port.membase) = USR1_RTSD;
|
||||
uart_handle_cts_change(&sport->port, !!val);
|
||||
wake_up_interruptible(&sport->port.info->delta_msr_wait);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t imx_txint(int irq, void *dev_id)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)dev_id;
|
||||
struct circ_buf *xmit = &sport->port.info->xmit;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock,flags);
|
||||
if (sport->port.x_char)
|
||||
{
|
||||
/* Send next char */
|
||||
URTX0((u32)sport->port.membase) = sport->port.x_char;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
|
||||
imx_stop_tx(&sport->port);
|
||||
goto out;
|
||||
}
|
||||
|
||||
imx_transmit_buffer(sport);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&sport->port);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&sport->port.lock,flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t imx_rxint(int irq, void *dev_id)
|
||||
{
|
||||
struct imx_port *sport = dev_id;
|
||||
unsigned int rx,flg,ignored = 0;
|
||||
struct tty_struct *tty = sport->port.info->tty;
|
||||
unsigned long flags;
|
||||
|
||||
rx = URXD0((u32)sport->port.membase);
|
||||
spin_lock_irqsave(&sport->port.lock,flags);
|
||||
|
||||
do {
|
||||
flg = TTY_NORMAL;
|
||||
sport->port.icount.rx++;
|
||||
|
||||
if( USR2((u32)sport->port.membase) & USR2_BRCD ) {
|
||||
USR2((u32)sport->port.membase) |= USR2_BRCD;
|
||||
if(uart_handle_break(&sport->port))
|
||||
goto ignore_char;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char
|
||||
(&sport->port, (unsigned char)rx))
|
||||
goto ignore_char;
|
||||
|
||||
if( rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) )
|
||||
goto handle_error;
|
||||
|
||||
error_return:
|
||||
tty_insert_flip_char(tty, rx, flg);
|
||||
|
||||
ignore_char:
|
||||
rx = URXD0((u32)sport->port.membase);
|
||||
} while(rx & URXD_CHARRDY);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&sport->port.lock,flags);
|
||||
tty_flip_buffer_push(tty);
|
||||
return IRQ_HANDLED;
|
||||
|
||||
handle_error:
|
||||
if (rx & URXD_PRERR)
|
||||
sport->port.icount.parity++;
|
||||
else if (rx & URXD_FRMERR)
|
||||
sport->port.icount.frame++;
|
||||
if (rx & URXD_OVRRUN)
|
||||
sport->port.icount.overrun++;
|
||||
|
||||
if (rx & sport->port.ignore_status_mask) {
|
||||
if (++ignored > 100)
|
||||
goto out;
|
||||
goto ignore_char;
|
||||
}
|
||||
|
||||
rx &= sport->port.read_status_mask;
|
||||
|
||||
if (rx & URXD_PRERR)
|
||||
flg = TTY_PARITY;
|
||||
else if (rx & URXD_FRMERR)
|
||||
flg = TTY_FRAME;
|
||||
if (rx & URXD_OVRRUN)
|
||||
flg = TTY_OVERRUN;
|
||||
|
||||
#ifdef SUPPORT_SYSRQ
|
||||
sport->port.sysrq = 0;
|
||||
#endif
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TIOCSER_TEMT when transmitter is not busy.
|
||||
*/
|
||||
static unsigned int imx_tx_empty(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
return USR2((u32)sport->port.membase) & USR2_TXDC ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a modem side uart, so the meanings of RTS and CTS are inverted.
|
||||
*/
|
||||
static unsigned int imx_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
|
||||
|
||||
if (USR1((u32)sport->port.membase) & USR1_RTSS)
|
||||
tmp |= TIOCM_CTS;
|
||||
|
||||
if (UCR2((u32)sport->port.membase) & UCR2_CTS)
|
||||
tmp |= TIOCM_RTS;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
if (mctrl & TIOCM_RTS)
|
||||
UCR2((u32)sport->port.membase) |= UCR2_CTS;
|
||||
else
|
||||
UCR2((u32)sport->port.membase) &= ~UCR2_CTS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts always disabled.
|
||||
*/
|
||||
static void imx_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
if ( break_state != 0 )
|
||||
UCR1((u32)sport->port.membase) |= UCR1_SNDBRK;
|
||||
else
|
||||
UCR1((u32)sport->port.membase) &= ~UCR1_SNDBRK;
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
#define TXTL 2 /* reset default */
|
||||
#define RXTL 1 /* reset default */
|
||||
|
||||
static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
|
||||
{
|
||||
unsigned int val;
|
||||
unsigned int ufcr_rfdiv;
|
||||
|
||||
/* set receiver / transmitter trigger level.
|
||||
* RFDIV is set such way to satisfy requested uartclk value
|
||||
*/
|
||||
val = TXTL<<10 | RXTL;
|
||||
ufcr_rfdiv = (imx_get_perclk1() + sport->port.uartclk / 2) / sport->port.uartclk;
|
||||
|
||||
if(!ufcr_rfdiv)
|
||||
ufcr_rfdiv = 1;
|
||||
|
||||
if(ufcr_rfdiv >= 7)
|
||||
ufcr_rfdiv = 6;
|
||||
else
|
||||
ufcr_rfdiv = 6 - ufcr_rfdiv;
|
||||
|
||||
val |= UFCR_RFDIV & (ufcr_rfdiv << 7);
|
||||
|
||||
UFCR((u32)sport->port.membase) = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_startup(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
int retval;
|
||||
unsigned long flags;
|
||||
|
||||
imx_setup_ufcr(sport, 0);
|
||||
|
||||
/* disable the DREN bit (Data Ready interrupt enable) before
|
||||
* requesting IRQs
|
||||
*/
|
||||
UCR4((u32)sport->port.membase) &= ~UCR4_DREN;
|
||||
|
||||
/*
|
||||
* Allocate the IRQ
|
||||
*/
|
||||
retval = request_irq(sport->rxirq, imx_rxint, 0,
|
||||
DRIVER_NAME, sport);
|
||||
if (retval) goto error_out1;
|
||||
|
||||
retval = request_irq(sport->txirq, imx_txint, 0,
|
||||
DRIVER_NAME, sport);
|
||||
if (retval) goto error_out2;
|
||||
|
||||
retval = request_irq(sport->rtsirq, imx_rtsint,
|
||||
(sport->rtsirq < IMX_IRQS) ? 0 :
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
DRIVER_NAME, sport);
|
||||
if (retval) goto error_out3;
|
||||
|
||||
/*
|
||||
* Finally, clear and enable interrupts
|
||||
*/
|
||||
|
||||
USR1((u32)sport->port.membase) = USR1_RTSD;
|
||||
UCR1((u32)sport->port.membase) |=
|
||||
(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
|
||||
|
||||
UCR2((u32)sport->port.membase) |= (UCR2_RXEN | UCR2_TXEN);
|
||||
/*
|
||||
* Enable modem status interrupts
|
||||
*/
|
||||
spin_lock_irqsave(&sport->port.lock,flags);
|
||||
imx_enable_ms(&sport->port);
|
||||
spin_unlock_irqrestore(&sport->port.lock,flags);
|
||||
|
||||
return 0;
|
||||
|
||||
error_out3:
|
||||
free_irq(sport->txirq, sport);
|
||||
error_out2:
|
||||
free_irq(sport->rxirq, sport);
|
||||
error_out1:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void imx_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
/*
|
||||
* Stop our timer.
|
||||
*/
|
||||
del_timer_sync(&sport->timer);
|
||||
|
||||
/*
|
||||
* Free the interrupts
|
||||
*/
|
||||
free_irq(sport->rtsirq, sport);
|
||||
free_irq(sport->txirq, sport);
|
||||
free_irq(sport->rxirq, sport);
|
||||
|
||||
/*
|
||||
* Disable all interrupts, port and break condition.
|
||||
*/
|
||||
|
||||
UCR1((u32)sport->port.membase) &=
|
||||
~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
|
||||
}
|
||||
|
||||
static void
|
||||
imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long flags;
|
||||
unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
|
||||
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
|
||||
|
||||
/*
|
||||
* If we don't support modem control lines, don't allow
|
||||
* these to be set.
|
||||
*/
|
||||
if (0) {
|
||||
termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
|
||||
termios->c_cflag |= CLOCAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only support CS7 and CS8.
|
||||
*/
|
||||
while ((termios->c_cflag & CSIZE) != CS7 &&
|
||||
(termios->c_cflag & CSIZE) != CS8) {
|
||||
termios->c_cflag &= ~CSIZE;
|
||||
termios->c_cflag |= old_csize;
|
||||
old_csize = CS8;
|
||||
}
|
||||
|
||||
if ((termios->c_cflag & CSIZE) == CS8)
|
||||
ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS;
|
||||
else
|
||||
ucr2 = UCR2_SRST | UCR2_IRTS;
|
||||
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
if( sport->have_rtscts ) {
|
||||
ucr2 &= ~UCR2_IRTS;
|
||||
ucr2 |= UCR2_CTSC;
|
||||
} else {
|
||||
termios->c_cflag &= ~CRTSCTS;
|
||||
}
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
ucr2 |= UCR2_STPB;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
ucr2 |= UCR2_PREN;
|
||||
if (termios->c_cflag & PARODD)
|
||||
ucr2 |= UCR2_PROE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
sport->port.read_status_mask = 0;
|
||||
if (termios->c_iflag & INPCK)
|
||||
sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
sport->port.read_status_mask |= URXD_BRK;
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
sport->port.ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
sport->port.ignore_status_mask |= URXD_PRERR;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
sport->port.ignore_status_mask |= URXD_BRK;
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
sport->port.ignore_status_mask |= URXD_OVRRUN;
|
||||
}
|
||||
|
||||
del_timer_sync(&sport->timer);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/*
|
||||
* disable interrupts and drain transmitter
|
||||
*/
|
||||
old_ucr1 = UCR1((u32)sport->port.membase);
|
||||
UCR1((u32)sport->port.membase) &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
|
||||
|
||||
while ( !(USR2((u32)sport->port.membase) & USR2_TXDC))
|
||||
barrier();
|
||||
|
||||
/* then, disable everything */
|
||||
old_txrxen = UCR2((u32)sport->port.membase) & ( UCR2_TXEN | UCR2_RXEN );
|
||||
UCR2((u32)sport->port.membase) &= ~( UCR2_TXEN | UCR2_RXEN);
|
||||
|
||||
/* set the parity, stop bits and data size */
|
||||
UCR2((u32)sport->port.membase) = ucr2;
|
||||
|
||||
/* set the baud rate. We assume uartclk = 16 MHz
|
||||
*
|
||||
* baud * 16 UBIR - 1
|
||||
* --------- = --------
|
||||
* uartclk UBMR - 1
|
||||
*/
|
||||
UBIR((u32)sport->port.membase) = (baud / 100) - 1;
|
||||
UBMR((u32)sport->port.membase) = 10000 - 1;
|
||||
|
||||
UCR1((u32)sport->port.membase) = old_ucr1;
|
||||
UCR2((u32)sport->port.membase) |= old_txrxen;
|
||||
|
||||
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
|
||||
imx_enable_ms(&sport->port);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static const char *imx_type(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
return sport->port.type == PORT_IMX ? "IMX" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory region(s) being used by 'port'.
|
||||
*/
|
||||
static void imx_release_port(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Request the memory region(s) being used by 'port'.
|
||||
*/
|
||||
static int imx_request_port(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
|
||||
"imx-uart") != NULL ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void imx_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
|
||||
if (flags & UART_CONFIG_TYPE &&
|
||||
imx_request_port(&sport->port) == 0)
|
||||
sport->port.type = PORT_IMX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the new serial_struct (for TIOCSSERIAL).
|
||||
* The only change we allow are to the flags and type, and
|
||||
* even then only between PORT_IMX and PORT_UNKNOWN
|
||||
*/
|
||||
static int
|
||||
imx_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
int ret = 0;
|
||||
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.irq != ser->irq)
|
||||
ret = -EINVAL;
|
||||
if (ser->io_type != UPIO_MEM)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.uartclk / 16 != ser->baud_base)
|
||||
ret = -EINVAL;
|
||||
if ((void *)sport->port.mapbase != ser->iomem_base)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.iobase != ser->port)
|
||||
ret = -EINVAL;
|
||||
if (ser->hub6 != 0)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops imx_pops = {
|
||||
.tx_empty = imx_tx_empty,
|
||||
.set_mctrl = imx_set_mctrl,
|
||||
.get_mctrl = imx_get_mctrl,
|
||||
.stop_tx = imx_stop_tx,
|
||||
.start_tx = imx_start_tx,
|
||||
.stop_rx = imx_stop_rx,
|
||||
.enable_ms = imx_enable_ms,
|
||||
.break_ctl = imx_break_ctl,
|
||||
.startup = imx_startup,
|
||||
.shutdown = imx_shutdown,
|
||||
.set_termios = imx_set_termios,
|
||||
.type = imx_type,
|
||||
.release_port = imx_release_port,
|
||||
.request_port = imx_request_port,
|
||||
.config_port = imx_config_port,
|
||||
.verify_port = imx_verify_port,
|
||||
};
|
||||
|
||||
static struct imx_port imx_ports[] = {
|
||||
{
|
||||
.txirq = UART1_MINT_TX,
|
||||
.rxirq = UART1_MINT_RX,
|
||||
.rtsirq = UART1_MINT_RTS,
|
||||
.port = {
|
||||
.type = PORT_IMX,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (void *)IMX_UART1_BASE,
|
||||
.mapbase = IMX_UART1_BASE, /* FIXME */
|
||||
.irq = UART1_MINT_RX,
|
||||
.uartclk = 16000000,
|
||||
.fifosize = 32,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.ops = &imx_pops,
|
||||
.line = 0,
|
||||
},
|
||||
}, {
|
||||
.txirq = UART2_MINT_TX,
|
||||
.rxirq = UART2_MINT_RX,
|
||||
.rtsirq = UART2_MINT_RTS,
|
||||
.port = {
|
||||
.type = PORT_IMX,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (void *)IMX_UART2_BASE,
|
||||
.mapbase = IMX_UART2_BASE, /* FIXME */
|
||||
.irq = UART2_MINT_RX,
|
||||
.uartclk = 16000000,
|
||||
.fifosize = 32,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.ops = &imx_pops,
|
||||
.line = 1,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Setup the IMX serial ports.
|
||||
* Note also that we support "console=ttySMXx" where "x" is either 0 or 1.
|
||||
* Which serial port this ends up being depends on the machine you're
|
||||
* running this kernel on. I'm not convinced that this is a good idea,
|
||||
* but that's the way it traditionally works.
|
||||
*
|
||||
*/
|
||||
static void __init imx_init_ports(void)
|
||||
{
|
||||
static int first = 1;
|
||||
int i;
|
||||
|
||||
if (!first)
|
||||
return;
|
||||
first = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(imx_ports); i++) {
|
||||
init_timer(&imx_ports[i].timer);
|
||||
imx_ports[i].timer.function = imx_timeout;
|
||||
imx_ports[i].timer.data = (unsigned long)&imx_ports[i];
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_IMX_CONSOLE
|
||||
static void imx_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
while ((UTS((u32)sport->port.membase) & UTS_TXFULL))
|
||||
barrier();
|
||||
URTX0((u32)sport->port.membase) = ch;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts are disabled on entering
|
||||
*/
|
||||
static void
|
||||
imx_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct imx_port *sport = &imx_ports[co->index];
|
||||
unsigned int old_ucr1, old_ucr2;
|
||||
|
||||
/*
|
||||
* First, save UCR1/2 and then disable interrupts
|
||||
*/
|
||||
old_ucr1 = UCR1((u32)sport->port.membase);
|
||||
old_ucr2 = UCR2((u32)sport->port.membase);
|
||||
|
||||
UCR1((u32)sport->port.membase) =
|
||||
(old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN)
|
||||
& ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
|
||||
UCR2((u32)sport->port.membase) = old_ucr2 | UCR2_TXEN;
|
||||
|
||||
uart_console_write(&sport->port, s, count, imx_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore UCR1/2
|
||||
*/
|
||||
while (!(USR2((u32)sport->port.membase) & USR2_TXDC));
|
||||
|
||||
UCR1((u32)sport->port.membase) = old_ucr1;
|
||||
UCR2((u32)sport->port.membase) = old_ucr2;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the port was already initialised (eg, by a boot loader),
|
||||
* try to determine the current setup.
|
||||
*/
|
||||
static void __init
|
||||
imx_console_get_options(struct imx_port *sport, int *baud,
|
||||
int *parity, int *bits)
|
||||
{
|
||||
|
||||
if ( UCR1((u32)sport->port.membase) | UCR1_UARTEN ) {
|
||||
/* ok, the port was enabled */
|
||||
unsigned int ucr2, ubir,ubmr, uartclk;
|
||||
unsigned int baud_raw;
|
||||
unsigned int ucfr_rfdiv;
|
||||
|
||||
ucr2 = UCR2((u32)sport->port.membase);
|
||||
|
||||
*parity = 'n';
|
||||
if (ucr2 & UCR2_PREN) {
|
||||
if (ucr2 & UCR2_PROE)
|
||||
*parity = 'o';
|
||||
else
|
||||
*parity = 'e';
|
||||
}
|
||||
|
||||
if (ucr2 & UCR2_WS)
|
||||
*bits = 8;
|
||||
else
|
||||
*bits = 7;
|
||||
|
||||
ubir = UBIR((u32)sport->port.membase) & 0xffff;
|
||||
ubmr = UBMR((u32)sport->port.membase) & 0xffff;
|
||||
|
||||
|
||||
ucfr_rfdiv = (UFCR((u32)sport->port.membase) & UFCR_RFDIV) >> 7;
|
||||
if (ucfr_rfdiv == 6)
|
||||
ucfr_rfdiv = 7;
|
||||
else
|
||||
ucfr_rfdiv = 6 - ucfr_rfdiv;
|
||||
|
||||
uartclk = imx_get_perclk1();
|
||||
uartclk /= ucfr_rfdiv;
|
||||
|
||||
{ /*
|
||||
* The next code provides exact computation of
|
||||
* baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1))
|
||||
* without need of float support or long long division,
|
||||
* which would be required to prevent 32bit arithmetic overflow
|
||||
*/
|
||||
unsigned int mul = ubir + 1;
|
||||
unsigned int div = 16 * (ubmr + 1);
|
||||
unsigned int rem = uartclk % div;
|
||||
|
||||
baud_raw = (uartclk / div) * mul;
|
||||
baud_raw += (rem * mul + div / 2) / div;
|
||||
*baud = (baud_raw + 50) / 100 * 100;
|
||||
}
|
||||
|
||||
if(*baud != baud_raw)
|
||||
printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n",
|
||||
baud_raw, *baud);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init
|
||||
imx_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct imx_port *sport;
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
|
||||
co->index = 0;
|
||||
sport = &imx_ports[co->index];
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
imx_console_get_options(sport, &baud, &parity, &bits);
|
||||
|
||||
imx_setup_ufcr(sport, 0);
|
||||
|
||||
return uart_set_options(&sport->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver imx_reg;
|
||||
static struct console imx_console = {
|
||||
.name = "ttySMX",
|
||||
.write = imx_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = imx_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &imx_reg,
|
||||
};
|
||||
|
||||
static int __init imx_rs_console_init(void)
|
||||
{
|
||||
imx_init_ports();
|
||||
register_console(&imx_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(imx_rs_console_init);
|
||||
|
||||
#define IMX_CONSOLE &imx_console
|
||||
#else
|
||||
#define IMX_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver imx_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = DRIVER_NAME,
|
||||
.dev_name = "ttySMX",
|
||||
.major = SERIAL_IMX_MAJOR,
|
||||
.minor = MINOR_START,
|
||||
.nr = ARRAY_SIZE(imx_ports),
|
||||
.cons = IMX_CONSOLE,
|
||||
};
|
||||
|
||||
static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct imx_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
uart_suspend_port(&imx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_imx_resume(struct platform_device *dev)
|
||||
{
|
||||
struct imx_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
uart_resume_port(&imx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_imx_probe(struct platform_device *dev)
|
||||
{
|
||||
struct imxuart_platform_data *pdata;
|
||||
|
||||
imx_ports[dev->id].port.dev = &dev->dev;
|
||||
|
||||
pdata = (struct imxuart_platform_data *)dev->dev.platform_data;
|
||||
if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
|
||||
imx_ports[dev->id].have_rtscts = 1;
|
||||
|
||||
uart_add_one_port(&imx_reg, &imx_ports[dev->id].port);
|
||||
platform_set_drvdata(dev, &imx_ports[dev->id]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_imx_remove(struct platform_device *dev)
|
||||
{
|
||||
struct imx_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
platform_set_drvdata(dev, NULL);
|
||||
|
||||
if (sport)
|
||||
uart_remove_one_port(&imx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver serial_imx_driver = {
|
||||
.probe = serial_imx_probe,
|
||||
.remove = serial_imx_remove,
|
||||
|
||||
.suspend = serial_imx_suspend,
|
||||
.resume = serial_imx_resume,
|
||||
.driver = {
|
||||
.name = "imx-uart",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init imx_serial_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "Serial: IMX driver\n");
|
||||
|
||||
imx_init_ports();
|
||||
|
||||
ret = uart_register_driver(&imx_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&serial_imx_driver);
|
||||
if (ret != 0)
|
||||
uart_unregister_driver(&imx_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit imx_serial_exit(void)
|
||||
{
|
||||
uart_unregister_driver(&imx_reg);
|
||||
platform_driver_unregister(&serial_imx_driver);
|
||||
}
|
||||
|
||||
module_init(imx_serial_init);
|
||||
module_exit(imx_serial_exit);
|
||||
|
||||
MODULE_AUTHOR("Sascha Hauer");
|
||||
MODULE_DESCRIPTION("IMX generic serial port driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
2193
drivers/serial/ioc3_serial.c
Normal file
2193
drivers/serial/ioc3_serial.c
Normal file
File diff suppressed because it is too large
Load Diff
2940
drivers/serial/ioc4_serial.c
Normal file
2940
drivers/serial/ioc4_serial.c
Normal file
File diff suppressed because it is too large
Load Diff
1253
drivers/serial/ip22zilog.c
Normal file
1253
drivers/serial/ip22zilog.c
Normal file
File diff suppressed because it is too large
Load Diff
281
drivers/serial/ip22zilog.h
Normal file
281
drivers/serial/ip22zilog.h
Normal file
@@ -0,0 +1,281 @@
|
||||
#ifndef _IP22_ZILOG_H
|
||||
#define _IP22_ZILOG_H
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
struct zilog_channel {
|
||||
#ifdef __BIG_ENDIAN
|
||||
volatile unsigned char unused0[3];
|
||||
volatile unsigned char control;
|
||||
volatile unsigned char unused1[3];
|
||||
volatile unsigned char data;
|
||||
#else /* __LITTLE_ENDIAN */
|
||||
volatile unsigned char control;
|
||||
volatile unsigned char unused0[3];
|
||||
volatile unsigned char data;
|
||||
volatile unsigned char unused1[3];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct zilog_layout {
|
||||
struct zilog_channel channelB;
|
||||
struct zilog_channel channelA;
|
||||
};
|
||||
|
||||
#define NUM_ZSREGS 16
|
||||
|
||||
/* Conversion routines to/from brg time constants from/to bits
|
||||
* per second.
|
||||
*/
|
||||
#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
|
||||
#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
|
||||
|
||||
/* The Zilog register set */
|
||||
|
||||
#define FLAG 0x7e
|
||||
|
||||
/* Write Register 0 */
|
||||
#define R0 0 /* Register selects */
|
||||
#define R1 1
|
||||
#define R2 2
|
||||
#define R3 3
|
||||
#define R4 4
|
||||
#define R5 5
|
||||
#define R6 6
|
||||
#define R7 7
|
||||
#define R8 8
|
||||
#define R9 9
|
||||
#define R10 10
|
||||
#define R11 11
|
||||
#define R12 12
|
||||
#define R13 13
|
||||
#define R14 14
|
||||
#define R15 15
|
||||
|
||||
#define NULLCODE 0 /* Null Code */
|
||||
#define POINT_HIGH 0x8 /* Select upper half of registers */
|
||||
#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
|
||||
#define SEND_ABORT 0x18 /* HDLC Abort */
|
||||
#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
|
||||
#define RES_Tx_P 0x28 /* Reset TxINT Pending */
|
||||
#define ERR_RES 0x30 /* Error Reset */
|
||||
#define RES_H_IUS 0x38 /* Reset highest IUS */
|
||||
|
||||
#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
|
||||
#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
|
||||
#define RES_EOM_L 0xC0 /* Reset EOM latch */
|
||||
|
||||
/* Write Register 1 */
|
||||
|
||||
#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
|
||||
#define TxINT_ENAB 0x2 /* Tx Int Enable */
|
||||
#define PAR_SPEC 0x4 /* Parity is special condition */
|
||||
|
||||
#define RxINT_DISAB 0 /* Rx Int Disable */
|
||||
#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
|
||||
#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
|
||||
#define INT_ERR_Rx 0x18 /* Int on error only */
|
||||
#define RxINT_MASK 0x18
|
||||
|
||||
#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
|
||||
#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
|
||||
#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
|
||||
|
||||
/* Write Register #2 (Interrupt Vector) */
|
||||
|
||||
/* Write Register 3 */
|
||||
|
||||
#define RxENAB 0x1 /* Rx Enable */
|
||||
#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
|
||||
#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
|
||||
#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
|
||||
#define ENT_HM 0x10 /* Enter Hunt Mode */
|
||||
#define AUTO_ENAB 0x20 /* Auto Enables */
|
||||
#define Rx5 0x0 /* Rx 5 Bits/Character */
|
||||
#define Rx7 0x40 /* Rx 7 Bits/Character */
|
||||
#define Rx6 0x80 /* Rx 6 Bits/Character */
|
||||
#define Rx8 0xc0 /* Rx 8 Bits/Character */
|
||||
#define RxN_MASK 0xc0
|
||||
|
||||
/* Write Register 4 */
|
||||
|
||||
#define PAR_ENAB 0x1 /* Parity Enable */
|
||||
#define PAR_EVEN 0x2 /* Parity Even/Odd* */
|
||||
|
||||
#define SYNC_ENAB 0 /* Sync Modes Enable */
|
||||
#define SB1 0x4 /* 1 stop bit/char */
|
||||
#define SB15 0x8 /* 1.5 stop bits/char */
|
||||
#define SB2 0xc /* 2 stop bits/char */
|
||||
|
||||
#define MONSYNC 0 /* 8 Bit Sync character */
|
||||
#define BISYNC 0x10 /* 16 bit sync character */
|
||||
#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
|
||||
#define EXTSYNC 0x30 /* External Sync Mode */
|
||||
|
||||
#define X1CLK 0x0 /* x1 clock mode */
|
||||
#define X16CLK 0x40 /* x16 clock mode */
|
||||
#define X32CLK 0x80 /* x32 clock mode */
|
||||
#define X64CLK 0xC0 /* x64 clock mode */
|
||||
#define XCLK_MASK 0xC0
|
||||
|
||||
/* Write Register 5 */
|
||||
|
||||
#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
|
||||
#define RTS 0x2 /* RTS */
|
||||
#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
|
||||
#define TxENAB 0x8 /* Tx Enable */
|
||||
#define SND_BRK 0x10 /* Send Break */
|
||||
#define Tx5 0x0 /* Tx 5 bits (or less)/character */
|
||||
#define Tx7 0x20 /* Tx 7 bits/character */
|
||||
#define Tx6 0x40 /* Tx 6 bits/character */
|
||||
#define Tx8 0x60 /* Tx 8 bits/character */
|
||||
#define TxN_MASK 0x60
|
||||
#define DTR 0x80 /* DTR */
|
||||
|
||||
/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
|
||||
|
||||
/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
|
||||
|
||||
/* Write Register 8 (transmit buffer) */
|
||||
|
||||
/* Write Register 9 (Master interrupt control) */
|
||||
#define VIS 1 /* Vector Includes Status */
|
||||
#define NV 2 /* No Vector */
|
||||
#define DLC 4 /* Disable Lower Chain */
|
||||
#define MIE 8 /* Master Interrupt Enable */
|
||||
#define STATHI 0x10 /* Status high */
|
||||
#define NORESET 0 /* No reset on write to R9 */
|
||||
#define CHRB 0x40 /* Reset channel B */
|
||||
#define CHRA 0x80 /* Reset channel A */
|
||||
#define FHWRES 0xc0 /* Force hardware reset */
|
||||
|
||||
/* Write Register 10 (misc control bits) */
|
||||
#define BIT6 1 /* 6 bit/8bit sync */
|
||||
#define LOOPMODE 2 /* SDLC Loop mode */
|
||||
#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
|
||||
#define MARKIDLE 8 /* Mark/flag on idle */
|
||||
#define GAOP 0x10 /* Go active on poll */
|
||||
#define NRZ 0 /* NRZ mode */
|
||||
#define NRZI 0x20 /* NRZI mode */
|
||||
#define FM1 0x40 /* FM1 (transition = 1) */
|
||||
#define FM0 0x60 /* FM0 (transition = 0) */
|
||||
#define CRCPS 0x80 /* CRC Preset I/O */
|
||||
|
||||
/* Write Register 11 (Clock Mode control) */
|
||||
#define TRxCXT 0 /* TRxC = Xtal output */
|
||||
#define TRxCTC 1 /* TRxC = Transmit clock */
|
||||
#define TRxCBR 2 /* TRxC = BR Generator Output */
|
||||
#define TRxCDP 3 /* TRxC = DPLL output */
|
||||
#define TRxCOI 4 /* TRxC O/I */
|
||||
#define TCRTxCP 0 /* Transmit clock = RTxC pin */
|
||||
#define TCTRxCP 8 /* Transmit clock = TRxC pin */
|
||||
#define TCBR 0x10 /* Transmit clock = BR Generator output */
|
||||
#define TCDPLL 0x18 /* Transmit clock = DPLL output */
|
||||
#define RCRTxCP 0 /* Receive clock = RTxC pin */
|
||||
#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
|
||||
#define RCBR 0x40 /* Receive clock = BR Generator output */
|
||||
#define RCDPLL 0x60 /* Receive clock = DPLL output */
|
||||
#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
|
||||
|
||||
/* Write Register 12 (lower byte of baud rate generator time constant) */
|
||||
|
||||
/* Write Register 13 (upper byte of baud rate generator time constant) */
|
||||
|
||||
/* Write Register 14 (Misc control bits) */
|
||||
#define BRENAB 1 /* Baud rate generator enable */
|
||||
#define BRSRC 2 /* Baud rate generator source */
|
||||
#define DTRREQ 4 /* DTR/Request function */
|
||||
#define AUTOECHO 8 /* Auto Echo */
|
||||
#define LOOPBAK 0x10 /* Local loopback */
|
||||
#define SEARCH 0x20 /* Enter search mode */
|
||||
#define RMC 0x40 /* Reset missing clock */
|
||||
#define DISDPLL 0x60 /* Disable DPLL */
|
||||
#define SSBR 0x80 /* Set DPLL source = BR generator */
|
||||
#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
|
||||
#define SFMM 0xc0 /* Set FM mode */
|
||||
#define SNRZI 0xe0 /* Set NRZI mode */
|
||||
|
||||
/* Write Register 15 (external/status interrupt control) */
|
||||
#define ZCIE 2 /* Zero count IE */
|
||||
#define DCDIE 8 /* DCD IE */
|
||||
#define SYNCIE 0x10 /* Sync/hunt IE */
|
||||
#define CTSIE 0x20 /* CTS IE */
|
||||
#define TxUIE 0x40 /* Tx Underrun/EOM IE */
|
||||
#define BRKIE 0x80 /* Break/Abort IE */
|
||||
|
||||
|
||||
/* Read Register 0 */
|
||||
#define Rx_CH_AV 0x1 /* Rx Character Available */
|
||||
#define ZCOUNT 0x2 /* Zero count */
|
||||
#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
|
||||
#define DCD 0x8 /* DCD */
|
||||
#define SYNC 0x10 /* Sync/hunt */
|
||||
#define CTS 0x20 /* CTS */
|
||||
#define TxEOM 0x40 /* Tx underrun */
|
||||
#define BRK_ABRT 0x80 /* Break/Abort */
|
||||
|
||||
/* Read Register 1 */
|
||||
#define ALL_SNT 0x1 /* All sent */
|
||||
/* Residue Data for 8 Rx bits/char programmed */
|
||||
#define RES3 0x8 /* 0/3 */
|
||||
#define RES4 0x4 /* 0/4 */
|
||||
#define RES5 0xc /* 0/5 */
|
||||
#define RES6 0x2 /* 0/6 */
|
||||
#define RES7 0xa /* 0/7 */
|
||||
#define RES8 0x6 /* 0/8 */
|
||||
#define RES18 0xe /* 1/8 */
|
||||
#define RES28 0x0 /* 2/8 */
|
||||
/* Special Rx Condition Interrupts */
|
||||
#define PAR_ERR 0x10 /* Parity error */
|
||||
#define Rx_OVR 0x20 /* Rx Overrun Error */
|
||||
#define CRC_ERR 0x40 /* CRC/Framing Error */
|
||||
#define END_FR 0x80 /* End of Frame (SDLC) */
|
||||
|
||||
/* Read Register 2 (channel b only) - Interrupt vector */
|
||||
#define CHB_Tx_EMPTY 0x00
|
||||
#define CHB_EXT_STAT 0x02
|
||||
#define CHB_Rx_AVAIL 0x04
|
||||
#define CHB_SPECIAL 0x06
|
||||
#define CHA_Tx_EMPTY 0x08
|
||||
#define CHA_EXT_STAT 0x0a
|
||||
#define CHA_Rx_AVAIL 0x0c
|
||||
#define CHA_SPECIAL 0x0e
|
||||
#define STATUS_MASK 0x0e
|
||||
|
||||
/* Read Register 3 (interrupt pending register) ch a only */
|
||||
#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
|
||||
#define CHBTxIP 0x2 /* Channel B Tx IP */
|
||||
#define CHBRxIP 0x4 /* Channel B Rx IP */
|
||||
#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
|
||||
#define CHATxIP 0x10 /* Channel A Tx IP */
|
||||
#define CHARxIP 0x20 /* Channel A Rx IP */
|
||||
|
||||
/* Read Register 8 (receive data register) */
|
||||
|
||||
/* Read Register 10 (misc status bits) */
|
||||
#define ONLOOP 2 /* On loop */
|
||||
#define LOOPSEND 0x10 /* Loop sending */
|
||||
#define CLK2MIS 0x40 /* Two clocks missing */
|
||||
#define CLK1MIS 0x80 /* One clock missing */
|
||||
|
||||
/* Read Register 12 (lower byte of baud rate generator constant) */
|
||||
|
||||
/* Read Register 13 (upper byte of baud rate generator constant) */
|
||||
|
||||
/* Read Register 15 (value of WR 15) */
|
||||
|
||||
/* Misc macros */
|
||||
#define ZS_CLEARERR(channel) do { writeb(ERR_RES, &channel->control); \
|
||||
udelay(5); } while(0)
|
||||
|
||||
#define ZS_CLEARSTAT(channel) do { writeb(RES_EXT_INT, &channel->control); \
|
||||
udelay(5); } while(0)
|
||||
|
||||
#define ZS_CLEARFIFO(channel) do { readb(&channel->data); \
|
||||
udelay(2); \
|
||||
readb(&channel->data); \
|
||||
udelay(2); \
|
||||
readb(&channel->data); \
|
||||
udelay(2); } while(0)
|
||||
|
||||
#endif /* _IP22_ZILOG_H */
|
||||
8
drivers/serial/jsm/Makefile
Normal file
8
drivers/serial/jsm/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Makefile for Jasmine adapter
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SERIAL_JSM) += jsm.o
|
||||
|
||||
jsm-objs := jsm_driver.o jsm_neo.o jsm_tty.o
|
||||
|
||||
396
drivers/serial/jsm/jsm.h
Normal file
396
drivers/serial/jsm/jsm.h
Normal file
@@ -0,0 +1,396 @@
|
||||
/************************************************************************
|
||||
* Copyright 2003 Digi International (www.digi.com)
|
||||
*
|
||||
* Copyright (C) 2004 IBM 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; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*
|
||||
* Contact Information:
|
||||
* Scott H Kilau <Scott_Kilau@digi.com>
|
||||
* Wendy Xiong <wendyx@us.ibm.com>
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef __JSM_DRIVER_H
|
||||
#define __JSM_DRIVER_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h> /* To pick up the varions Linux types */
|
||||
#include <linux/tty.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
/*
|
||||
* Debugging levels can be set using debug insmod variable
|
||||
* They can also be compiled out completely.
|
||||
*/
|
||||
enum {
|
||||
DBG_INIT = 0x01,
|
||||
DBG_BASIC = 0x02,
|
||||
DBG_CORE = 0x04,
|
||||
DBG_OPEN = 0x08,
|
||||
DBG_CLOSE = 0x10,
|
||||
DBG_READ = 0x20,
|
||||
DBG_WRITE = 0x40,
|
||||
DBG_IOCTL = 0x80,
|
||||
DBG_PROC = 0x100,
|
||||
DBG_PARAM = 0x200,
|
||||
DBG_PSCAN = 0x400,
|
||||
DBG_EVENT = 0x800,
|
||||
DBG_DRAIN = 0x1000,
|
||||
DBG_MSIGS = 0x2000,
|
||||
DBG_MGMT = 0x4000,
|
||||
DBG_INTR = 0x8000,
|
||||
DBG_CARR = 0x10000,
|
||||
};
|
||||
|
||||
#define jsm_printk(nlevel, klevel, pdev, fmt, args...) \
|
||||
if ((DBG_##nlevel & jsm_debug)) \
|
||||
dev_printk(KERN_##klevel, pdev->dev, fmt, ## args)
|
||||
|
||||
#define MAXPORTS 8
|
||||
#define MAX_STOPS_SENT 5
|
||||
|
||||
/* Board type definitions */
|
||||
|
||||
#define T_NEO 0000
|
||||
#define T_CLASSIC 0001
|
||||
#define T_PCIBUS 0400
|
||||
|
||||
/* Board State Definitions */
|
||||
|
||||
#define BD_RUNNING 0x0
|
||||
#define BD_REASON 0x7f
|
||||
#define BD_NOTFOUND 0x1
|
||||
#define BD_NOIOPORT 0x2
|
||||
#define BD_NOMEM 0x3
|
||||
#define BD_NOBIOS 0x4
|
||||
#define BD_NOFEP 0x5
|
||||
#define BD_FAILED 0x6
|
||||
#define BD_ALLOCATED 0x7
|
||||
#define BD_TRIBOOT 0x8
|
||||
#define BD_BADKME 0x80
|
||||
|
||||
|
||||
/* 4 extra for alignment play space */
|
||||
#define WRITEBUFLEN ((4096) + 4)
|
||||
#define MYFLIPLEN N_TTY_BUF_SIZE
|
||||
|
||||
#define JSM_VERSION "jsm: 1.2-1-INKERNEL"
|
||||
#define JSM_PARTNUM "40002438_A-INKERNEL"
|
||||
|
||||
struct jsm_board;
|
||||
struct jsm_channel;
|
||||
|
||||
/************************************************************************
|
||||
* Per board operations structure *
|
||||
************************************************************************/
|
||||
struct board_ops {
|
||||
irq_handler_t intr;
|
||||
void (*uart_init) (struct jsm_channel *ch);
|
||||
void (*uart_off) (struct jsm_channel *ch);
|
||||
void (*param) (struct jsm_channel *ch);
|
||||
void (*assert_modem_signals) (struct jsm_channel *ch);
|
||||
void (*flush_uart_write) (struct jsm_channel *ch);
|
||||
void (*flush_uart_read) (struct jsm_channel *ch);
|
||||
void (*disable_receiver) (struct jsm_channel *ch);
|
||||
void (*enable_receiver) (struct jsm_channel *ch);
|
||||
void (*send_break) (struct jsm_channel *ch);
|
||||
void (*clear_break) (struct jsm_channel *ch, int);
|
||||
void (*send_start_character) (struct jsm_channel *ch);
|
||||
void (*send_stop_character) (struct jsm_channel *ch);
|
||||
void (*copy_data_from_queue_to_uart) (struct jsm_channel *ch);
|
||||
u32 (*get_uart_bytes_left) (struct jsm_channel *ch);
|
||||
void (*send_immediate_char) (struct jsm_channel *ch, unsigned char);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Per-board information
|
||||
*/
|
||||
struct jsm_board
|
||||
{
|
||||
int boardnum; /* Board number: 0-32 */
|
||||
|
||||
int type; /* Type of board */
|
||||
u8 rev; /* PCI revision ID */
|
||||
struct pci_dev *pci_dev;
|
||||
u32 maxports; /* MAX ports this board can handle */
|
||||
|
||||
spinlock_t bd_lock; /* Used to protect board */
|
||||
|
||||
spinlock_t bd_intr_lock; /* Used to protect the poller tasklet and
|
||||
* the interrupt routine from each other.
|
||||
*/
|
||||
|
||||
u32 nasync; /* Number of ports on card */
|
||||
|
||||
u32 irq; /* Interrupt request number */
|
||||
u64 intr_count; /* Count of interrupts */
|
||||
|
||||
u64 membase; /* Start of base memory of the card */
|
||||
u64 membase_end; /* End of base memory of the card */
|
||||
|
||||
u8 __iomem *re_map_membase;/* Remapped memory of the card */
|
||||
|
||||
u64 iobase; /* Start of io base of the card */
|
||||
u64 iobase_end; /* End of io base of the card */
|
||||
|
||||
u32 bd_uart_offset; /* Space between each UART */
|
||||
|
||||
struct jsm_channel *channels[MAXPORTS]; /* array of pointers to our channels. */
|
||||
char *flipbuf; /* Our flip buffer, alloced if board is found */
|
||||
|
||||
u32 bd_dividend; /* Board/UARTs specific dividend */
|
||||
|
||||
struct board_ops *bd_ops;
|
||||
|
||||
struct list_head jsm_board_entry;
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* Device flag definitions for ch_flags.
|
||||
************************************************************************/
|
||||
#define CH_PRON 0x0001 /* Printer on string */
|
||||
#define CH_STOP 0x0002 /* Output is stopped */
|
||||
#define CH_STOPI 0x0004 /* Input is stopped */
|
||||
#define CH_CD 0x0008 /* Carrier is present */
|
||||
#define CH_FCAR 0x0010 /* Carrier forced on */
|
||||
#define CH_HANGUP 0x0020 /* Hangup received */
|
||||
|
||||
#define CH_RECEIVER_OFF 0x0040 /* Receiver is off */
|
||||
#define CH_OPENING 0x0080 /* Port in fragile open state */
|
||||
#define CH_CLOSING 0x0100 /* Port in fragile close state */
|
||||
#define CH_FIFO_ENABLED 0x0200 /* Port has FIFOs enabled */
|
||||
#define CH_TX_FIFO_EMPTY 0x0400 /* TX Fifo is completely empty */
|
||||
#define CH_TX_FIFO_LWM 0x0800 /* TX Fifo is below Low Water */
|
||||
#define CH_BREAK_SENDING 0x1000 /* Break is being sent */
|
||||
#define CH_LOOPBACK 0x2000 /* Channel is in lookback mode */
|
||||
#define CH_FLIPBUF_IN_USE 0x4000 /* Channel's flipbuf is in use */
|
||||
#define CH_BAUD0 0x08000 /* Used for checking B0 transitions */
|
||||
|
||||
/* Our Read/Error/Write queue sizes */
|
||||
#define RQUEUEMASK 0x1FFF /* 8 K - 1 */
|
||||
#define EQUEUEMASK 0x1FFF /* 8 K - 1 */
|
||||
#define WQUEUEMASK 0x0FFF /* 4 K - 1 */
|
||||
#define RQUEUESIZE (RQUEUEMASK + 1)
|
||||
#define EQUEUESIZE RQUEUESIZE
|
||||
#define WQUEUESIZE (WQUEUEMASK + 1)
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Channel information structure.
|
||||
************************************************************************/
|
||||
struct jsm_channel {
|
||||
struct uart_port uart_port;
|
||||
struct jsm_board *ch_bd; /* Board structure pointer */
|
||||
|
||||
spinlock_t ch_lock; /* provide for serialization */
|
||||
wait_queue_head_t ch_flags_wait;
|
||||
|
||||
u32 ch_portnum; /* Port number, 0 offset. */
|
||||
u32 ch_open_count; /* open count */
|
||||
u32 ch_flags; /* Channel flags */
|
||||
|
||||
u64 ch_close_delay; /* How long we should drop RTS/DTR for */
|
||||
|
||||
u64 ch_cpstime; /* Time for CPS calculations */
|
||||
|
||||
tcflag_t ch_c_iflag; /* channel iflags */
|
||||
tcflag_t ch_c_cflag; /* channel cflags */
|
||||
tcflag_t ch_c_oflag; /* channel oflags */
|
||||
tcflag_t ch_c_lflag; /* channel lflags */
|
||||
u8 ch_stopc; /* Stop character */
|
||||
u8 ch_startc; /* Start character */
|
||||
|
||||
u32 ch_old_baud; /* Cache of the current baud */
|
||||
u32 ch_custom_speed;/* Custom baud, if set */
|
||||
|
||||
u32 ch_wopen; /* Waiting for open process cnt */
|
||||
|
||||
u8 ch_mostat; /* FEP output modem status */
|
||||
u8 ch_mistat; /* FEP input modem status */
|
||||
|
||||
struct neo_uart_struct __iomem *ch_neo_uart; /* Pointer to the "mapped" UART struct */
|
||||
u8 ch_cached_lsr; /* Cached value of the LSR register */
|
||||
|
||||
u8 *ch_rqueue; /* Our read queue buffer - malloc'ed */
|
||||
u16 ch_r_head; /* Head location of the read queue */
|
||||
u16 ch_r_tail; /* Tail location of the read queue */
|
||||
|
||||
u8 *ch_equeue; /* Our error queue buffer - malloc'ed */
|
||||
u16 ch_e_head; /* Head location of the error queue */
|
||||
u16 ch_e_tail; /* Tail location of the error queue */
|
||||
|
||||
u8 *ch_wqueue; /* Our write queue buffer - malloc'ed */
|
||||
u16 ch_w_head; /* Head location of the write queue */
|
||||
u16 ch_w_tail; /* Tail location of the write queue */
|
||||
|
||||
u64 ch_rxcount; /* total of data received so far */
|
||||
u64 ch_txcount; /* total of data transmitted so far */
|
||||
|
||||
u8 ch_r_tlevel; /* Receive Trigger level */
|
||||
u8 ch_t_tlevel; /* Transmit Trigger level */
|
||||
|
||||
u8 ch_r_watermark; /* Receive Watermark */
|
||||
|
||||
|
||||
u32 ch_stops_sent; /* How many times I have sent a stop character
|
||||
* to try to stop the other guy sending.
|
||||
*/
|
||||
u64 ch_err_parity; /* Count of parity errors on channel */
|
||||
u64 ch_err_frame; /* Count of framing errors on channel */
|
||||
u64 ch_err_break; /* Count of breaks on channel */
|
||||
u64 ch_err_overrun; /* Count of overruns on channel */
|
||||
|
||||
u64 ch_xon_sends; /* Count of xons transmitted */
|
||||
u64 ch_xoff_sends; /* Count of xoffs transmitted */
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Per channel/port NEO UART structure *
|
||||
************************************************************************
|
||||
* Base Structure Entries Usage Meanings to Host *
|
||||
* *
|
||||
* W = read write R = read only *
|
||||
* U = Unused. *
|
||||
************************************************************************/
|
||||
|
||||
struct neo_uart_struct {
|
||||
u8 txrx; /* WR RHR/THR - Holding Reg */
|
||||
u8 ier; /* WR IER - Interrupt Enable Reg */
|
||||
u8 isr_fcr; /* WR ISR/FCR - Interrupt Status Reg/Fifo Control Reg */
|
||||
u8 lcr; /* WR LCR - Line Control Reg */
|
||||
u8 mcr; /* WR MCR - Modem Control Reg */
|
||||
u8 lsr; /* WR LSR - Line Status Reg */
|
||||
u8 msr; /* WR MSR - Modem Status Reg */
|
||||
u8 spr; /* WR SPR - Scratch Pad Reg */
|
||||
u8 fctr; /* WR FCTR - Feature Control Reg */
|
||||
u8 efr; /* WR EFR - Enhanced Function Reg */
|
||||
u8 tfifo; /* WR TXCNT/TXTRG - Transmit FIFO Reg */
|
||||
u8 rfifo; /* WR RXCNT/RXTRG - Recieve FIFO Reg */
|
||||
u8 xoffchar1; /* WR XOFF 1 - XOff Character 1 Reg */
|
||||
u8 xoffchar2; /* WR XOFF 2 - XOff Character 2 Reg */
|
||||
u8 xonchar1; /* WR XON 1 - Xon Character 1 Reg */
|
||||
u8 xonchar2; /* WR XON 2 - XOn Character 2 Reg */
|
||||
|
||||
u8 reserved1[0x2ff - 0x200]; /* U Reserved by Exar */
|
||||
u8 txrxburst[64]; /* RW 64 bytes of RX/TX FIFO Data */
|
||||
u8 reserved2[0x37f - 0x340]; /* U Reserved by Exar */
|
||||
u8 rxburst_with_errors[64]; /* R 64 bytes of RX FIFO Data + LSR */
|
||||
};
|
||||
|
||||
/* Where to read the extended interrupt register (32bits instead of 8bits) */
|
||||
#define UART_17158_POLL_ADDR_OFFSET 0x80
|
||||
|
||||
/*
|
||||
* These are the redefinitions for the FCTR on the XR17C158, since
|
||||
* Exar made them different than their earlier design. (XR16C854)
|
||||
*/
|
||||
|
||||
/* These are only applicable when table D is selected */
|
||||
#define UART_17158_FCTR_RTS_NODELAY 0x00
|
||||
#define UART_17158_FCTR_RTS_4DELAY 0x01
|
||||
#define UART_17158_FCTR_RTS_6DELAY 0x02
|
||||
#define UART_17158_FCTR_RTS_8DELAY 0x03
|
||||
#define UART_17158_FCTR_RTS_12DELAY 0x12
|
||||
#define UART_17158_FCTR_RTS_16DELAY 0x05
|
||||
#define UART_17158_FCTR_RTS_20DELAY 0x13
|
||||
#define UART_17158_FCTR_RTS_24DELAY 0x06
|
||||
#define UART_17158_FCTR_RTS_28DELAY 0x14
|
||||
#define UART_17158_FCTR_RTS_32DELAY 0x07
|
||||
#define UART_17158_FCTR_RTS_36DELAY 0x16
|
||||
#define UART_17158_FCTR_RTS_40DELAY 0x08
|
||||
#define UART_17158_FCTR_RTS_44DELAY 0x09
|
||||
#define UART_17158_FCTR_RTS_48DELAY 0x10
|
||||
#define UART_17158_FCTR_RTS_52DELAY 0x11
|
||||
|
||||
#define UART_17158_FCTR_RTS_IRDA 0x10
|
||||
#define UART_17158_FCTR_RS485 0x20
|
||||
#define UART_17158_FCTR_TRGA 0x00
|
||||
#define UART_17158_FCTR_TRGB 0x40
|
||||
#define UART_17158_FCTR_TRGC 0x80
|
||||
#define UART_17158_FCTR_TRGD 0xC0
|
||||
|
||||
/* 17158 trigger table selects.. */
|
||||
#define UART_17158_FCTR_BIT6 0x40
|
||||
#define UART_17158_FCTR_BIT7 0x80
|
||||
|
||||
/* 17158 TX/RX memmapped buffer offsets */
|
||||
#define UART_17158_RX_FIFOSIZE 64
|
||||
#define UART_17158_TX_FIFOSIZE 64
|
||||
|
||||
/* 17158 Extended IIR's */
|
||||
#define UART_17158_IIR_RDI_TIMEOUT 0x0C /* Receiver data TIMEOUT */
|
||||
#define UART_17158_IIR_XONXOFF 0x10 /* Received an XON/XOFF char */
|
||||
#define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20 /* CTS/DSR or RTS/DTR state change */
|
||||
#define UART_17158_IIR_FIFO_ENABLED 0xC0 /* 16550 FIFOs are Enabled */
|
||||
|
||||
/*
|
||||
* These are the extended interrupts that get sent
|
||||
* back to us from the UART's 32bit interrupt register
|
||||
*/
|
||||
#define UART_17158_RX_LINE_STATUS 0x1 /* RX Ready */
|
||||
#define UART_17158_RXRDY_TIMEOUT 0x2 /* RX Ready Timeout */
|
||||
#define UART_17158_TXRDY 0x3 /* TX Ready */
|
||||
#define UART_17158_MSR 0x4 /* Modem State Change */
|
||||
#define UART_17158_TX_AND_FIFO_CLR 0x40 /* Transmitter Holding Reg Empty */
|
||||
#define UART_17158_RX_FIFO_DATA_ERROR 0x80 /* UART detected an RX FIFO Data error */
|
||||
|
||||
/*
|
||||
* These are the EXTENDED definitions for the 17C158's Interrupt
|
||||
* Enable Register.
|
||||
*/
|
||||
#define UART_17158_EFR_ECB 0x10 /* Enhanced control bit */
|
||||
#define UART_17158_EFR_IXON 0x2 /* Receiver compares Xon1/Xoff1 */
|
||||
#define UART_17158_EFR_IXOFF 0x8 /* Transmit Xon1/Xoff1 */
|
||||
#define UART_17158_EFR_RTSDTR 0x40 /* Auto RTS/DTR Flow Control Enable */
|
||||
#define UART_17158_EFR_CTSDSR 0x80 /* Auto CTS/DSR Flow COntrol Enable */
|
||||
|
||||
#define UART_17158_XOFF_DETECT 0x1 /* Indicates whether chip saw an incoming XOFF char */
|
||||
#define UART_17158_XON_DETECT 0x2 /* Indicates whether chip saw an incoming XON char */
|
||||
|
||||
#define UART_17158_IER_RSVD1 0x10 /* Reserved by Exar */
|
||||
#define UART_17158_IER_XOFF 0x20 /* Xoff Interrupt Enable */
|
||||
#define UART_17158_IER_RTSDTR 0x40 /* Output Interrupt Enable */
|
||||
#define UART_17158_IER_CTSDSR 0x80 /* Input Interrupt Enable */
|
||||
|
||||
#define PCI_DEVICE_NEO_2DB9_PCI_NAME "Neo 2 - DB9 Universal PCI"
|
||||
#define PCI_DEVICE_NEO_2DB9PRI_PCI_NAME "Neo 2 - DB9 Universal PCI - Powered Ring Indicator"
|
||||
#define PCI_DEVICE_NEO_2RJ45_PCI_NAME "Neo 2 - RJ45 Universal PCI"
|
||||
#define PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME "Neo 2 - RJ45 Universal PCI - Powered Ring Indicator"
|
||||
|
||||
/*
|
||||
* Our Global Variables.
|
||||
*/
|
||||
extern struct uart_driver jsm_uart_driver;
|
||||
extern struct board_ops jsm_neo_ops;
|
||||
extern int jsm_debug;
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Prototypes for non-static functions used in more than one module
|
||||
*
|
||||
*************************************************************************/
|
||||
int jsm_tty_write(struct uart_port *port);
|
||||
int jsm_tty_init(struct jsm_board *);
|
||||
int jsm_uart_port_init(struct jsm_board *);
|
||||
int jsm_remove_uart_port(struct jsm_board *);
|
||||
void jsm_input(struct jsm_channel *ch);
|
||||
void jsm_check_queue_flow_control(struct jsm_channel *ch);
|
||||
|
||||
#endif
|
||||
242
drivers/serial/jsm/jsm_driver.c
Normal file
242
drivers/serial/jsm/jsm_driver.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/************************************************************************
|
||||
* Copyright 2003 Digi International (www.digi.com)
|
||||
*
|
||||
* Copyright (C) 2004 IBM 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; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*
|
||||
* Contact Information:
|
||||
* Scott H Kilau <Scott_Kilau@digi.com>
|
||||
* Wendy Xiong <wendyx@us.ibm.com>
|
||||
*
|
||||
*
|
||||
***********************************************************************/
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "jsm.h"
|
||||
|
||||
MODULE_AUTHOR("Digi International, http://www.digi.com");
|
||||
MODULE_DESCRIPTION("Driver for the Digi International "
|
||||
"Neo PCI based product line");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_SUPPORTED_DEVICE("jsm");
|
||||
|
||||
#define JSM_DRIVER_NAME "jsm"
|
||||
#define NR_PORTS 32
|
||||
#define JSM_MINOR_START 0
|
||||
|
||||
struct uart_driver jsm_uart_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = JSM_DRIVER_NAME,
|
||||
.dev_name = "ttyn",
|
||||
.major = 0,
|
||||
.minor = JSM_MINOR_START,
|
||||
.nr = NR_PORTS,
|
||||
};
|
||||
|
||||
int jsm_debug;
|
||||
module_param(jsm_debug, int, 0);
|
||||
MODULE_PARM_DESC(jsm_debug, "Driver debugging level");
|
||||
|
||||
static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
int rc = 0;
|
||||
struct jsm_board *brd;
|
||||
static int adapter_count = 0;
|
||||
int retval;
|
||||
|
||||
rc = pci_enable_device(pdev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "Device enable FAILED\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = pci_request_regions(pdev, "jsm");
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "pci_request_region FAILED\n");
|
||||
goto out_disable_device;
|
||||
}
|
||||
|
||||
brd = kzalloc(sizeof(struct jsm_board), GFP_KERNEL);
|
||||
if (!brd) {
|
||||
dev_err(&pdev->dev,
|
||||
"memory allocation for board structure failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto out_release_regions;
|
||||
}
|
||||
|
||||
/* store the info for the board we've found */
|
||||
brd->boardnum = adapter_count++;
|
||||
brd->pci_dev = pdev;
|
||||
brd->maxports = 2;
|
||||
|
||||
spin_lock_init(&brd->bd_lock);
|
||||
spin_lock_init(&brd->bd_intr_lock);
|
||||
|
||||
/* store which revision we have */
|
||||
pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
|
||||
|
||||
brd->irq = pdev->irq;
|
||||
|
||||
jsm_printk(INIT, INFO, &brd->pci_dev,
|
||||
"jsm_found_board - NEO adapter\n");
|
||||
|
||||
/* get the PCI Base Address Registers */
|
||||
brd->membase = pci_resource_start(pdev, 0);
|
||||
brd->membase_end = pci_resource_end(pdev, 0);
|
||||
|
||||
if (brd->membase & 1)
|
||||
brd->membase &= ~3;
|
||||
else
|
||||
brd->membase &= ~15;
|
||||
|
||||
/* Assign the board_ops struct */
|
||||
brd->bd_ops = &jsm_neo_ops;
|
||||
|
||||
brd->bd_uart_offset = 0x200;
|
||||
brd->bd_dividend = 921600;
|
||||
|
||||
brd->re_map_membase = ioremap(brd->membase, 0x1000);
|
||||
if (!brd->re_map_membase) {
|
||||
dev_err(&pdev->dev,
|
||||
"card has no PCI Memory resources, "
|
||||
"failing board.\n");
|
||||
rc = -ENOMEM;
|
||||
goto out_kfree_brd;
|
||||
}
|
||||
|
||||
rc = request_irq(brd->irq, brd->bd_ops->intr,
|
||||
IRQF_DISABLED|IRQF_SHARED, "JSM", brd);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
|
||||
goto out_iounmap;
|
||||
}
|
||||
|
||||
rc = jsm_tty_init(brd);
|
||||
if (rc < 0) {
|
||||
dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
|
||||
retval = -ENXIO;
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
rc = jsm_uart_port_init(brd);
|
||||
if (rc < 0) {
|
||||
/* XXX: leaking all resources from jsm_tty_init here! */
|
||||
dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc);
|
||||
retval = -ENXIO;
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
/* Log the information about the board */
|
||||
dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n",
|
||||
adapter_count, brd->rev, brd->irq);
|
||||
|
||||
/*
|
||||
* allocate flip buffer for board.
|
||||
*
|
||||
* Okay to malloc with GFP_KERNEL, we are not at interrupt
|
||||
* context, and there are no locks held.
|
||||
*/
|
||||
brd->flipbuf = kzalloc(MYFLIPLEN, GFP_KERNEL);
|
||||
if (!brd->flipbuf) {
|
||||
/* XXX: leaking all resources from jsm_tty_init and
|
||||
jsm_uart_port_init here! */
|
||||
dev_err(&pdev->dev, "memory allocation for flipbuf failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, brd);
|
||||
|
||||
return 0;
|
||||
out_free_irq:
|
||||
free_irq(brd->irq, brd);
|
||||
out_iounmap:
|
||||
iounmap(brd->re_map_membase);
|
||||
out_kfree_brd:
|
||||
kfree(brd);
|
||||
out_release_regions:
|
||||
pci_release_regions(pdev);
|
||||
out_disable_device:
|
||||
pci_disable_device(pdev);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void jsm_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
struct jsm_board *brd = pci_get_drvdata(pdev);
|
||||
int i = 0;
|
||||
|
||||
jsm_remove_uart_port(brd);
|
||||
|
||||
free_irq(brd->irq, brd);
|
||||
iounmap(brd->re_map_membase);
|
||||
|
||||
/* Free all allocated channels structs */
|
||||
for (i = 0; i < brd->maxports; i++) {
|
||||
if (brd->channels[i]) {
|
||||
kfree(brd->channels[i]->ch_rqueue);
|
||||
kfree(brd->channels[i]->ch_equeue);
|
||||
kfree(brd->channels[i]->ch_wqueue);
|
||||
kfree(brd->channels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
kfree(brd->flipbuf);
|
||||
kfree(brd);
|
||||
}
|
||||
|
||||
static struct pci_device_id jsm_pci_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
|
||||
|
||||
static struct pci_driver jsm_driver = {
|
||||
.name = "jsm",
|
||||
.id_table = jsm_pci_tbl,
|
||||
.probe = jsm_probe_one,
|
||||
.remove = __devexit_p(jsm_remove_one),
|
||||
};
|
||||
|
||||
static int __init jsm_init_module(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = uart_register_driver(&jsm_uart_driver);
|
||||
if (!rc) {
|
||||
rc = pci_register_driver(&jsm_driver);
|
||||
if (rc)
|
||||
uart_unregister_driver(&jsm_uart_driver);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit jsm_exit_module(void)
|
||||
{
|
||||
pci_unregister_driver(&jsm_driver);
|
||||
uart_unregister_driver(&jsm_uart_driver);
|
||||
}
|
||||
|
||||
module_init(jsm_init_module);
|
||||
module_exit(jsm_exit_module);
|
||||
1424
drivers/serial/jsm/jsm_neo.c
Normal file
1424
drivers/serial/jsm/jsm_neo.c
Normal file
File diff suppressed because it is too large
Load Diff
931
drivers/serial/jsm/jsm_tty.c
Normal file
931
drivers/serial/jsm/jsm_tty.c
Normal file
@@ -0,0 +1,931 @@
|
||||
/************************************************************************
|
||||
* Copyright 2003 Digi International (www.digi.com)
|
||||
*
|
||||
* Copyright (C) 2004 IBM 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; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*
|
||||
* Contact Information:
|
||||
* Scott H Kilau <Scott_Kilau@digi.com>
|
||||
* Ananda Venkatarman <mansarov@us.ibm.com>
|
||||
* Modifications:
|
||||
* 01/19/06: changed jsm_input routine to use the dynamically allocated
|
||||
* tty_buffer changes. Contributors: Scott Kilau and Ananda V.
|
||||
***********************************************************************/
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/delay.h> /* For udelay */
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "jsm.h"
|
||||
|
||||
static void jsm_carrier(struct jsm_channel *ch);
|
||||
|
||||
static inline int jsm_get_mstat(struct jsm_channel *ch)
|
||||
{
|
||||
unsigned char mstat;
|
||||
unsigned result;
|
||||
|
||||
jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "start\n");
|
||||
|
||||
mstat = (ch->ch_mostat | ch->ch_mistat);
|
||||
|
||||
result = 0;
|
||||
|
||||
if (mstat & UART_MCR_DTR)
|
||||
result |= TIOCM_DTR;
|
||||
if (mstat & UART_MCR_RTS)
|
||||
result |= TIOCM_RTS;
|
||||
if (mstat & UART_MSR_CTS)
|
||||
result |= TIOCM_CTS;
|
||||
if (mstat & UART_MSR_DSR)
|
||||
result |= TIOCM_DSR;
|
||||
if (mstat & UART_MSR_RI)
|
||||
result |= TIOCM_RI;
|
||||
if (mstat & UART_MSR_DCD)
|
||||
result |= TIOCM_CD;
|
||||
|
||||
jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned int jsm_tty_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return modem signals to ld.
|
||||
*/
|
||||
static unsigned int jsm_tty_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
int result;
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
|
||||
|
||||
result = jsm_get_mstat(channel);
|
||||
|
||||
if (result < 0)
|
||||
return -ENXIO;
|
||||
|
||||
jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* jsm_set_modem_info()
|
||||
*
|
||||
* Set modem signals, called by ld.
|
||||
*/
|
||||
static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
|
||||
|
||||
if (mctrl & TIOCM_RTS)
|
||||
channel->ch_mostat |= UART_MCR_RTS;
|
||||
else
|
||||
channel->ch_mostat &= ~UART_MCR_RTS;
|
||||
|
||||
if (mctrl & TIOCM_DTR)
|
||||
channel->ch_mostat |= UART_MCR_DTR;
|
||||
else
|
||||
channel->ch_mostat &= ~UART_MCR_DTR;
|
||||
|
||||
channel->ch_bd->bd_ops->assert_modem_signals(channel);
|
||||
|
||||
jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
static void jsm_tty_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
|
||||
|
||||
channel->ch_flags &= ~(CH_STOP);
|
||||
jsm_tty_write(port);
|
||||
|
||||
jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
|
||||
}
|
||||
|
||||
static void jsm_tty_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
|
||||
|
||||
channel->ch_flags |= (CH_STOP);
|
||||
|
||||
jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
|
||||
}
|
||||
|
||||
static void jsm_tty_send_xchar(struct uart_port *port, char ch)
|
||||
{
|
||||
unsigned long lock_flags;
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
struct ktermios *termios;
|
||||
|
||||
spin_lock_irqsave(&port->lock, lock_flags);
|
||||
termios = port->info->tty->termios;
|
||||
if (ch == termios->c_cc[VSTART])
|
||||
channel->ch_bd->bd_ops->send_start_character(channel);
|
||||
|
||||
if (ch == termios->c_cc[VSTOP])
|
||||
channel->ch_bd->bd_ops->send_stop_character(channel);
|
||||
spin_unlock_irqrestore(&port->lock, lock_flags);
|
||||
}
|
||||
|
||||
static void jsm_tty_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
channel->ch_bd->bd_ops->disable_receiver(channel);
|
||||
}
|
||||
|
||||
static void jsm_tty_break(struct uart_port *port, int break_state)
|
||||
{
|
||||
unsigned long lock_flags;
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
spin_lock_irqsave(&port->lock, lock_flags);
|
||||
if (break_state == -1)
|
||||
channel->ch_bd->bd_ops->send_break(channel);
|
||||
else
|
||||
channel->ch_bd->bd_ops->clear_break(channel, 0);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, lock_flags);
|
||||
}
|
||||
|
||||
static int jsm_tty_open(struct uart_port *port)
|
||||
{
|
||||
struct jsm_board *brd;
|
||||
int rc = 0;
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
struct ktermios *termios;
|
||||
|
||||
/* Get board pointer from our array of majors we have allocated */
|
||||
brd = channel->ch_bd;
|
||||
|
||||
/*
|
||||
* Allocate channel buffers for read/write/error.
|
||||
* Set flag, so we don't get trounced on.
|
||||
*/
|
||||
channel->ch_flags |= (CH_OPENING);
|
||||
|
||||
/* Drop locks, as malloc with GFP_KERNEL can sleep */
|
||||
|
||||
if (!channel->ch_rqueue) {
|
||||
channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL);
|
||||
if (!channel->ch_rqueue) {
|
||||
jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
|
||||
"unable to allocate read queue buf");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
if (!channel->ch_equeue) {
|
||||
channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL);
|
||||
if (!channel->ch_equeue) {
|
||||
jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
|
||||
"unable to allocate error queue buf");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
if (!channel->ch_wqueue) {
|
||||
channel->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL);
|
||||
if (!channel->ch_wqueue) {
|
||||
jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
|
||||
"unable to allocate write queue buf");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
channel->ch_flags &= ~(CH_OPENING);
|
||||
/*
|
||||
* Initialize if neither terminal is open.
|
||||
*/
|
||||
jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev,
|
||||
"jsm_open: initializing channel in open...\n");
|
||||
|
||||
/*
|
||||
* Flush input queues.
|
||||
*/
|
||||
channel->ch_r_head = channel->ch_r_tail = 0;
|
||||
channel->ch_e_head = channel->ch_e_tail = 0;
|
||||
channel->ch_w_head = channel->ch_w_tail = 0;
|
||||
|
||||
brd->bd_ops->flush_uart_write(channel);
|
||||
brd->bd_ops->flush_uart_read(channel);
|
||||
|
||||
channel->ch_flags = 0;
|
||||
channel->ch_cached_lsr = 0;
|
||||
channel->ch_stops_sent = 0;
|
||||
|
||||
termios = port->info->tty->termios;
|
||||
channel->ch_c_cflag = termios->c_cflag;
|
||||
channel->ch_c_iflag = termios->c_iflag;
|
||||
channel->ch_c_oflag = termios->c_oflag;
|
||||
channel->ch_c_lflag = termios->c_lflag;
|
||||
channel->ch_startc = termios->c_cc[VSTART];
|
||||
channel->ch_stopc = termios->c_cc[VSTOP];
|
||||
|
||||
/* Tell UART to init itself */
|
||||
brd->bd_ops->uart_init(channel);
|
||||
|
||||
/*
|
||||
* Run param in case we changed anything
|
||||
*/
|
||||
brd->bd_ops->param(channel);
|
||||
|
||||
jsm_carrier(channel);
|
||||
|
||||
channel->ch_open_count++;
|
||||
|
||||
jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void jsm_tty_close(struct uart_port *port)
|
||||
{
|
||||
struct jsm_board *bd;
|
||||
struct ktermios *ts;
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n");
|
||||
|
||||
bd = channel->ch_bd;
|
||||
ts = channel->uart_port.info->tty->termios;
|
||||
|
||||
channel->ch_flags &= ~(CH_STOPI);
|
||||
|
||||
channel->ch_open_count--;
|
||||
|
||||
/*
|
||||
* If we have HUPCL set, lower DTR and RTS
|
||||
*/
|
||||
if (channel->ch_c_cflag & HUPCL) {
|
||||
jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev,
|
||||
"Close. HUPCL set, dropping DTR/RTS\n");
|
||||
|
||||
/* Drop RTS/DTR */
|
||||
channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
|
||||
bd->bd_ops->assert_modem_signals(channel);
|
||||
}
|
||||
|
||||
channel->ch_old_baud = 0;
|
||||
|
||||
/* Turn off UART interrupts for this port */
|
||||
channel->ch_bd->bd_ops->uart_off(channel);
|
||||
|
||||
jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "finish\n");
|
||||
}
|
||||
|
||||
static void jsm_tty_set_termios(struct uart_port *port,
|
||||
struct ktermios *termios,
|
||||
struct ktermios *old_termios)
|
||||
{
|
||||
unsigned long lock_flags;
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
spin_lock_irqsave(&port->lock, lock_flags);
|
||||
channel->ch_c_cflag = termios->c_cflag;
|
||||
channel->ch_c_iflag = termios->c_iflag;
|
||||
channel->ch_c_oflag = termios->c_oflag;
|
||||
channel->ch_c_lflag = termios->c_lflag;
|
||||
channel->ch_startc = termios->c_cc[VSTART];
|
||||
channel->ch_stopc = termios->c_cc[VSTOP];
|
||||
|
||||
channel->ch_bd->bd_ops->param(channel);
|
||||
jsm_carrier(channel);
|
||||
spin_unlock_irqrestore(&port->lock, lock_flags);
|
||||
}
|
||||
|
||||
static const char *jsm_tty_type(struct uart_port *port)
|
||||
{
|
||||
return "jsm";
|
||||
}
|
||||
|
||||
static void jsm_tty_release_port(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static int jsm_tty_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jsm_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
port->type = PORT_JSM;
|
||||
}
|
||||
|
||||
static struct uart_ops jsm_ops = {
|
||||
.tx_empty = jsm_tty_tx_empty,
|
||||
.set_mctrl = jsm_tty_set_mctrl,
|
||||
.get_mctrl = jsm_tty_get_mctrl,
|
||||
.stop_tx = jsm_tty_stop_tx,
|
||||
.start_tx = jsm_tty_start_tx,
|
||||
.send_xchar = jsm_tty_send_xchar,
|
||||
.stop_rx = jsm_tty_stop_rx,
|
||||
.break_ctl = jsm_tty_break,
|
||||
.startup = jsm_tty_open,
|
||||
.shutdown = jsm_tty_close,
|
||||
.set_termios = jsm_tty_set_termios,
|
||||
.type = jsm_tty_type,
|
||||
.release_port = jsm_tty_release_port,
|
||||
.request_port = jsm_tty_request_port,
|
||||
.config_port = jsm_config_port,
|
||||
};
|
||||
|
||||
/*
|
||||
* jsm_tty_init()
|
||||
*
|
||||
* Init the tty subsystem. Called once per board after board has been
|
||||
* downloaded and init'ed.
|
||||
*/
|
||||
int jsm_tty_init(struct jsm_board *brd)
|
||||
{
|
||||
int i;
|
||||
void __iomem *vaddr;
|
||||
struct jsm_channel *ch;
|
||||
|
||||
if (!brd)
|
||||
return -ENXIO;
|
||||
|
||||
jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
|
||||
|
||||
/*
|
||||
* Initialize board structure elements.
|
||||
*/
|
||||
|
||||
brd->nasync = brd->maxports;
|
||||
|
||||
/*
|
||||
* Allocate channel memory that might not have been allocated
|
||||
* when the driver was first loaded.
|
||||
*/
|
||||
for (i = 0; i < brd->nasync; i++) {
|
||||
if (!brd->channels[i]) {
|
||||
|
||||
/*
|
||||
* Okay to malloc with GFP_KERNEL, we are not at
|
||||
* interrupt context, and there are no locks held.
|
||||
*/
|
||||
brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL);
|
||||
if (!brd->channels[i]) {
|
||||
jsm_printk(CORE, ERR, &brd->pci_dev,
|
||||
"%s:%d Unable to allocate memory for channel struct\n",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ch = brd->channels[0];
|
||||
vaddr = brd->re_map_membase;
|
||||
|
||||
/* Set up channel variables */
|
||||
for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
|
||||
|
||||
if (!brd->channels[i])
|
||||
continue;
|
||||
|
||||
spin_lock_init(&ch->ch_lock);
|
||||
|
||||
if (brd->bd_uart_offset == 0x200)
|
||||
ch->ch_neo_uart = vaddr + (brd->bd_uart_offset * i);
|
||||
|
||||
ch->ch_bd = brd;
|
||||
ch->ch_portnum = i;
|
||||
|
||||
/* .25 second delay */
|
||||
ch->ch_close_delay = 250;
|
||||
|
||||
init_waitqueue_head(&ch->ch_flags_wait);
|
||||
}
|
||||
|
||||
jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jsm_uart_port_init(struct jsm_board *brd)
|
||||
{
|
||||
int i;
|
||||
struct jsm_channel *ch;
|
||||
|
||||
if (!brd)
|
||||
return -ENXIO;
|
||||
|
||||
jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
|
||||
|
||||
/*
|
||||
* Initialize board structure elements.
|
||||
*/
|
||||
|
||||
brd->nasync = brd->maxports;
|
||||
|
||||
/* Set up channel variables */
|
||||
for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
|
||||
|
||||
if (!brd->channels[i])
|
||||
continue;
|
||||
|
||||
brd->channels[i]->uart_port.irq = brd->irq;
|
||||
brd->channels[i]->uart_port.type = PORT_JSM;
|
||||
brd->channels[i]->uart_port.iotype = UPIO_MEM;
|
||||
brd->channels[i]->uart_port.membase = brd->re_map_membase;
|
||||
brd->channels[i]->uart_port.fifosize = 16;
|
||||
brd->channels[i]->uart_port.ops = &jsm_ops;
|
||||
brd->channels[i]->uart_port.line = brd->channels[i]->ch_portnum + brd->boardnum * 2;
|
||||
if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port))
|
||||
printk(KERN_INFO "Added device failed\n");
|
||||
else
|
||||
printk(KERN_INFO "Added device \n");
|
||||
}
|
||||
|
||||
jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jsm_remove_uart_port(struct jsm_board *brd)
|
||||
{
|
||||
int i;
|
||||
struct jsm_channel *ch;
|
||||
|
||||
if (!brd)
|
||||
return -ENXIO;
|
||||
|
||||
jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
|
||||
|
||||
/*
|
||||
* Initialize board structure elements.
|
||||
*/
|
||||
|
||||
brd->nasync = brd->maxports;
|
||||
|
||||
/* Set up channel variables */
|
||||
for (i = 0; i < brd->nasync; i++) {
|
||||
|
||||
if (!brd->channels[i])
|
||||
continue;
|
||||
|
||||
ch = brd->channels[i];
|
||||
|
||||
uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
|
||||
}
|
||||
|
||||
jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jsm_input(struct jsm_channel *ch)
|
||||
{
|
||||
struct jsm_board *bd;
|
||||
struct tty_struct *tp;
|
||||
struct tty_ldisc *ld;
|
||||
u32 rmask;
|
||||
u16 head;
|
||||
u16 tail;
|
||||
int data_len;
|
||||
unsigned long lock_flags;
|
||||
int flip_len = 0;
|
||||
int len = 0;
|
||||
int n = 0;
|
||||
int s = 0;
|
||||
int i = 0;
|
||||
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
|
||||
|
||||
if (!ch)
|
||||
return;
|
||||
|
||||
tp = ch->uart_port.info->tty;
|
||||
|
||||
bd = ch->ch_bd;
|
||||
if(!bd)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&ch->ch_lock, lock_flags);
|
||||
|
||||
/*
|
||||
*Figure the number of characters in the buffer.
|
||||
*Exit immediately if none.
|
||||
*/
|
||||
|
||||
rmask = RQUEUEMASK;
|
||||
|
||||
head = ch->ch_r_head & rmask;
|
||||
tail = ch->ch_r_tail & rmask;
|
||||
|
||||
data_len = (head - tail) & rmask;
|
||||
if (data_len == 0) {
|
||||
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
|
||||
|
||||
/*
|
||||
*If the device is not open, or CREAD is off, flush
|
||||
*input data and return immediately.
|
||||
*/
|
||||
if (!tp ||
|
||||
!(tp->termios->c_cflag & CREAD) ) {
|
||||
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
|
||||
"input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum);
|
||||
ch->ch_r_head = tail;
|
||||
|
||||
/* Force queue flow control to be released, if needed */
|
||||
jsm_check_queue_flow_control(ch);
|
||||
|
||||
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are throttled, simply don't read any data.
|
||||
*/
|
||||
if (ch->ch_flags & CH_STOPI) {
|
||||
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
|
||||
"Port %d throttled, not reading any data. head: %x tail: %x\n",
|
||||
ch->ch_portnum, head, tail);
|
||||
return;
|
||||
}
|
||||
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n");
|
||||
|
||||
/*
|
||||
* If the rxbuf is empty and we are not throttled, put as much
|
||||
* as we can directly into the linux TTY buffer.
|
||||
*
|
||||
*/
|
||||
flip_len = TTY_FLIPBUF_SIZE;
|
||||
|
||||
len = min(data_len, flip_len);
|
||||
len = min(len, (N_TTY_BUF_SIZE - 1) - tp->read_cnt);
|
||||
ld = tty_ldisc_ref(tp);
|
||||
|
||||
/*
|
||||
* If we were unable to get a reference to the ld,
|
||||
* don't flush our buffer, and act like the ld doesn't
|
||||
* have any space to put the data right now.
|
||||
*/
|
||||
if (!ld) {
|
||||
len = 0;
|
||||
} else {
|
||||
/*
|
||||
* If ld doesn't have a pointer to a receive_buf function,
|
||||
* flush the data, then act like the ld doesn't have any
|
||||
* space to put the data right now.
|
||||
*/
|
||||
if (!ld->receive_buf) {
|
||||
ch->ch_r_head = ch->ch_r_tail;
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (len <= 0) {
|
||||
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n");
|
||||
if (ld)
|
||||
tty_ldisc_deref(ld);
|
||||
return;
|
||||
}
|
||||
|
||||
len = tty_buffer_request_room(tp, len);
|
||||
n = len;
|
||||
|
||||
/*
|
||||
* n now contains the most amount of data we can copy,
|
||||
* bounded either by the flip buffer size or the amount
|
||||
* of data the card actually has pending...
|
||||
*/
|
||||
while (n) {
|
||||
s = ((head >= tail) ? head : RQUEUESIZE) - tail;
|
||||
s = min(s, n);
|
||||
|
||||
if (s <= 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* If conditions are such that ld needs to see all
|
||||
* UART errors, we will have to walk each character
|
||||
* and error byte and send them to the buffer one at
|
||||
* a time.
|
||||
*/
|
||||
|
||||
if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
|
||||
for (i = 0; i < s; i++) {
|
||||
/*
|
||||
* Give the Linux ld the flags in the
|
||||
* format it likes.
|
||||
*/
|
||||
if (*(ch->ch_equeue +tail +i) & UART_LSR_BI)
|
||||
tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_BREAK);
|
||||
else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE)
|
||||
tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_PARITY);
|
||||
else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE)
|
||||
tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_FRAME);
|
||||
else
|
||||
tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_NORMAL);
|
||||
}
|
||||
} else {
|
||||
tty_insert_flip_string(tp, ch->ch_rqueue + tail, s) ;
|
||||
}
|
||||
tail += s;
|
||||
n -= s;
|
||||
/* Flip queue if needed */
|
||||
tail &= rmask;
|
||||
}
|
||||
|
||||
ch->ch_r_tail = tail & rmask;
|
||||
ch->ch_e_tail = tail & rmask;
|
||||
jsm_check_queue_flow_control(ch);
|
||||
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
|
||||
|
||||
/* Tell the tty layer its okay to "eat" the data now */
|
||||
tty_flip_buffer_push(tp);
|
||||
|
||||
if (ld)
|
||||
tty_ldisc_deref(ld);
|
||||
|
||||
jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
|
||||
}
|
||||
|
||||
static void jsm_carrier(struct jsm_channel *ch)
|
||||
{
|
||||
struct jsm_board *bd;
|
||||
|
||||
int virt_carrier = 0;
|
||||
int phys_carrier = 0;
|
||||
|
||||
jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n");
|
||||
if (!ch)
|
||||
return;
|
||||
|
||||
bd = ch->ch_bd;
|
||||
|
||||
if (!bd)
|
||||
return;
|
||||
|
||||
if (ch->ch_mistat & UART_MSR_DCD) {
|
||||
jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
|
||||
"mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
|
||||
phys_carrier = 1;
|
||||
}
|
||||
|
||||
if (ch->ch_c_cflag & CLOCAL)
|
||||
virt_carrier = 1;
|
||||
|
||||
jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
|
||||
"DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier);
|
||||
|
||||
/*
|
||||
* Test for a VIRTUAL carrier transition to HIGH.
|
||||
*/
|
||||
if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {
|
||||
|
||||
/*
|
||||
* When carrier rises, wake any threads waiting
|
||||
* for carrier in the open routine.
|
||||
*/
|
||||
|
||||
jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
|
||||
"carrier: virt DCD rose\n");
|
||||
|
||||
if (waitqueue_active(&(ch->ch_flags_wait)))
|
||||
wake_up_interruptible(&ch->ch_flags_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test for a PHYSICAL carrier transition to HIGH.
|
||||
*/
|
||||
if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {
|
||||
|
||||
/*
|
||||
* When carrier rises, wake any threads waiting
|
||||
* for carrier in the open routine.
|
||||
*/
|
||||
|
||||
jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
|
||||
"carrier: physical DCD rose\n");
|
||||
|
||||
if (waitqueue_active(&(ch->ch_flags_wait)))
|
||||
wake_up_interruptible(&ch->ch_flags_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test for a PHYSICAL transition to low, so long as we aren't
|
||||
* currently ignoring physical transitions (which is what "virtual
|
||||
* carrier" indicates).
|
||||
*
|
||||
* The transition of the virtual carrier to low really doesn't
|
||||
* matter... it really only means "ignore carrier state", not
|
||||
* "make pretend that carrier is there".
|
||||
*/
|
||||
if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0)
|
||||
&& (phys_carrier == 0)) {
|
||||
/*
|
||||
* When carrier drops:
|
||||
*
|
||||
* Drop carrier on all open units.
|
||||
*
|
||||
* Flush queues, waking up any task waiting in the
|
||||
* line discipline.
|
||||
*
|
||||
* Send a hangup to the control terminal.
|
||||
*
|
||||
* Enable all select calls.
|
||||
*/
|
||||
if (waitqueue_active(&(ch->ch_flags_wait)))
|
||||
wake_up_interruptible(&ch->ch_flags_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that our cached values reflect the current reality.
|
||||
*/
|
||||
if (virt_carrier == 1)
|
||||
ch->ch_flags |= CH_FCAR;
|
||||
else
|
||||
ch->ch_flags &= ~CH_FCAR;
|
||||
|
||||
if (phys_carrier == 1)
|
||||
ch->ch_flags |= CH_CD;
|
||||
else
|
||||
ch->ch_flags &= ~CH_CD;
|
||||
}
|
||||
|
||||
|
||||
void jsm_check_queue_flow_control(struct jsm_channel *ch)
|
||||
{
|
||||
struct board_ops *bd_ops = ch->ch_bd->bd_ops;
|
||||
int qleft = 0;
|
||||
|
||||
/* Store how much space we have left in the queue */
|
||||
if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0)
|
||||
qleft += RQUEUEMASK + 1;
|
||||
|
||||
/*
|
||||
* Check to see if we should enforce flow control on our queue because
|
||||
* the ld (or user) isn't reading data out of our queue fast enuf.
|
||||
*
|
||||
* NOTE: This is done based on what the current flow control of the
|
||||
* port is set for.
|
||||
*
|
||||
* 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
|
||||
* This will cause the UART's FIFO to back up, and force
|
||||
* the RTS signal to be dropped.
|
||||
* 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
|
||||
* the other side, in hopes it will stop sending data to us.
|
||||
* 3) NONE - Nothing we can do. We will simply drop any extra data
|
||||
* that gets sent into us when the queue fills up.
|
||||
*/
|
||||
if (qleft < 256) {
|
||||
/* HWFLOW */
|
||||
if (ch->ch_c_cflag & CRTSCTS) {
|
||||
if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
|
||||
bd_ops->disable_receiver(ch);
|
||||
ch->ch_flags |= (CH_RECEIVER_OFF);
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
|
||||
"Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
|
||||
qleft);
|
||||
}
|
||||
}
|
||||
/* SWFLOW */
|
||||
else if (ch->ch_c_iflag & IXOFF) {
|
||||
if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
|
||||
bd_ops->send_stop_character(ch);
|
||||
ch->ch_stops_sent++;
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
|
||||
"Sending stop char! Times sent: %x\n", ch->ch_stops_sent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if we should unenforce flow control because
|
||||
* ld (or user) finally read enuf data out of our queue.
|
||||
*
|
||||
* NOTE: This is done based on what the current flow control of the
|
||||
* port is set for.
|
||||
*
|
||||
* 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
|
||||
* This will cause the UART's FIFO to raise RTS back up,
|
||||
* which will allow the other side to start sending data again.
|
||||
* 2) SWFLOW (IXOFF) - Send a start character to
|
||||
* the other side, so it will start sending data to us again.
|
||||
* 3) NONE - Do nothing. Since we didn't do anything to turn off the
|
||||
* other side, we don't need to do anything now.
|
||||
*/
|
||||
if (qleft > (RQUEUESIZE / 2)) {
|
||||
/* HWFLOW */
|
||||
if (ch->ch_c_cflag & CRTSCTS) {
|
||||
if (ch->ch_flags & CH_RECEIVER_OFF) {
|
||||
bd_ops->enable_receiver(ch);
|
||||
ch->ch_flags &= ~(CH_RECEIVER_OFF);
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
|
||||
"Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
|
||||
qleft);
|
||||
}
|
||||
}
|
||||
/* SWFLOW */
|
||||
else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
|
||||
ch->ch_stops_sent = 0;
|
||||
bd_ops->send_start_character(ch);
|
||||
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* jsm_tty_write()
|
||||
*
|
||||
* Take data from the user or kernel and send it out to the FEP.
|
||||
* In here exists all the Transparent Print magic as well.
|
||||
*/
|
||||
int jsm_tty_write(struct uart_port *port)
|
||||
{
|
||||
int bufcount = 0, n = 0;
|
||||
int data_count = 0,data_count1 =0;
|
||||
u16 head;
|
||||
u16 tail;
|
||||
u16 tmask;
|
||||
u32 remain;
|
||||
int temp_tail = port->info->xmit.tail;
|
||||
struct jsm_channel *channel = (struct jsm_channel *)port;
|
||||
|
||||
tmask = WQUEUEMASK;
|
||||
head = (channel->ch_w_head) & tmask;
|
||||
tail = (channel->ch_w_tail) & tmask;
|
||||
|
||||
if ((bufcount = tail - head - 1) < 0)
|
||||
bufcount += WQUEUESIZE;
|
||||
|
||||
n = bufcount;
|
||||
|
||||
n = min(n, 56);
|
||||
remain = WQUEUESIZE - head;
|
||||
|
||||
data_count = 0;
|
||||
if (n >= remain) {
|
||||
n -= remain;
|
||||
while ((port->info->xmit.head != temp_tail) &&
|
||||
(data_count < remain)) {
|
||||
channel->ch_wqueue[head++] =
|
||||
port->info->xmit.buf[temp_tail];
|
||||
|
||||
temp_tail++;
|
||||
temp_tail &= (UART_XMIT_SIZE - 1);
|
||||
data_count++;
|
||||
}
|
||||
if (data_count == remain) head = 0;
|
||||
}
|
||||
|
||||
data_count1 = 0;
|
||||
if (n > 0) {
|
||||
remain = n;
|
||||
while ((port->info->xmit.head != temp_tail) &&
|
||||
(data_count1 < remain)) {
|
||||
channel->ch_wqueue[head++] =
|
||||
port->info->xmit.buf[temp_tail];
|
||||
|
||||
temp_tail++;
|
||||
temp_tail &= (UART_XMIT_SIZE - 1);
|
||||
data_count1++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
port->info->xmit.tail = temp_tail;
|
||||
|
||||
data_count += data_count1;
|
||||
if (data_count) {
|
||||
head &= tmask;
|
||||
channel->ch_w_head = head;
|
||||
}
|
||||
|
||||
if (data_count) {
|
||||
channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel);
|
||||
}
|
||||
|
||||
return data_count;
|
||||
}
|
||||
1192
drivers/serial/m32r_sio.c
Normal file
1192
drivers/serial/m32r_sio.c
Normal file
File diff suppressed because it is too large
Load Diff
54
drivers/serial/m32r_sio.h
Normal file
54
drivers/serial/m32r_sio.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* m32r_sio.h
|
||||
*
|
||||
* Driver for M32R serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
* Based on drivers/serial/8250.h.
|
||||
*
|
||||
* Copyright (C) 2001 Russell King.
|
||||
* Copyright (C) 2004 Hirokazu Takata <takata at linux-m32r.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
|
||||
struct m32r_sio_probe {
|
||||
struct module *owner;
|
||||
int (*pci_init_one)(struct pci_dev *dev);
|
||||
void (*pci_remove_one)(struct pci_dev *dev);
|
||||
void (*pnp_init)(void);
|
||||
};
|
||||
|
||||
int m32r_sio_register_probe(struct m32r_sio_probe *probe);
|
||||
void m32r_sio_unregister_probe(struct m32r_sio_probe *probe);
|
||||
void m32r_sio_get_irq_map(unsigned int *map);
|
||||
void m32r_sio_suspend_port(int line);
|
||||
void m32r_sio_resume_port(int line);
|
||||
|
||||
struct old_serial_port {
|
||||
unsigned int uart;
|
||||
unsigned int baud_base;
|
||||
unsigned int port;
|
||||
unsigned int irq;
|
||||
unsigned int flags;
|
||||
unsigned char io_type;
|
||||
unsigned char __iomem *iomem_base;
|
||||
unsigned short iomem_reg_shift;
|
||||
};
|
||||
|
||||
#define _INLINE_ inline
|
||||
|
||||
#define PROBE_RSA (1 << 0)
|
||||
#define PROBE_ANY (~0)
|
||||
|
||||
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
|
||||
|
||||
#ifdef CONFIG_SERIAL_SIO_SHARE_IRQ
|
||||
#define M32R_SIO_SHARE_IRQS 1
|
||||
#else
|
||||
#define M32R_SIO_SHARE_IRQS 0
|
||||
#endif
|
||||
152
drivers/serial/m32r_sio_reg.h
Normal file
152
drivers/serial/m32r_sio_reg.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* m32r_sio_reg.h
|
||||
*
|
||||
* Copyright (C) 1992, 1994 by Theodore Ts'o.
|
||||
* Copyright (C) 2004 Hirokazu Takata <takata at linux-m32r.org>
|
||||
*
|
||||
* Redistribution of this file is permitted under the terms of the GNU
|
||||
* Public License (GPL)
|
||||
*
|
||||
* These are the UART port assignments, expressed as offsets from the base
|
||||
* register. These assignments should hold for any serial port based on
|
||||
* a 8250, 16450, or 16550(A).
|
||||
*/
|
||||
|
||||
#ifndef _M32R_SIO_REG_H
|
||||
#define _M32R_SIO_REG_H
|
||||
|
||||
|
||||
#ifdef CONFIG_SERIAL_M32R_PLDSIO
|
||||
|
||||
#define SIOCR 0x000
|
||||
#define SIOMOD0 0x002
|
||||
#define SIOMOD1 0x004
|
||||
#define SIOSTS 0x006
|
||||
#define SIOTRCR 0x008
|
||||
#define SIOBAUR 0x00a
|
||||
// #define SIORBAUR 0x018
|
||||
#define SIOTXB 0x00c
|
||||
#define SIORXB 0x00e
|
||||
|
||||
#define UART_RX ((unsigned long) PLD_ESIO0RXB)
|
||||
/* In: Receive buffer (DLAB=0) */
|
||||
#define UART_TX ((unsigned long) PLD_ESIO0TXB)
|
||||
/* Out: Transmit buffer (DLAB=0) */
|
||||
#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */
|
||||
#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx
|
||||
* In: Fifo count
|
||||
* Out: Fifo custom trigger levels
|
||||
* XR16C85x only */
|
||||
|
||||
#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */
|
||||
#define UART_IER ((unsigned long) PLD_ESIO0INTCR)
|
||||
/* Out: Interrupt Enable Register */
|
||||
#define UART_FCTR 0 /* (LCR=BF) Feature Control Register
|
||||
* XR16C85x only */
|
||||
|
||||
#define UART_IIR 0 /* In: Interrupt ID Register */
|
||||
#define UART_FCR 0 /* Out: FIFO Control Register */
|
||||
#define UART_EFR 0 /* I/O: Extended Features Register */
|
||||
/* (DLAB=1, 16C660 only) */
|
||||
|
||||
#define UART_LCR 0 /* Out: Line Control Register */
|
||||
#define UART_MCR 0 /* Out: Modem Control Register */
|
||||
#define UART_LSR ((unsigned long) PLD_ESIO0STS)
|
||||
/* In: Line Status Register */
|
||||
#define UART_MSR 0 /* In: Modem Status Register */
|
||||
#define UART_SCR 0 /* I/O: Scratch Register */
|
||||
#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register
|
||||
* FCTR bit 6 selects SCR or EMSR
|
||||
* XR16c85x only */
|
||||
|
||||
#else /* not CONFIG_SERIAL_M32R_PLDSIO */
|
||||
|
||||
#define SIOCR 0x000
|
||||
#define SIOMOD0 0x004
|
||||
#define SIOMOD1 0x008
|
||||
#define SIOSTS 0x00c
|
||||
#define SIOTRCR 0x010
|
||||
#define SIOBAUR 0x014
|
||||
#define SIORBAUR 0x018
|
||||
#define SIOTXB 0x01c
|
||||
#define SIORXB 0x020
|
||||
|
||||
#define UART_RX M32R_SIO0_RXB_PORTL /* In: Receive buffer (DLAB=0) */
|
||||
#define UART_TX M32R_SIO0_TXB_PORTL /* Out: Transmit buffer (DLAB=0) */
|
||||
#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */
|
||||
#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx
|
||||
* In: Fifo count
|
||||
* Out: Fifo custom trigger levels
|
||||
* XR16C85x only */
|
||||
|
||||
#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */
|
||||
#define UART_IER M32R_SIO0_TRCR_PORTL /* Out: Interrupt Enable Register */
|
||||
#define UART_FCTR 0 /* (LCR=BF) Feature Control Register
|
||||
* XR16C85x only */
|
||||
|
||||
#define UART_IIR 0 /* In: Interrupt ID Register */
|
||||
#define UART_FCR 0 /* Out: FIFO Control Register */
|
||||
#define UART_EFR 0 /* I/O: Extended Features Register */
|
||||
/* (DLAB=1, 16C660 only) */
|
||||
|
||||
#define UART_LCR 0 /* Out: Line Control Register */
|
||||
#define UART_MCR 0 /* Out: Modem Control Register */
|
||||
#define UART_LSR M32R_SIO0_STS_PORTL /* In: Line Status Register */
|
||||
#define UART_MSR 0 /* In: Modem Status Register */
|
||||
#define UART_SCR 0 /* I/O: Scratch Register */
|
||||
#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register
|
||||
* FCTR bit 6 selects SCR or EMSR
|
||||
* XR16c85x only */
|
||||
|
||||
#endif /* CONFIG_SERIAL_M32R_PLDSIO */
|
||||
|
||||
#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
|
||||
|
||||
/*
|
||||
* These are the definitions for the Line Control Register
|
||||
*
|
||||
* Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting
|
||||
* UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
|
||||
*/
|
||||
#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
|
||||
#define UART_LCR_SBC 0x40 /* Set break control */
|
||||
#define UART_LCR_SPAR 0x20 /* Stick parity (?) */
|
||||
#define UART_LCR_EPAR 0x10 /* Even parity select */
|
||||
#define UART_LCR_PARITY 0x08 /* Parity Enable */
|
||||
#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */
|
||||
#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */
|
||||
#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */
|
||||
#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */
|
||||
#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */
|
||||
|
||||
/*
|
||||
* These are the definitions for the Line Status Register
|
||||
*/
|
||||
#define UART_LSR_TEMT 0x02 /* Transmitter empty */
|
||||
#define UART_LSR_THRE 0x01 /* Transmit-hold-register empty */
|
||||
#define UART_LSR_BI 0x00 /* Break interrupt indicator */
|
||||
#define UART_LSR_FE 0x80 /* Frame error indicator */
|
||||
#define UART_LSR_PE 0x40 /* Parity error indicator */
|
||||
#define UART_LSR_OE 0x20 /* Overrun error indicator */
|
||||
#define UART_LSR_DR 0x04 /* Receiver data ready */
|
||||
|
||||
/*
|
||||
* These are the definitions for the Interrupt Identification Register
|
||||
*/
|
||||
#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
|
||||
#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
|
||||
|
||||
#define UART_IIR_MSI 0x00 /* Modem status interrupt */
|
||||
#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
|
||||
#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
|
||||
#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
|
||||
|
||||
/*
|
||||
* These are the definitions for the Interrupt Enable Register
|
||||
*/
|
||||
#define UART_IER_MSI 0x00 /* Enable Modem status interrupt */
|
||||
#define UART_IER_RLSI 0x08 /* Enable receiver line status interrupt */
|
||||
#define UART_IER_THRI 0x03 /* Enable Transmitter holding register int. */
|
||||
#define UART_IER_RDI 0x04 /* Enable receiver data interrupt */
|
||||
|
||||
#endif /* _M32R_SIO_REG_H */
|
||||
1975
drivers/serial/mcfserial.c
Normal file
1975
drivers/serial/mcfserial.c
Normal file
File diff suppressed because it is too large
Load Diff
74
drivers/serial/mcfserial.h
Normal file
74
drivers/serial/mcfserial.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* mcfserial.c -- serial driver for ColdFire internal UARTS.
|
||||
*
|
||||
* Copyright (c) 1999 Greg Ungerer <gerg@snapgear.com>
|
||||
* Copyright (c) 2000-2001 Lineo, Inc. <www.lineo.com>
|
||||
* Copyright (c) 2002 SnapGear Inc., <www.snapgear.com>
|
||||
*
|
||||
* Based on code from 68332serial.c which was:
|
||||
*
|
||||
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
|
||||
* Copyright (C) 1998 TSHG
|
||||
* Copyright (c) 1999 Rt-Control Inc. <jeff@uclinux.org>
|
||||
*/
|
||||
#ifndef _MCF_SERIAL_H
|
||||
#define _MCF_SERIAL_H
|
||||
|
||||
#include <linux/serial.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/*
|
||||
* Define a local serial stats structure.
|
||||
*/
|
||||
|
||||
struct mcf_stats {
|
||||
unsigned int rx;
|
||||
unsigned int tx;
|
||||
unsigned int rxbreak;
|
||||
unsigned int rxframing;
|
||||
unsigned int rxparity;
|
||||
unsigned int rxoverrun;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This is our internal structure for each serial port's state.
|
||||
* Each serial port has one of these structures associated with it.
|
||||
*/
|
||||
|
||||
struct mcf_serial {
|
||||
int magic;
|
||||
volatile unsigned char *addr; /* UART memory address */
|
||||
int irq;
|
||||
int flags; /* defined in tty.h */
|
||||
int type; /* UART type */
|
||||
struct tty_struct *tty;
|
||||
unsigned char imr; /* Software imr register */
|
||||
unsigned int baud;
|
||||
int sigs;
|
||||
int custom_divisor;
|
||||
int x_char; /* xon/xoff character */
|
||||
int baud_base;
|
||||
int close_delay;
|
||||
unsigned short closing_wait;
|
||||
unsigned short closing_wait2;
|
||||
unsigned long event;
|
||||
int line;
|
||||
int count; /* # of fd on device */
|
||||
int blocked_open; /* # of blocked opens */
|
||||
unsigned char *xmit_buf;
|
||||
int xmit_head;
|
||||
int xmit_tail;
|
||||
int xmit_cnt;
|
||||
struct mcf_stats stats;
|
||||
struct work_struct tqueue;
|
||||
struct work_struct tqueue_hangup;
|
||||
wait_queue_head_t open_wait;
|
||||
wait_queue_head_t close_wait;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _MCF_SERIAL_H */
|
||||
1161
drivers/serial/mpc52xx_uart.c
Normal file
1161
drivers/serial/mpc52xx_uart.c
Normal file
File diff suppressed because it is too large
Load Diff
2091
drivers/serial/mpsc.c
Normal file
2091
drivers/serial/mpsc.c
Normal file
File diff suppressed because it is too large
Load Diff
634
drivers/serial/mux.c
Normal file
634
drivers/serial/mux.c
Normal file
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
** mux.c:
|
||||
** serial driver for the Mux console found in some PA-RISC servers.
|
||||
**
|
||||
** (c) Copyright 2002 Ryan Bradetich
|
||||
** (c) Copyright 2002 Hewlett-Packard Company
|
||||
**
|
||||
** 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; either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This Driver currently only supports the console (port 0) on the MUX.
|
||||
** Additional work will be needed on this driver to enable the full
|
||||
** functionality of the MUX.
|
||||
**
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h> /* for udelay */
|
||||
#include <linux/device.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/parisc-device.h>
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
#include <linux/sysrq.h>
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
#define MUX_OFFSET 0x800
|
||||
#define MUX_LINE_OFFSET 0x80
|
||||
|
||||
#define MUX_FIFO_SIZE 255
|
||||
#define MUX_POLL_DELAY (30 * HZ / 1000)
|
||||
|
||||
#define IO_DATA_REG_OFFSET 0x3c
|
||||
#define IO_DCOUNT_REG_OFFSET 0x40
|
||||
|
||||
#define MUX_EOFIFO(status) ((status & 0xF000) == 0xF000)
|
||||
#define MUX_STATUS(status) ((status & 0xF000) == 0x8000)
|
||||
#define MUX_BREAK(status) ((status & 0xF000) == 0x2000)
|
||||
|
||||
#define MUX_NR 256
|
||||
static unsigned int port_cnt __read_mostly;
|
||||
struct mux_port {
|
||||
struct uart_port port;
|
||||
int enabled;
|
||||
};
|
||||
static struct mux_port mux_ports[MUX_NR];
|
||||
|
||||
static struct uart_driver mux_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "ttyB",
|
||||
.dev_name = "ttyB",
|
||||
.major = MUX_MAJOR,
|
||||
.minor = 0,
|
||||
.nr = MUX_NR,
|
||||
};
|
||||
|
||||
static struct timer_list mux_timer;
|
||||
|
||||
#define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + IO_DATA_REG_OFFSET)
|
||||
#define UART_GET_FIFO_CNT(p) __raw_readl((p)->membase + IO_DCOUNT_REG_OFFSET)
|
||||
|
||||
/**
|
||||
* get_mux_port_count - Get the number of available ports on the Mux.
|
||||
* @dev: The parisc device.
|
||||
*
|
||||
* This function is used to determine the number of ports the Mux
|
||||
* supports. The IODC data reports the number of ports the Mux
|
||||
* can support, but there are cases where not all the Mux ports
|
||||
* are connected. This function can override the IODC and
|
||||
* return the true port count.
|
||||
*/
|
||||
static int __init get_mux_port_count(struct parisc_device *dev)
|
||||
{
|
||||
int status;
|
||||
u8 iodc_data[32];
|
||||
unsigned long bytecnt;
|
||||
|
||||
/* If this is the built-in Mux for the K-Class (Eole CAP/MUX),
|
||||
* we only need to allocate resources for 1 port since the
|
||||
* other 7 ports are not connected.
|
||||
*/
|
||||
if(dev->id.hversion == 0x15)
|
||||
return 1;
|
||||
|
||||
status = pdc_iodc_read(&bytecnt, dev->hpa.start, 0, iodc_data, 32);
|
||||
BUG_ON(status != PDC_OK);
|
||||
|
||||
/* Return the number of ports specified in the iodc data. */
|
||||
return ((((iodc_data)[4] & 0xf0) >> 4) * 8) + 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_tx_empty - Check if the transmitter fifo is empty.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* This function test if the transmitter fifo for the port
|
||||
* described by 'port' is empty. If it is empty, this function
|
||||
* should return TIOCSER_TEMT, otherwise return 0.
|
||||
*/
|
||||
static unsigned int mux_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return UART_GET_FIFO_CNT(port) ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_set_mctrl - Set the current state of the modem control inputs.
|
||||
* @ports: Ptr to the uart_port.
|
||||
* @mctrl: Modem control bits.
|
||||
*
|
||||
* The Serial MUX does not support CTS, DCD or DSR so this function
|
||||
* is ignored.
|
||||
*/
|
||||
static void mux_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_get_mctrl - Returns the current state of modem control inputs.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* The Serial MUX does not support CTS, DCD or DSR so these lines are
|
||||
* treated as permanently active.
|
||||
*/
|
||||
static unsigned int mux_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_stop_tx - Stop transmitting characters.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* The Serial MUX does not support this function.
|
||||
*/
|
||||
static void mux_stop_tx(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_start_tx - Start transmitting characters.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* The Serial Mux does not support this function.
|
||||
*/
|
||||
static void mux_start_tx(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_stop_rx - Stop receiving characters.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* The Serial Mux does not support this function.
|
||||
*/
|
||||
static void mux_stop_rx(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_enable_ms - Enable modum status interrupts.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* The Serial Mux does not support this function.
|
||||
*/
|
||||
static void mux_enable_ms(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_break_ctl - Control the transmitssion of a break signal.
|
||||
* @port: Ptr to the uart_port.
|
||||
* @break_state: Raise/Lower the break signal.
|
||||
*
|
||||
* The Serial Mux does not support this function.
|
||||
*/
|
||||
static void mux_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_write - Write chars to the mux fifo.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* This function writes all the data from the uart buffer to
|
||||
* the mux fifo.
|
||||
*/
|
||||
static void mux_write(struct uart_port *port)
|
||||
{
|
||||
int count;
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
if(port->x_char) {
|
||||
UART_PUT_CHAR(port, port->x_char);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
mux_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
count = (port->fifosize) - UART_GET_FIFO_CNT(port);
|
||||
do {
|
||||
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
if(uart_circ_empty(xmit))
|
||||
break;
|
||||
|
||||
} while(--count > 0);
|
||||
|
||||
while(UART_GET_FIFO_CNT(port))
|
||||
udelay(1);
|
||||
|
||||
if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
mux_stop_tx(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_read - Read chars from the mux fifo.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* This reads all available data from the mux's fifo and pushes
|
||||
* the data to the tty layer.
|
||||
*/
|
||||
static void mux_read(struct uart_port *port)
|
||||
{
|
||||
int data;
|
||||
struct tty_struct *tty = port->info->tty;
|
||||
__u32 start_count = port->icount.rx;
|
||||
|
||||
while(1) {
|
||||
data = __raw_readl(port->membase + IO_DATA_REG_OFFSET);
|
||||
|
||||
if (MUX_STATUS(data))
|
||||
continue;
|
||||
|
||||
if (MUX_EOFIFO(data))
|
||||
break;
|
||||
|
||||
port->icount.rx++;
|
||||
|
||||
if (MUX_BREAK(data)) {
|
||||
port->icount.brk++;
|
||||
if(uart_handle_break(port))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(port, data & 0xffu))
|
||||
continue;
|
||||
|
||||
tty_insert_flip_char(tty, data & 0xFF, TTY_NORMAL);
|
||||
}
|
||||
|
||||
if (start_count != port->icount.rx) {
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_startup - Initialize the port.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* Grab any resources needed for this port and start the
|
||||
* mux timer.
|
||||
*/
|
||||
static int mux_startup(struct uart_port *port)
|
||||
{
|
||||
mux_ports[port->line].enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_shutdown - Disable the port.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* Release any resources needed for the port.
|
||||
*/
|
||||
static void mux_shutdown(struct uart_port *port)
|
||||
{
|
||||
mux_ports[port->line].enabled = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_set_termios - Chane port parameters.
|
||||
* @port: Ptr to the uart_port.
|
||||
* @termios: new termios settings.
|
||||
* @old: old termios settings.
|
||||
*
|
||||
* The Serial Mux does not support this function.
|
||||
*/
|
||||
static void
|
||||
mux_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_type - Describe the port.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* Return a pointer to a string constant describing the
|
||||
* specified port.
|
||||
*/
|
||||
static const char *mux_type(struct uart_port *port)
|
||||
{
|
||||
return "Mux";
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_release_port - Release memory and IO regions.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* Release any memory and IO region resources currently in use by
|
||||
* the port.
|
||||
*/
|
||||
static void mux_release_port(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_request_port - Request memory and IO regions.
|
||||
* @port: Ptr to the uart_port.
|
||||
*
|
||||
* Request any memory and IO region resources required by the port.
|
||||
* If any fail, no resources should be registered when this function
|
||||
* returns, and it should return -EBUSY on failure.
|
||||
*/
|
||||
static int mux_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_config_port - Perform port autoconfiguration.
|
||||
* @port: Ptr to the uart_port.
|
||||
* @type: Bitmask of required configurations.
|
||||
*
|
||||
* Perform any autoconfiguration steps for the port. This function is
|
||||
* called if the UPF_BOOT_AUTOCONF flag is specified for the port.
|
||||
* [Note: This is required for now because of a bug in the Serial core.
|
||||
* rmk has already submitted a patch to linus, should be available for
|
||||
* 2.5.47.]
|
||||
*/
|
||||
static void mux_config_port(struct uart_port *port, int type)
|
||||
{
|
||||
port->type = PORT_MUX;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_verify_port - Verify the port information.
|
||||
* @port: Ptr to the uart_port.
|
||||
* @ser: Ptr to the serial information.
|
||||
*
|
||||
* Verify the new serial port information contained within serinfo is
|
||||
* suitable for this port type.
|
||||
*/
|
||||
static int mux_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
if(port->membase == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_drv_poll - Mux poll function.
|
||||
* @unused: Unused variable
|
||||
*
|
||||
* This function periodically polls the Serial MUX to check for new data.
|
||||
*/
|
||||
static void mux_poll(unsigned long unused)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < port_cnt; ++i) {
|
||||
if(!mux_ports[i].enabled)
|
||||
continue;
|
||||
|
||||
mux_read(&mux_ports[i].port);
|
||||
mux_write(&mux_ports[i].port);
|
||||
}
|
||||
|
||||
mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_SERIAL_MUX_CONSOLE
|
||||
static void mux_console_write(struct console *co, const char *s, unsigned count)
|
||||
{
|
||||
/* Wait until the FIFO drains. */
|
||||
while(UART_GET_FIFO_CNT(&mux_ports[0].port))
|
||||
udelay(1);
|
||||
|
||||
while(count--) {
|
||||
if(*s == '\n') {
|
||||
UART_PUT_CHAR(&mux_ports[0].port, '\r');
|
||||
}
|
||||
UART_PUT_CHAR(&mux_ports[0].port, *s++);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int mux_console_setup(struct console *co, char *options)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct tty_driver *mux_console_device(struct console *co, int *index)
|
||||
{
|
||||
*index = co->index;
|
||||
return mux_driver.tty_driver;
|
||||
}
|
||||
|
||||
static struct console mux_console = {
|
||||
.name = "ttyB",
|
||||
.write = mux_console_write,
|
||||
.device = mux_console_device,
|
||||
.setup = mux_console_setup,
|
||||
.flags = CON_ENABLED | CON_PRINTBUFFER,
|
||||
.index = 0,
|
||||
};
|
||||
|
||||
#define MUX_CONSOLE &mux_console
|
||||
#else
|
||||
#define MUX_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_ops mux_pops = {
|
||||
.tx_empty = mux_tx_empty,
|
||||
.set_mctrl = mux_set_mctrl,
|
||||
.get_mctrl = mux_get_mctrl,
|
||||
.stop_tx = mux_stop_tx,
|
||||
.start_tx = mux_start_tx,
|
||||
.stop_rx = mux_stop_rx,
|
||||
.enable_ms = mux_enable_ms,
|
||||
.break_ctl = mux_break_ctl,
|
||||
.startup = mux_startup,
|
||||
.shutdown = mux_shutdown,
|
||||
.set_termios = mux_set_termios,
|
||||
.type = mux_type,
|
||||
.release_port = mux_release_port,
|
||||
.request_port = mux_request_port,
|
||||
.config_port = mux_config_port,
|
||||
.verify_port = mux_verify_port,
|
||||
};
|
||||
|
||||
/**
|
||||
* mux_probe - Determine if the Serial Mux should claim this device.
|
||||
* @dev: The parisc device.
|
||||
*
|
||||
* Deterimine if the Serial Mux should claim this chip (return 0)
|
||||
* or not (return 1).
|
||||
*/
|
||||
static int __init mux_probe(struct parisc_device *dev)
|
||||
{
|
||||
int i, status;
|
||||
|
||||
int port_count = get_mux_port_count(dev);
|
||||
printk(KERN_INFO "Serial mux driver (%d ports) Revision: 0.6\n", port_count);
|
||||
|
||||
dev_set_drvdata(&dev->dev, (void *)(long)port_count);
|
||||
request_mem_region(dev->hpa.start + MUX_OFFSET,
|
||||
port_count * MUX_LINE_OFFSET, "Mux");
|
||||
|
||||
if(!port_cnt) {
|
||||
mux_driver.cons = MUX_CONSOLE;
|
||||
|
||||
status = uart_register_driver(&mux_driver);
|
||||
if(status) {
|
||||
printk(KERN_ERR "Serial mux: Unable to register driver.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < port_count; ++i, ++port_cnt) {
|
||||
struct uart_port *port = &mux_ports[port_cnt].port;
|
||||
port->iobase = 0;
|
||||
port->mapbase = dev->hpa.start + MUX_OFFSET +
|
||||
(i * MUX_LINE_OFFSET);
|
||||
port->membase = ioremap_nocache(port->mapbase, MUX_LINE_OFFSET);
|
||||
port->iotype = UPIO_MEM;
|
||||
port->type = PORT_MUX;
|
||||
port->irq = NO_IRQ;
|
||||
port->uartclk = 0;
|
||||
port->fifosize = MUX_FIFO_SIZE;
|
||||
port->ops = &mux_pops;
|
||||
port->flags = UPF_BOOT_AUTOCONF;
|
||||
port->line = port_cnt;
|
||||
|
||||
/* The port->timeout needs to match what is present in
|
||||
* uart_wait_until_sent in serial_core.c. Otherwise
|
||||
* the time spent in msleep_interruptable will be very
|
||||
* long, causing the appearance of a console hang.
|
||||
*/
|
||||
port->timeout = HZ / 50;
|
||||
spin_lock_init(&port->lock);
|
||||
|
||||
status = uart_add_one_port(&mux_driver, port);
|
||||
BUG_ON(status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit mux_remove(struct parisc_device *dev)
|
||||
{
|
||||
int i, j;
|
||||
int port_count = (long)dev_get_drvdata(&dev->dev);
|
||||
|
||||
/* Find Port 0 for this card in the mux_ports list. */
|
||||
for(i = 0; i < port_cnt; ++i) {
|
||||
if(mux_ports[i].port.mapbase == dev->hpa.start + MUX_OFFSET)
|
||||
break;
|
||||
}
|
||||
BUG_ON(i + port_count > port_cnt);
|
||||
|
||||
/* Release the resources associated with each port on the device. */
|
||||
for(j = 0; j < port_count; ++j, ++i) {
|
||||
struct uart_port *port = &mux_ports[i].port;
|
||||
|
||||
uart_remove_one_port(&mux_driver, port);
|
||||
if(port->membase)
|
||||
iounmap(port->membase);
|
||||
}
|
||||
|
||||
release_mem_region(dev->hpa.start + MUX_OFFSET, port_count * MUX_LINE_OFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Hack. This idea was taken from the 8250_gsc.c on how to properly order
|
||||
* the serial port detection in the proper order. The idea is we always
|
||||
* want the builtin mux to be detected before addin mux cards, so we
|
||||
* specifically probe for the builtin mux cards first.
|
||||
*
|
||||
* This table only contains the parisc_device_id of known builtin mux
|
||||
* devices. All other mux cards will be detected by the generic mux_tbl.
|
||||
*/
|
||||
static struct parisc_device_id builtin_mux_tbl[] = {
|
||||
{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x15, 0x0000D }, /* All K-class */
|
||||
{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x44, 0x0000D }, /* E35, E45, and E55 */
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static struct parisc_device_id mux_tbl[] = {
|
||||
{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0000D },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(parisc, builtin_mux_tbl);
|
||||
MODULE_DEVICE_TABLE(parisc, mux_tbl);
|
||||
|
||||
static struct parisc_driver builtin_serial_mux_driver = {
|
||||
.name = "builtin_serial_mux",
|
||||
.id_table = builtin_mux_tbl,
|
||||
.probe = mux_probe,
|
||||
.remove = __devexit_p(mux_remove),
|
||||
};
|
||||
|
||||
static struct parisc_driver serial_mux_driver = {
|
||||
.name = "serial_mux",
|
||||
.id_table = mux_tbl,
|
||||
.probe = mux_probe,
|
||||
.remove = __devexit_p(mux_remove),
|
||||
};
|
||||
|
||||
/**
|
||||
* mux_init - Serial MUX initalization procedure.
|
||||
*
|
||||
* Register the Serial MUX driver.
|
||||
*/
|
||||
static int __init mux_init(void)
|
||||
{
|
||||
register_parisc_driver(&builtin_serial_mux_driver);
|
||||
register_parisc_driver(&serial_mux_driver);
|
||||
|
||||
if(port_cnt > 0) {
|
||||
/* Start the Mux timer */
|
||||
init_timer(&mux_timer);
|
||||
mux_timer.function = mux_poll;
|
||||
mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY);
|
||||
|
||||
#ifdef CONFIG_SERIAL_MUX_CONSOLE
|
||||
register_console(&mux_console);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mux_exit - Serial MUX cleanup procedure.
|
||||
*
|
||||
* Unregister the Serial MUX driver from the tty layer.
|
||||
*/
|
||||
static void __exit mux_exit(void)
|
||||
{
|
||||
/* Delete the Mux timer. */
|
||||
if(port_cnt > 0) {
|
||||
del_timer(&mux_timer);
|
||||
#ifdef CONFIG_SERIAL_MUX_CONSOLE
|
||||
unregister_console(&mux_console);
|
||||
#endif
|
||||
}
|
||||
|
||||
unregister_parisc_driver(&builtin_serial_mux_driver);
|
||||
unregister_parisc_driver(&serial_mux_driver);
|
||||
uart_unregister_driver(&mux_driver);
|
||||
}
|
||||
|
||||
module_init(mux_init);
|
||||
module_exit(mux_exit);
|
||||
|
||||
MODULE_AUTHOR("Ryan Bradetich");
|
||||
MODULE_DESCRIPTION("Serial MUX driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_CHARDEV_MAJOR(MUX_MAJOR);
|
||||
747
drivers/serial/netx-serial.c
Normal file
747
drivers/serial/netx-serial.c
Normal file
@@ -0,0 +1,747 @@
|
||||
/*
|
||||
* drivers/serial/netx-serial.c
|
||||
*
|
||||
* Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_NETX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/netx-regs.h>
|
||||
|
||||
/* We've been assigned a range on the "Low-density serial ports" major */
|
||||
#define SERIAL_NX_MAJOR 204
|
||||
#define MINOR_START 170
|
||||
|
||||
#ifdef CONFIG_SERIAL_NETX_CONSOLE
|
||||
|
||||
enum uart_regs {
|
||||
UART_DR = 0x00,
|
||||
UART_SR = 0x04,
|
||||
UART_LINE_CR = 0x08,
|
||||
UART_BAUDDIV_MSB = 0x0c,
|
||||
UART_BAUDDIV_LSB = 0x10,
|
||||
UART_CR = 0x14,
|
||||
UART_FR = 0x18,
|
||||
UART_IIR = 0x1c,
|
||||
UART_ILPR = 0x20,
|
||||
UART_RTS_CR = 0x24,
|
||||
UART_RTS_LEAD = 0x28,
|
||||
UART_RTS_TRAIL = 0x2c,
|
||||
UART_DRV_ENABLE = 0x30,
|
||||
UART_BRM_CR = 0x34,
|
||||
UART_RXFIFO_IRQLEVEL = 0x38,
|
||||
UART_TXFIFO_IRQLEVEL = 0x3c,
|
||||
};
|
||||
|
||||
#define SR_FE (1<<0)
|
||||
#define SR_PE (1<<1)
|
||||
#define SR_BE (1<<2)
|
||||
#define SR_OE (1<<3)
|
||||
|
||||
#define LINE_CR_BRK (1<<0)
|
||||
#define LINE_CR_PEN (1<<1)
|
||||
#define LINE_CR_EPS (1<<2)
|
||||
#define LINE_CR_STP2 (1<<3)
|
||||
#define LINE_CR_FEN (1<<4)
|
||||
#define LINE_CR_5BIT (0<<5)
|
||||
#define LINE_CR_6BIT (1<<5)
|
||||
#define LINE_CR_7BIT (2<<5)
|
||||
#define LINE_CR_8BIT (3<<5)
|
||||
#define LINE_CR_BITS_MASK (3<<5)
|
||||
|
||||
#define CR_UART_EN (1<<0)
|
||||
#define CR_SIREN (1<<1)
|
||||
#define CR_SIRLP (1<<2)
|
||||
#define CR_MSIE (1<<3)
|
||||
#define CR_RIE (1<<4)
|
||||
#define CR_TIE (1<<5)
|
||||
#define CR_RTIE (1<<6)
|
||||
#define CR_LBE (1<<7)
|
||||
|
||||
#define FR_CTS (1<<0)
|
||||
#define FR_DSR (1<<1)
|
||||
#define FR_DCD (1<<2)
|
||||
#define FR_BUSY (1<<3)
|
||||
#define FR_RXFE (1<<4)
|
||||
#define FR_TXFF (1<<5)
|
||||
#define FR_RXFF (1<<6)
|
||||
#define FR_TXFE (1<<7)
|
||||
|
||||
#define IIR_MIS (1<<0)
|
||||
#define IIR_RIS (1<<1)
|
||||
#define IIR_TIS (1<<2)
|
||||
#define IIR_RTIS (1<<3)
|
||||
#define IIR_MASK 0xf
|
||||
|
||||
#define RTS_CR_AUTO (1<<0)
|
||||
#define RTS_CR_RTS (1<<1)
|
||||
#define RTS_CR_COUNT (1<<2)
|
||||
#define RTS_CR_MOD2 (1<<3)
|
||||
#define RTS_CR_RTS_POL (1<<4)
|
||||
#define RTS_CR_CTS_CTR (1<<5)
|
||||
#define RTS_CR_CTS_POL (1<<6)
|
||||
#define RTS_CR_STICK (1<<7)
|
||||
|
||||
#define UART_PORT_SIZE 0x40
|
||||
#define DRIVER_NAME "netx-uart"
|
||||
|
||||
struct netx_port {
|
||||
struct uart_port port;
|
||||
};
|
||||
|
||||
static void netx_stop_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned int val;
|
||||
val = readl(port->membase + UART_CR);
|
||||
writel(val & ~CR_TIE, port->membase + UART_CR);
|
||||
}
|
||||
|
||||
static void netx_stop_rx(struct uart_port *port)
|
||||
{
|
||||
unsigned int val;
|
||||
val = readl(port->membase + UART_CR);
|
||||
writel(val & ~CR_RIE, port->membase + UART_CR);
|
||||
}
|
||||
|
||||
static void netx_enable_ms(struct uart_port *port)
|
||||
{
|
||||
unsigned int val;
|
||||
val = readl(port->membase + UART_CR);
|
||||
writel(val | CR_MSIE, port->membase + UART_CR);
|
||||
}
|
||||
|
||||
static inline void netx_transmit_buffer(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
if (port->x_char) {
|
||||
writel(port->x_char, port->membase + UART_DR);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (uart_tx_stopped(port) || uart_circ_empty(xmit)) {
|
||||
netx_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
/* send xmit->buf[xmit->tail]
|
||||
* out the port here */
|
||||
writel(xmit->buf[xmit->tail], port->membase + UART_DR);
|
||||
xmit->tail = (xmit->tail + 1) &
|
||||
(UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (!(readl(port->membase + UART_FR) & FR_TXFF));
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
netx_stop_tx(port);
|
||||
}
|
||||
|
||||
static void netx_start_tx(struct uart_port *port)
|
||||
{
|
||||
writel(
|
||||
readl(port->membase + UART_CR) | CR_TIE, port->membase + UART_CR);
|
||||
|
||||
if (!(readl(port->membase + UART_FR) & FR_TXFF))
|
||||
netx_transmit_buffer(port);
|
||||
}
|
||||
|
||||
static unsigned int netx_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return readl(port->membase + UART_FR) & FR_BUSY ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static void netx_txint(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
netx_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
netx_transmit_buffer(port);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
}
|
||||
|
||||
static void netx_rxint(struct uart_port *port)
|
||||
{
|
||||
unsigned char rx, flg, status;
|
||||
struct tty_struct *tty = port->info->tty;
|
||||
|
||||
while (!(readl(port->membase + UART_FR) & FR_RXFE)) {
|
||||
rx = readl(port->membase + UART_DR);
|
||||
flg = TTY_NORMAL;
|
||||
port->icount.rx++;
|
||||
status = readl(port->membase + UART_SR);
|
||||
if (status & SR_BE) {
|
||||
writel(0, port->membase + UART_SR);
|
||||
if (uart_handle_break(port))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlikely(status & (SR_FE | SR_PE | SR_OE))) {
|
||||
|
||||
if (status & SR_PE)
|
||||
port->icount.parity++;
|
||||
else if (status & SR_FE)
|
||||
port->icount.frame++;
|
||||
if (status & SR_OE)
|
||||
port->icount.overrun++;
|
||||
|
||||
status &= port->read_status_mask;
|
||||
|
||||
if (status & SR_BE)
|
||||
flg = TTY_BREAK;
|
||||
else if (status & SR_PE)
|
||||
flg = TTY_PARITY;
|
||||
else if (status & SR_FE)
|
||||
flg = TTY_FRAME;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(port, rx))
|
||||
continue;
|
||||
|
||||
uart_insert_char(port, status, SR_OE, rx, flg);
|
||||
}
|
||||
|
||||
tty_flip_buffer_push(tty);
|
||||
return;
|
||||
}
|
||||
|
||||
static irqreturn_t netx_int(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
unsigned long flags;
|
||||
unsigned char status;
|
||||
|
||||
spin_lock_irqsave(&port->lock,flags);
|
||||
|
||||
status = readl(port->membase + UART_IIR) & IIR_MASK;
|
||||
while (status) {
|
||||
if (status & IIR_RIS)
|
||||
netx_rxint(port);
|
||||
if (status & IIR_TIS)
|
||||
netx_txint(port);
|
||||
if (status & IIR_MIS) {
|
||||
if (readl(port->membase + UART_FR) & FR_CTS)
|
||||
uart_handle_cts_change(port, 1);
|
||||
else
|
||||
uart_handle_cts_change(port, 0);
|
||||
}
|
||||
writel(0, port->membase + UART_IIR);
|
||||
status = readl(port->membase + UART_IIR) & IIR_MASK;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->lock,flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int netx_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
unsigned int ret = TIOCM_DSR | TIOCM_CAR;
|
||||
|
||||
if (readl(port->membase + UART_FR) & FR_CTS)
|
||||
ret |= TIOCM_CTS;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void netx_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (mctrl & TIOCM_RTS) {
|
||||
val = readl(port->membase + UART_RTS_CR);
|
||||
writel(val | RTS_CR_RTS, port->membase + UART_RTS_CR);
|
||||
}
|
||||
}
|
||||
|
||||
static void netx_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
unsigned int line_cr;
|
||||
spin_lock_irq(&port->lock);
|
||||
|
||||
line_cr = readl(port->membase + UART_LINE_CR);
|
||||
if (break_state != 0)
|
||||
line_cr |= LINE_CR_BRK;
|
||||
else
|
||||
line_cr &= ~LINE_CR_BRK;
|
||||
writel(line_cr, port->membase + UART_LINE_CR);
|
||||
|
||||
spin_unlock_irq(&port->lock);
|
||||
}
|
||||
|
||||
static int netx_startup(struct uart_port *port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = request_irq(port->irq, netx_int, 0,
|
||||
DRIVER_NAME, port);
|
||||
if (ret) {
|
||||
dev_err(port->dev, "unable to grab irq%d\n",port->irq);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
writel(readl(port->membase + UART_LINE_CR) | LINE_CR_FEN,
|
||||
port->membase + UART_LINE_CR);
|
||||
|
||||
writel(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE | CR_UART_EN,
|
||||
port->membase + UART_CR);
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void netx_shutdown(struct uart_port *port)
|
||||
{
|
||||
writel(0, port->membase + UART_CR) ;
|
||||
|
||||
free_irq(port->irq, port);
|
||||
}
|
||||
|
||||
static void
|
||||
netx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud, quot;
|
||||
unsigned char old_cr;
|
||||
unsigned char line_cr = LINE_CR_FEN;
|
||||
unsigned char rts_cr = 0;
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
line_cr |= LINE_CR_5BIT;
|
||||
break;
|
||||
case CS6:
|
||||
line_cr |= LINE_CR_6BIT;
|
||||
break;
|
||||
case CS7:
|
||||
line_cr |= LINE_CR_7BIT;
|
||||
break;
|
||||
case CS8:
|
||||
line_cr |= LINE_CR_8BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
line_cr |= LINE_CR_STP2;
|
||||
|
||||
if (termios->c_cflag & PARENB) {
|
||||
line_cr |= LINE_CR_PEN;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
line_cr |= LINE_CR_EPS;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CRTSCTS)
|
||||
rts_cr = RTS_CR_AUTO | RTS_CR_CTS_CTR | RTS_CR_RTS_POL;
|
||||
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = baud * 4096;
|
||||
quot /= 1000;
|
||||
quot *= 256;
|
||||
quot /= 100000;
|
||||
|
||||
spin_lock_irq(&port->lock);
|
||||
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
old_cr = readl(port->membase + UART_CR);
|
||||
|
||||
/* disable interrupts */
|
||||
writel(old_cr & ~(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE),
|
||||
port->membase + UART_CR);
|
||||
|
||||
/* drain transmitter */
|
||||
while (readl(port->membase + UART_FR) & FR_BUSY);
|
||||
|
||||
/* disable UART */
|
||||
writel(old_cr & ~CR_UART_EN, port->membase + UART_CR);
|
||||
|
||||
/* modem status interrupts */
|
||||
old_cr &= ~CR_MSIE;
|
||||
if (UART_ENABLE_MS(port, termios->c_cflag))
|
||||
old_cr |= CR_MSIE;
|
||||
|
||||
writel((quot>>8) & 0xff, port->membase + UART_BAUDDIV_MSB);
|
||||
writel(quot & 0xff, port->membase + UART_BAUDDIV_LSB);
|
||||
writel(line_cr, port->membase + UART_LINE_CR);
|
||||
|
||||
writel(rts_cr, port->membase + UART_RTS_CR);
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= SR_PE;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
port->ignore_status_mask |= SR_BE;
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= SR_PE;
|
||||
}
|
||||
|
||||
port->read_status_mask = 0;
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
port->read_status_mask |= SR_BE;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= SR_PE | SR_FE;
|
||||
|
||||
writel(old_cr, port->membase + UART_CR);
|
||||
|
||||
spin_unlock_irq(&port->lock);
|
||||
}
|
||||
|
||||
static const char *netx_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_NETX ? "NETX" : NULL;
|
||||
}
|
||||
|
||||
static void netx_release_port(struct uart_port *port)
|
||||
{
|
||||
release_mem_region(port->mapbase, UART_PORT_SIZE);
|
||||
}
|
||||
|
||||
static int netx_request_port(struct uart_port *port)
|
||||
{
|
||||
return request_mem_region(port->mapbase, UART_PORT_SIZE,
|
||||
DRIVER_NAME) != NULL ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
static void netx_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE && netx_request_port(port) == 0)
|
||||
port->type = PORT_NETX;
|
||||
}
|
||||
|
||||
static int
|
||||
netx_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_NETX)
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops netx_pops = {
|
||||
.tx_empty = netx_tx_empty,
|
||||
.set_mctrl = netx_set_mctrl,
|
||||
.get_mctrl = netx_get_mctrl,
|
||||
.stop_tx = netx_stop_tx,
|
||||
.start_tx = netx_start_tx,
|
||||
.stop_rx = netx_stop_rx,
|
||||
.enable_ms = netx_enable_ms,
|
||||
.break_ctl = netx_break_ctl,
|
||||
.startup = netx_startup,
|
||||
.shutdown = netx_shutdown,
|
||||
.set_termios = netx_set_termios,
|
||||
.type = netx_type,
|
||||
.release_port = netx_release_port,
|
||||
.request_port = netx_request_port,
|
||||
.config_port = netx_config_port,
|
||||
.verify_port = netx_verify_port,
|
||||
};
|
||||
|
||||
static struct netx_port netx_ports[] = {
|
||||
{
|
||||
.port = {
|
||||
.type = PORT_NETX,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (char __iomem *)io_p2v(NETX_PA_UART0),
|
||||
.mapbase = NETX_PA_UART0,
|
||||
.irq = NETX_IRQ_UART0,
|
||||
.uartclk = 100000000,
|
||||
.fifosize = 16,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.ops = &netx_pops,
|
||||
.line = 0,
|
||||
},
|
||||
}, {
|
||||
.port = {
|
||||
.type = PORT_NETX,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (char __iomem *)io_p2v(NETX_PA_UART1),
|
||||
.mapbase = NETX_PA_UART1,
|
||||
.irq = NETX_IRQ_UART1,
|
||||
.uartclk = 100000000,
|
||||
.fifosize = 16,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.ops = &netx_pops,
|
||||
.line = 1,
|
||||
},
|
||||
}, {
|
||||
.port = {
|
||||
.type = PORT_NETX,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (char __iomem *)io_p2v(NETX_PA_UART2),
|
||||
.mapbase = NETX_PA_UART2,
|
||||
.irq = NETX_IRQ_UART2,
|
||||
.uartclk = 100000000,
|
||||
.fifosize = 16,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.ops = &netx_pops,
|
||||
.line = 2,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static void netx_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (readl(port->membase + UART_FR) & FR_BUSY);
|
||||
writel(ch, port->membase + UART_DR);
|
||||
}
|
||||
|
||||
static void
|
||||
netx_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct uart_port *port = &netx_ports[co->index].port;
|
||||
unsigned char cr_save;
|
||||
|
||||
cr_save = readl(port->membase + UART_CR);
|
||||
writel(cr_save | CR_UART_EN, port->membase + UART_CR);
|
||||
|
||||
uart_console_write(port, s, count, netx_console_putchar);
|
||||
|
||||
while (readl(port->membase + UART_FR) & FR_BUSY);
|
||||
writel(cr_save, port->membase + UART_CR);
|
||||
}
|
||||
|
||||
static void __init
|
||||
netx_console_get_options(struct uart_port *port, int *baud,
|
||||
int *parity, int *bits, int *flow)
|
||||
{
|
||||
unsigned char line_cr;
|
||||
|
||||
*baud = (readl(port->membase + UART_BAUDDIV_MSB) << 8) |
|
||||
readl(port->membase + UART_BAUDDIV_LSB);
|
||||
*baud *= 1000;
|
||||
*baud /= 4096;
|
||||
*baud *= 1000;
|
||||
*baud /= 256;
|
||||
*baud *= 100;
|
||||
|
||||
line_cr = readl(port->membase + UART_LINE_CR);
|
||||
*parity = 'n';
|
||||
if (line_cr & LINE_CR_PEN) {
|
||||
if (line_cr & LINE_CR_EPS)
|
||||
*parity = 'e';
|
||||
else
|
||||
*parity = 'o';
|
||||
}
|
||||
|
||||
switch (line_cr & LINE_CR_BITS_MASK) {
|
||||
case LINE_CR_8BIT:
|
||||
*bits = 8;
|
||||
break;
|
||||
case LINE_CR_7BIT:
|
||||
*bits = 7;
|
||||
break;
|
||||
case LINE_CR_6BIT:
|
||||
*bits = 6;
|
||||
break;
|
||||
case LINE_CR_5BIT:
|
||||
*bits = 5;
|
||||
break;
|
||||
}
|
||||
|
||||
if (readl(port->membase + UART_RTS_CR) & RTS_CR_AUTO)
|
||||
*flow = 'r';
|
||||
}
|
||||
|
||||
static int __init
|
||||
netx_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct netx_port *sport;
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
if (co->index == -1 || co->index >= ARRAY_SIZE(netx_ports))
|
||||
co->index = 0;
|
||||
sport = &netx_ports[co->index];
|
||||
|
||||
if (options) {
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
} else {
|
||||
/* if the UART is enabled, assume it has been correctly setup
|
||||
* by the bootloader and get the options
|
||||
*/
|
||||
if (readl(sport->port.membase + UART_CR) & CR_UART_EN) {
|
||||
netx_console_get_options(&sport->port, &baud,
|
||||
&parity, &bits, &flow);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return uart_set_options(&sport->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver netx_reg;
|
||||
static struct console netx_console = {
|
||||
.name = "ttyNX",
|
||||
.write = netx_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = netx_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &netx_reg,
|
||||
};
|
||||
|
||||
static int __init netx_console_init(void)
|
||||
{
|
||||
register_console(&netx_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(netx_console_init);
|
||||
|
||||
#define NETX_CONSOLE &netx_console
|
||||
#else
|
||||
#define NETX_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver netx_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = DRIVER_NAME,
|
||||
.dev_name = "ttyNX",
|
||||
.major = SERIAL_NX_MAJOR,
|
||||
.minor = MINOR_START,
|
||||
.nr = ARRAY_SIZE(netx_ports),
|
||||
.cons = NETX_CONSOLE,
|
||||
};
|
||||
|
||||
static int serial_netx_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct netx_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
if (sport)
|
||||
uart_suspend_port(&netx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_netx_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct netx_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
if (sport)
|
||||
uart_resume_port(&netx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_netx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *port = &netx_ports[pdev->id].port;
|
||||
|
||||
dev_info(&pdev->dev, "initialising\n");
|
||||
|
||||
port->dev = &pdev->dev;
|
||||
|
||||
writel(1, port->membase + UART_RXFIFO_IRQLEVEL);
|
||||
uart_add_one_port(&netx_reg, &netx_ports[pdev->id].port);
|
||||
platform_set_drvdata(pdev, &netx_ports[pdev->id]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_netx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct netx_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (sport)
|
||||
uart_remove_one_port(&netx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver serial_netx_driver = {
|
||||
.probe = serial_netx_probe,
|
||||
.remove = serial_netx_remove,
|
||||
|
||||
.suspend = serial_netx_suspend,
|
||||
.resume = serial_netx_resume,
|
||||
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init netx_serial_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "Serial: NetX driver\n");
|
||||
|
||||
ret = uart_register_driver(&netx_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&serial_netx_driver);
|
||||
if (ret != 0)
|
||||
uart_unregister_driver(&netx_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit netx_serial_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&serial_netx_driver);
|
||||
uart_unregister_driver(&netx_reg);
|
||||
}
|
||||
|
||||
module_init(netx_serial_init);
|
||||
module_exit(netx_serial_exit);
|
||||
|
||||
MODULE_AUTHOR("Sascha Hauer");
|
||||
MODULE_DESCRIPTION("NetX serial port driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
143
drivers/serial/of_serial.c
Normal file
143
drivers/serial/of_serial.c
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Serial Port driver for Open Firmware platform devices
|
||||
*
|
||||
* Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
|
||||
*
|
||||
* 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; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial_8250.h>
|
||||
|
||||
#include <asm/of_platform.h>
|
||||
#include <asm/prom.h>
|
||||
|
||||
/*
|
||||
* Fill a struct uart_port for a given device node
|
||||
*/
|
||||
static int __devinit of_platform_serial_setup(struct of_device *ofdev,
|
||||
int type, struct uart_port *port)
|
||||
{
|
||||
struct resource resource;
|
||||
struct device_node *np = ofdev->node;
|
||||
const unsigned int *clk, *spd;
|
||||
int ret;
|
||||
|
||||
memset(port, 0, sizeof *port);
|
||||
spd = get_property(np, "current-speed", NULL);
|
||||
clk = get_property(np, "clock-frequency", NULL);
|
||||
if (!clk) {
|
||||
dev_warn(&ofdev->dev, "no clock-frequency property set\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(np, 0, &resource);
|
||||
if (ret) {
|
||||
dev_warn(&ofdev->dev, "invalid address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_init(&port->lock);
|
||||
port->mapbase = resource.start;
|
||||
port->irq = irq_of_parse_and_map(np, 0);
|
||||
port->iotype = UPIO_MEM;
|
||||
port->type = type;
|
||||
port->uartclk = *clk;
|
||||
port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP;
|
||||
port->dev = &ofdev->dev;
|
||||
port->custom_divisor = *clk / (16 * (*spd));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to register a serial port
|
||||
*/
|
||||
static int __devinit of_platform_serial_probe(struct of_device *ofdev,
|
||||
const struct of_device_id *id)
|
||||
{
|
||||
struct uart_port port;
|
||||
int port_type;
|
||||
int ret;
|
||||
|
||||
if (of_find_property(ofdev->node, "used-by-rtas", NULL))
|
||||
return -EBUSY;
|
||||
|
||||
port_type = (unsigned long)id->data;
|
||||
ret = of_platform_serial_setup(ofdev, port_type, &port);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
switch (port_type) {
|
||||
case PORT_UNKNOWN:
|
||||
dev_info(&ofdev->dev, "Unknown serial port found, "
|
||||
"attempting to use 8250 driver\n");
|
||||
/* fallthrough */
|
||||
case PORT_8250 ... PORT_MAX_8250:
|
||||
ret = serial8250_register_port(&port);
|
||||
break;
|
||||
default:
|
||||
/* need to add code for these */
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ofdev->dev.driver_data = (void *)(unsigned long)ret;
|
||||
return 0;
|
||||
out:
|
||||
irq_dispose_mapping(port.irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release a line
|
||||
*/
|
||||
static int of_platform_serial_remove(struct of_device *ofdev)
|
||||
{
|
||||
int line = (unsigned long)ofdev->dev.driver_data;
|
||||
serial8250_unregister_port(line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A few common types, add more as needed.
|
||||
*/
|
||||
static struct of_device_id __devinitdata of_platform_serial_table[] = {
|
||||
{ .type = "serial", .compatible = "ns8250", .data = (void *)PORT_8250, },
|
||||
{ .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, },
|
||||
{ .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, },
|
||||
{ .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, },
|
||||
{ .type = "serial", .data = (void *)PORT_UNKNOWN, },
|
||||
{ /* end of list */ },
|
||||
};
|
||||
|
||||
static struct of_platform_driver __devinitdata of_platform_serial_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "of_serial",
|
||||
.probe = of_platform_serial_probe,
|
||||
.remove = of_platform_serial_remove,
|
||||
.match_table = of_platform_serial_table,
|
||||
};
|
||||
|
||||
static int __init of_platform_serial_init(void)
|
||||
{
|
||||
return of_register_platform_driver(&of_platform_serial_driver);
|
||||
}
|
||||
module_init(of_platform_serial_init);
|
||||
|
||||
static void __exit of_platform_serial_exit(void)
|
||||
{
|
||||
return of_unregister_platform_driver(&of_platform_serial_driver);
|
||||
};
|
||||
module_exit(of_platform_serial_exit);
|
||||
|
||||
MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices");
|
||||
2025
drivers/serial/pmac_zilog.c
Normal file
2025
drivers/serial/pmac_zilog.c
Normal file
File diff suppressed because it is too large
Load Diff
382
drivers/serial/pmac_zilog.h
Normal file
382
drivers/serial/pmac_zilog.h
Normal file
@@ -0,0 +1,382 @@
|
||||
#ifndef __PMAC_ZILOG_H__
|
||||
#define __PMAC_ZILOG_H__
|
||||
|
||||
#define pmz_debug(fmt,arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg)
|
||||
|
||||
/*
|
||||
* At most 2 ESCCs with 2 ports each
|
||||
*/
|
||||
#define MAX_ZS_PORTS 4
|
||||
|
||||
/*
|
||||
* We wrap our port structure around the generic uart_port.
|
||||
*/
|
||||
#define NUM_ZSREGS 17
|
||||
|
||||
struct uart_pmac_port {
|
||||
struct uart_port port;
|
||||
struct uart_pmac_port *mate;
|
||||
|
||||
/* macio_dev for the escc holding this port (maybe be null on
|
||||
* early inited port)
|
||||
*/
|
||||
struct macio_dev *dev;
|
||||
/* device node to this port, this points to one of 2 childs
|
||||
* of "escc" node (ie. ch-a or ch-b)
|
||||
*/
|
||||
struct device_node *node;
|
||||
|
||||
/* Port type as obtained from device tree (IRDA, modem, ...) */
|
||||
int port_type;
|
||||
u8 curregs[NUM_ZSREGS];
|
||||
|
||||
unsigned int flags;
|
||||
#define PMACZILOG_FLAG_IS_CONS 0x00000001
|
||||
#define PMACZILOG_FLAG_IS_KGDB 0x00000002
|
||||
#define PMACZILOG_FLAG_MODEM_STATUS 0x00000004
|
||||
#define PMACZILOG_FLAG_IS_CHANNEL_A 0x00000008
|
||||
#define PMACZILOG_FLAG_REGS_HELD 0x00000010
|
||||
#define PMACZILOG_FLAG_TX_STOPPED 0x00000020
|
||||
#define PMACZILOG_FLAG_TX_ACTIVE 0x00000040
|
||||
#define PMACZILOG_FLAG_ENABLED 0x00000080
|
||||
#define PMACZILOG_FLAG_IS_IRDA 0x00000100
|
||||
#define PMACZILOG_FLAG_IS_INTMODEM 0x00000200
|
||||
#define PMACZILOG_FLAG_HAS_DMA 0x00000400
|
||||
#define PMACZILOG_FLAG_RSRC_REQUESTED 0x00000800
|
||||
#define PMACZILOG_FLAG_IS_ASLEEP 0x00001000
|
||||
#define PMACZILOG_FLAG_IS_OPEN 0x00002000
|
||||
#define PMACZILOG_FLAG_IS_IRQ_ON 0x00004000
|
||||
#define PMACZILOG_FLAG_IS_EXTCLK 0x00008000
|
||||
#define PMACZILOG_FLAG_BREAK 0x00010000
|
||||
|
||||
unsigned char parity_mask;
|
||||
unsigned char prev_status;
|
||||
|
||||
volatile u8 __iomem *control_reg;
|
||||
volatile u8 __iomem *data_reg;
|
||||
|
||||
unsigned int tx_dma_irq;
|
||||
unsigned int rx_dma_irq;
|
||||
volatile struct dbdma_regs __iomem *tx_dma_regs;
|
||||
volatile struct dbdma_regs __iomem *rx_dma_regs;
|
||||
|
||||
struct ktermios termios_cache;
|
||||
};
|
||||
|
||||
#define to_pmz(p) ((struct uart_pmac_port *)(p))
|
||||
|
||||
static inline struct uart_pmac_port *pmz_get_port_A(struct uart_pmac_port *uap)
|
||||
{
|
||||
if (uap->flags & PMACZILOG_FLAG_IS_CHANNEL_A)
|
||||
return uap;
|
||||
return uap->mate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register acessors. Note that we don't need to enforce a recovery
|
||||
* delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip,
|
||||
* though if we try to use this driver on older machines, we might have
|
||||
* to add it back
|
||||
*/
|
||||
static inline u8 read_zsreg(struct uart_pmac_port *port, u8 reg)
|
||||
{
|
||||
if (reg != 0)
|
||||
writeb(reg, port->control_reg);
|
||||
return readb(port->control_reg);
|
||||
}
|
||||
|
||||
static inline void write_zsreg(struct uart_pmac_port *port, u8 reg, u8 value)
|
||||
{
|
||||
if (reg != 0)
|
||||
writeb(reg, port->control_reg);
|
||||
writeb(value, port->control_reg);
|
||||
}
|
||||
|
||||
static inline u8 read_zsdata(struct uart_pmac_port *port)
|
||||
{
|
||||
return readb(port->data_reg);
|
||||
}
|
||||
|
||||
static inline void write_zsdata(struct uart_pmac_port *port, u8 data)
|
||||
{
|
||||
writeb(data, port->data_reg);
|
||||
}
|
||||
|
||||
static inline void zssync(struct uart_pmac_port *port)
|
||||
{
|
||||
(void)readb(port->control_reg);
|
||||
}
|
||||
|
||||
/* Conversion routines to/from brg time constants from/to bits
|
||||
* per second.
|
||||
*/
|
||||
#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
|
||||
#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
|
||||
|
||||
#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */
|
||||
|
||||
/* The Zilog register set */
|
||||
|
||||
#define FLAG 0x7e
|
||||
|
||||
/* Write Register 0 */
|
||||
#define R0 0 /* Register selects */
|
||||
#define R1 1
|
||||
#define R2 2
|
||||
#define R3 3
|
||||
#define R4 4
|
||||
#define R5 5
|
||||
#define R6 6
|
||||
#define R7 7
|
||||
#define R8 8
|
||||
#define R9 9
|
||||
#define R10 10
|
||||
#define R11 11
|
||||
#define R12 12
|
||||
#define R13 13
|
||||
#define R14 14
|
||||
#define R15 15
|
||||
#define R7P 16
|
||||
|
||||
#define NULLCODE 0 /* Null Code */
|
||||
#define POINT_HIGH 0x8 /* Select upper half of registers */
|
||||
#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
|
||||
#define SEND_ABORT 0x18 /* HDLC Abort */
|
||||
#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
|
||||
#define RES_Tx_P 0x28 /* Reset TxINT Pending */
|
||||
#define ERR_RES 0x30 /* Error Reset */
|
||||
#define RES_H_IUS 0x38 /* Reset highest IUS */
|
||||
|
||||
#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
|
||||
#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
|
||||
#define RES_EOM_L 0xC0 /* Reset EOM latch */
|
||||
|
||||
/* Write Register 1 */
|
||||
|
||||
#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
|
||||
#define TxINT_ENAB 0x2 /* Tx Int Enable */
|
||||
#define PAR_SPEC 0x4 /* Parity is special condition */
|
||||
|
||||
#define RxINT_DISAB 0 /* Rx Int Disable */
|
||||
#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
|
||||
#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
|
||||
#define INT_ERR_Rx 0x18 /* Int on error only */
|
||||
#define RxINT_MASK 0x18
|
||||
|
||||
#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */
|
||||
#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */
|
||||
#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */
|
||||
|
||||
/* Write Register #2 (Interrupt Vector) */
|
||||
|
||||
/* Write Register 3 */
|
||||
|
||||
#define RxENABLE 0x1 /* Rx Enable */
|
||||
#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
|
||||
#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
|
||||
#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
|
||||
#define ENT_HM 0x10 /* Enter Hunt Mode */
|
||||
#define AUTO_ENAB 0x20 /* Auto Enables */
|
||||
#define Rx5 0x0 /* Rx 5 Bits/Character */
|
||||
#define Rx7 0x40 /* Rx 7 Bits/Character */
|
||||
#define Rx6 0x80 /* Rx 6 Bits/Character */
|
||||
#define Rx8 0xc0 /* Rx 8 Bits/Character */
|
||||
#define RxN_MASK 0xc0
|
||||
|
||||
/* Write Register 4 */
|
||||
|
||||
#define PAR_ENAB 0x1 /* Parity Enable */
|
||||
#define PAR_EVEN 0x2 /* Parity Even/Odd* */
|
||||
|
||||
#define SYNC_ENAB 0 /* Sync Modes Enable */
|
||||
#define SB1 0x4 /* 1 stop bit/char */
|
||||
#define SB15 0x8 /* 1.5 stop bits/char */
|
||||
#define SB2 0xc /* 2 stop bits/char */
|
||||
#define SB_MASK 0xc
|
||||
|
||||
#define MONSYNC 0 /* 8 Bit Sync character */
|
||||
#define BISYNC 0x10 /* 16 bit sync character */
|
||||
#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
|
||||
#define EXTSYNC 0x30 /* External Sync Mode */
|
||||
|
||||
#define X1CLK 0x0 /* x1 clock mode */
|
||||
#define X16CLK 0x40 /* x16 clock mode */
|
||||
#define X32CLK 0x80 /* x32 clock mode */
|
||||
#define X64CLK 0xC0 /* x64 clock mode */
|
||||
#define XCLK_MASK 0xC0
|
||||
|
||||
/* Write Register 5 */
|
||||
|
||||
#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
|
||||
#define RTS 0x2 /* RTS */
|
||||
#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
|
||||
#define TxENABLE 0x8 /* Tx Enable */
|
||||
#define SND_BRK 0x10 /* Send Break */
|
||||
#define Tx5 0x0 /* Tx 5 bits (or less)/character */
|
||||
#define Tx7 0x20 /* Tx 7 bits/character */
|
||||
#define Tx6 0x40 /* Tx 6 bits/character */
|
||||
#define Tx8 0x60 /* Tx 8 bits/character */
|
||||
#define TxN_MASK 0x60
|
||||
#define DTR 0x80 /* DTR */
|
||||
|
||||
/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
|
||||
|
||||
/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
|
||||
|
||||
/* Write Register 7' (Some enhanced feature control) */
|
||||
#define ENEXREAD 0x40 /* Enable read of some write registers */
|
||||
|
||||
/* Write Register 8 (transmit buffer) */
|
||||
|
||||
/* Write Register 9 (Master interrupt control) */
|
||||
#define VIS 1 /* Vector Includes Status */
|
||||
#define NV 2 /* No Vector */
|
||||
#define DLC 4 /* Disable Lower Chain */
|
||||
#define MIE 8 /* Master Interrupt Enable */
|
||||
#define STATHI 0x10 /* Status high */
|
||||
#define NORESET 0 /* No reset on write to R9 */
|
||||
#define CHRB 0x40 /* Reset channel B */
|
||||
#define CHRA 0x80 /* Reset channel A */
|
||||
#define FHWRES 0xc0 /* Force hardware reset */
|
||||
|
||||
/* Write Register 10 (misc control bits) */
|
||||
#define BIT6 1 /* 6 bit/8bit sync */
|
||||
#define LOOPMODE 2 /* SDLC Loop mode */
|
||||
#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
|
||||
#define MARKIDLE 8 /* Mark/flag on idle */
|
||||
#define GAOP 0x10 /* Go active on poll */
|
||||
#define NRZ 0 /* NRZ mode */
|
||||
#define NRZI 0x20 /* NRZI mode */
|
||||
#define FM1 0x40 /* FM1 (transition = 1) */
|
||||
#define FM0 0x60 /* FM0 (transition = 0) */
|
||||
#define CRCPS 0x80 /* CRC Preset I/O */
|
||||
|
||||
/* Write Register 11 (Clock Mode control) */
|
||||
#define TRxCXT 0 /* TRxC = Xtal output */
|
||||
#define TRxCTC 1 /* TRxC = Transmit clock */
|
||||
#define TRxCBR 2 /* TRxC = BR Generator Output */
|
||||
#define TRxCDP 3 /* TRxC = DPLL output */
|
||||
#define TRxCOI 4 /* TRxC O/I */
|
||||
#define TCRTxCP 0 /* Transmit clock = RTxC pin */
|
||||
#define TCTRxCP 8 /* Transmit clock = TRxC pin */
|
||||
#define TCBR 0x10 /* Transmit clock = BR Generator output */
|
||||
#define TCDPLL 0x18 /* Transmit clock = DPLL output */
|
||||
#define RCRTxCP 0 /* Receive clock = RTxC pin */
|
||||
#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
|
||||
#define RCBR 0x40 /* Receive clock = BR Generator output */
|
||||
#define RCDPLL 0x60 /* Receive clock = DPLL output */
|
||||
#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
|
||||
|
||||
/* Write Register 12 (lower byte of baud rate generator time constant) */
|
||||
|
||||
/* Write Register 13 (upper byte of baud rate generator time constant) */
|
||||
|
||||
/* Write Register 14 (Misc control bits) */
|
||||
#define BRENAB 1 /* Baud rate generator enable */
|
||||
#define BRSRC 2 /* Baud rate generator source */
|
||||
#define DTRREQ 4 /* DTR/Request function */
|
||||
#define AUTOECHO 8 /* Auto Echo */
|
||||
#define LOOPBAK 0x10 /* Local loopback */
|
||||
#define SEARCH 0x20 /* Enter search mode */
|
||||
#define RMC 0x40 /* Reset missing clock */
|
||||
#define DISDPLL 0x60 /* Disable DPLL */
|
||||
#define SSBR 0x80 /* Set DPLL source = BR generator */
|
||||
#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
|
||||
#define SFMM 0xc0 /* Set FM mode */
|
||||
#define SNRZI 0xe0 /* Set NRZI mode */
|
||||
|
||||
/* Write Register 15 (external/status interrupt control) */
|
||||
#define EN85C30 1 /* Enable some 85c30-enhanced registers */
|
||||
#define ZCIE 2 /* Zero count IE */
|
||||
#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */
|
||||
#define DCDIE 8 /* DCD IE */
|
||||
#define SYNCIE 0x10 /* Sync/hunt IE */
|
||||
#define CTSIE 0x20 /* CTS IE */
|
||||
#define TxUIE 0x40 /* Tx Underrun/EOM IE */
|
||||
#define BRKIE 0x80 /* Break/Abort IE */
|
||||
|
||||
|
||||
/* Read Register 0 */
|
||||
#define Rx_CH_AV 0x1 /* Rx Character Available */
|
||||
#define ZCOUNT 0x2 /* Zero count */
|
||||
#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
|
||||
#define DCD 0x8 /* DCD */
|
||||
#define SYNC_HUNT 0x10 /* Sync/hunt */
|
||||
#define CTS 0x20 /* CTS */
|
||||
#define TxEOM 0x40 /* Tx underrun */
|
||||
#define BRK_ABRT 0x80 /* Break/Abort */
|
||||
|
||||
/* Read Register 1 */
|
||||
#define ALL_SNT 0x1 /* All sent */
|
||||
/* Residue Data for 8 Rx bits/char programmed */
|
||||
#define RES3 0x8 /* 0/3 */
|
||||
#define RES4 0x4 /* 0/4 */
|
||||
#define RES5 0xc /* 0/5 */
|
||||
#define RES6 0x2 /* 0/6 */
|
||||
#define RES7 0xa /* 0/7 */
|
||||
#define RES8 0x6 /* 0/8 */
|
||||
#define RES18 0xe /* 1/8 */
|
||||
#define RES28 0x0 /* 2/8 */
|
||||
/* Special Rx Condition Interrupts */
|
||||
#define PAR_ERR 0x10 /* Parity error */
|
||||
#define Rx_OVR 0x20 /* Rx Overrun Error */
|
||||
#define CRC_ERR 0x40 /* CRC/Framing Error */
|
||||
#define END_FR 0x80 /* End of Frame (SDLC) */
|
||||
|
||||
/* Read Register 2 (channel b only) - Interrupt vector */
|
||||
#define CHB_Tx_EMPTY 0x00
|
||||
#define CHB_EXT_STAT 0x02
|
||||
#define CHB_Rx_AVAIL 0x04
|
||||
#define CHB_SPECIAL 0x06
|
||||
#define CHA_Tx_EMPTY 0x08
|
||||
#define CHA_EXT_STAT 0x0a
|
||||
#define CHA_Rx_AVAIL 0x0c
|
||||
#define CHA_SPECIAL 0x0e
|
||||
#define STATUS_MASK 0x06
|
||||
|
||||
/* Read Register 3 (interrupt pending register) ch a only */
|
||||
#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
|
||||
#define CHBTxIP 0x2 /* Channel B Tx IP */
|
||||
#define CHBRxIP 0x4 /* Channel B Rx IP */
|
||||
#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
|
||||
#define CHATxIP 0x10 /* Channel A Tx IP */
|
||||
#define CHARxIP 0x20 /* Channel A Rx IP */
|
||||
|
||||
/* Read Register 8 (receive data register) */
|
||||
|
||||
/* Read Register 10 (misc status bits) */
|
||||
#define ONLOOP 2 /* On loop */
|
||||
#define LOOPSEND 0x10 /* Loop sending */
|
||||
#define CLK2MIS 0x40 /* Two clocks missing */
|
||||
#define CLK1MIS 0x80 /* One clock missing */
|
||||
|
||||
/* Read Register 12 (lower byte of baud rate generator constant) */
|
||||
|
||||
/* Read Register 13 (upper byte of baud rate generator constant) */
|
||||
|
||||
/* Read Register 15 (value of WR 15) */
|
||||
|
||||
/* Misc macros */
|
||||
#define ZS_CLEARERR(port) (write_zsreg(port, 0, ERR_RES))
|
||||
#define ZS_CLEARFIFO(port) do { volatile unsigned char garbage; \
|
||||
garbage = read_zsdata(port); \
|
||||
garbage = read_zsdata(port); \
|
||||
garbage = read_zsdata(port); \
|
||||
} while(0)
|
||||
|
||||
#define ZS_IS_CONS(UP) ((UP)->flags & PMACZILOG_FLAG_IS_CONS)
|
||||
#define ZS_IS_KGDB(UP) ((UP)->flags & PMACZILOG_FLAG_IS_KGDB)
|
||||
#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & PMACZILOG_FLAG_IS_CHANNEL_A)
|
||||
#define ZS_REGS_HELD(UP) ((UP)->flags & PMACZILOG_FLAG_REGS_HELD)
|
||||
#define ZS_TX_STOPPED(UP) ((UP)->flags & PMACZILOG_FLAG_TX_STOPPED)
|
||||
#define ZS_TX_ACTIVE(UP) ((UP)->flags & PMACZILOG_FLAG_TX_ACTIVE)
|
||||
#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & PMACZILOG_FLAG_MODEM_STATUS)
|
||||
#define ZS_IS_IRDA(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRDA)
|
||||
#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM)
|
||||
#define ZS_HAS_DMA(UP) ((UP)->flags & PMACZILOG_FLAG_HAS_DMA)
|
||||
#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP)
|
||||
#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN)
|
||||
#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON)
|
||||
#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK)
|
||||
|
||||
#endif /* __PMAC_ZILOG_H__ */
|
||||
852
drivers/serial/pnx8xxx_uart.c
Normal file
852
drivers/serial/pnx8xxx_uart.c
Normal file
@@ -0,0 +1,852 @@
|
||||
/*
|
||||
* UART driver for PNX8XXX SoCs
|
||||
*
|
||||
* Author: Per Hallsmark per.hallsmark@mvista.com
|
||||
* Ported to 2.6 kernel by EmbeddedAlley
|
||||
* Reworked by Vitaly Wool <vitalywool@gmail.com>
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of
|
||||
* any kind, whether express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_PNX8XXX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_pnx8xxx.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
/* We'll be using StrongARM sa1100 serial port major/minor */
|
||||
#define SERIAL_PNX8XXX_MAJOR 204
|
||||
#define MINOR_START 5
|
||||
|
||||
#define NR_PORTS 2
|
||||
|
||||
#define PNX8XXX_ISR_PASS_LIMIT 256
|
||||
|
||||
/*
|
||||
* Convert from ignore_status_mask or read_status_mask to FIFO
|
||||
* and interrupt status bits
|
||||
*/
|
||||
#define SM_TO_FIFO(x) ((x) >> 10)
|
||||
#define SM_TO_ISTAT(x) ((x) & 0x000001ff)
|
||||
#define FIFO_TO_SM(x) ((x) << 10)
|
||||
#define ISTAT_TO_SM(x) ((x) & 0x000001ff)
|
||||
|
||||
/*
|
||||
* This is the size of our serial port register set.
|
||||
*/
|
||||
#define UART_PORT_SIZE 0x1000
|
||||
|
||||
/*
|
||||
* This determines how often we check the modem status signals
|
||||
* for any change. They generally aren't connected to an IRQ
|
||||
* so we have to poll them. We also check immediately before
|
||||
* filling the TX fifo incase CTS has been dropped.
|
||||
*/
|
||||
#define MCTRL_TIMEOUT (250*HZ/1000)
|
||||
|
||||
extern struct pnx8xxx_port pnx8xxx_ports[];
|
||||
|
||||
static inline int serial_in(struct pnx8xxx_port *sport, int offset)
|
||||
{
|
||||
return (__raw_readl(sport->port.membase + offset));
|
||||
}
|
||||
|
||||
static inline void serial_out(struct pnx8xxx_port *sport, int offset, int value)
|
||||
{
|
||||
__raw_writel(value, sport->port.membase + offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle any change of modem status signal since we were last called.
|
||||
*/
|
||||
static void pnx8xxx_mctrl_check(struct pnx8xxx_port *sport)
|
||||
{
|
||||
unsigned int status, changed;
|
||||
|
||||
status = sport->port.ops->get_mctrl(&sport->port);
|
||||
changed = status ^ sport->old_status;
|
||||
|
||||
if (changed == 0)
|
||||
return;
|
||||
|
||||
sport->old_status = status;
|
||||
|
||||
if (changed & TIOCM_RI)
|
||||
sport->port.icount.rng++;
|
||||
if (changed & TIOCM_DSR)
|
||||
sport->port.icount.dsr++;
|
||||
if (changed & TIOCM_CAR)
|
||||
uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
|
||||
if (changed & TIOCM_CTS)
|
||||
uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
|
||||
|
||||
wake_up_interruptible(&sport->port.info->delta_msr_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is our per-port timeout handler, for checking the
|
||||
* modem status signals.
|
||||
*/
|
||||
static void pnx8xxx_timeout(unsigned long data)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)data;
|
||||
unsigned long flags;
|
||||
|
||||
if (sport->port.info) {
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
pnx8xxx_mctrl_check(sport);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupts disabled on entry
|
||||
*/
|
||||
static void pnx8xxx_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
u32 ien;
|
||||
|
||||
/* Disable TX intr */
|
||||
ien = serial_in(sport, PNX8XXX_IEN);
|
||||
serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLTX);
|
||||
|
||||
/* Clear all pending TX intr */
|
||||
serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX);
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupts may not be disabled on entry
|
||||
*/
|
||||
static void pnx8xxx_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
u32 ien;
|
||||
|
||||
/* Clear all pending TX intr */
|
||||
serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX);
|
||||
|
||||
/* Enable TX intr */
|
||||
ien = serial_in(sport, PNX8XXX_IEN);
|
||||
serial_out(sport, PNX8XXX_IEN, ien | PNX8XXX_UART_INT_ALLTX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts enabled
|
||||
*/
|
||||
static void pnx8xxx_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
u32 ien;
|
||||
|
||||
/* Disable RX intr */
|
||||
ien = serial_in(sport, PNX8XXX_IEN);
|
||||
serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLRX);
|
||||
|
||||
/* Clear all pending RX intr */
|
||||
serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the modem control timer to fire immediately.
|
||||
*/
|
||||
static void pnx8xxx_enable_ms(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
|
||||
mod_timer(&sport->timer, jiffies);
|
||||
}
|
||||
|
||||
static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport)
|
||||
{
|
||||
struct tty_struct *tty = sport->port.info->tty;
|
||||
unsigned int status, ch, flg;
|
||||
|
||||
status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) |
|
||||
ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT));
|
||||
while (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFIFO)) {
|
||||
ch = serial_in(sport, PNX8XXX_FIFO);
|
||||
|
||||
sport->port.icount.rx++;
|
||||
|
||||
flg = TTY_NORMAL;
|
||||
|
||||
/*
|
||||
* note that the error handling code is
|
||||
* out of the main execution path
|
||||
*/
|
||||
if (status & (FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE |
|
||||
PNX8XXX_UART_FIFO_RXPAR) |
|
||||
ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN))) {
|
||||
if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR))
|
||||
sport->port.icount.parity++;
|
||||
else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE))
|
||||
sport->port.icount.frame++;
|
||||
if (status & ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN))
|
||||
sport->port.icount.overrun++;
|
||||
|
||||
status &= sport->port.read_status_mask;
|
||||
|
||||
if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR))
|
||||
flg = TTY_PARITY;
|
||||
else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE))
|
||||
flg = TTY_FRAME;
|
||||
|
||||
#ifdef SUPPORT_SYSRQ
|
||||
sport->port.sysrq = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(&sport->port, ch))
|
||||
goto ignore_char;
|
||||
|
||||
uart_insert_char(&sport->port, status,
|
||||
ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN), ch, flg);
|
||||
|
||||
ignore_char:
|
||||
serial_out(sport, PNX8XXX_LCR, serial_in(sport, PNX8XXX_LCR) |
|
||||
PNX8XXX_UART_LCR_RX_NEXT);
|
||||
status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) |
|
||||
ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT));
|
||||
}
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
||||
static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport)
|
||||
{
|
||||
struct circ_buf *xmit = &sport->port.info->xmit;
|
||||
|
||||
if (sport->port.x_char) {
|
||||
serial_out(sport, PNX8XXX_FIFO, sport->port.x_char);
|
||||
sport->port.icount.tx++;
|
||||
sport->port.x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the modem control lines before
|
||||
* transmitting anything.
|
||||
*/
|
||||
pnx8xxx_mctrl_check(sport);
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
|
||||
pnx8xxx_stop_tx(&sport->port);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TX while bytes available
|
||||
*/
|
||||
while (((serial_in(sport, PNX8XXX_FIFO) &
|
||||
PNX8XXX_UART_FIFO_TXFIFO) >> 16) < 16) {
|
||||
serial_out(sport, PNX8XXX_FIFO, xmit->buf[xmit->tail]);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
sport->port.icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&sport->port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
pnx8xxx_stop_tx(&sport->port);
|
||||
}
|
||||
|
||||
static irqreturn_t pnx8xxx_int(int irq, void *dev_id)
|
||||
{
|
||||
struct pnx8xxx_port *sport = dev_id;
|
||||
unsigned int status;
|
||||
|
||||
spin_lock(&sport->port.lock);
|
||||
/* Get the interrupts */
|
||||
status = serial_in(sport, PNX8XXX_ISTAT) & serial_in(sport, PNX8XXX_IEN);
|
||||
|
||||
/* Break signal received */
|
||||
if (status & PNX8XXX_UART_INT_BREAK) {
|
||||
sport->port.icount.brk++;
|
||||
uart_handle_break(&sport->port);
|
||||
}
|
||||
|
||||
/* Byte received */
|
||||
if (status & PNX8XXX_UART_INT_RX)
|
||||
pnx8xxx_rx_chars(sport);
|
||||
|
||||
/* TX holding register empty - transmit a byte */
|
||||
if (status & PNX8XXX_UART_INT_TX)
|
||||
pnx8xxx_tx_chars(sport);
|
||||
|
||||
/* Clear the ISTAT register */
|
||||
serial_out(sport, PNX8XXX_ICLR, status);
|
||||
|
||||
spin_unlock(&sport->port.lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TIOCSER_TEMT when transmitter is not busy.
|
||||
*/
|
||||
static unsigned int pnx8xxx_tx_empty(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
|
||||
return serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static unsigned int pnx8xxx_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
unsigned int mctrl = TIOCM_DSR;
|
||||
unsigned int msr;
|
||||
|
||||
/* REVISIT */
|
||||
|
||||
msr = serial_in(sport, PNX8XXX_MCR);
|
||||
|
||||
mctrl |= msr & PNX8XXX_UART_MCR_CTS ? TIOCM_CTS : 0;
|
||||
mctrl |= msr & PNX8XXX_UART_MCR_DCD ? TIOCM_CAR : 0;
|
||||
|
||||
return mctrl;
|
||||
}
|
||||
|
||||
static void pnx8xxx_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
#if 0 /* FIXME */
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
unsigned int msr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts always disabled.
|
||||
*/
|
||||
static void pnx8xxx_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
unsigned long flags;
|
||||
unsigned int lcr;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
lcr = serial_in(sport, PNX8XXX_LCR);
|
||||
if (break_state == -1)
|
||||
lcr |= PNX8XXX_UART_LCR_TXBREAK;
|
||||
else
|
||||
lcr &= ~PNX8XXX_UART_LCR_TXBREAK;
|
||||
serial_out(sport, PNX8XXX_LCR, lcr);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static int pnx8xxx_startup(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Allocate the IRQ
|
||||
*/
|
||||
retval = request_irq(sport->port.irq, pnx8xxx_int, 0,
|
||||
"pnx8xxx-uart", sport);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/*
|
||||
* Finally, clear and enable interrupts
|
||||
*/
|
||||
|
||||
serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX |
|
||||
PNX8XXX_UART_INT_ALLTX);
|
||||
|
||||
serial_out(sport, PNX8XXX_IEN, serial_in(sport, PNX8XXX_IEN) |
|
||||
PNX8XXX_UART_INT_ALLRX |
|
||||
PNX8XXX_UART_INT_ALLTX);
|
||||
|
||||
/*
|
||||
* Enable modem status interrupts
|
||||
*/
|
||||
spin_lock_irq(&sport->port.lock);
|
||||
pnx8xxx_enable_ms(&sport->port);
|
||||
spin_unlock_irq(&sport->port.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnx8xxx_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
int lcr;
|
||||
|
||||
/*
|
||||
* Stop our timer.
|
||||
*/
|
||||
del_timer_sync(&sport->timer);
|
||||
|
||||
/*
|
||||
* Disable all interrupts
|
||||
*/
|
||||
serial_out(sport, PNX8XXX_IEN, 0);
|
||||
|
||||
/*
|
||||
* Reset the Tx and Rx FIFOS, disable the break condition
|
||||
*/
|
||||
lcr = serial_in(sport, PNX8XXX_LCR);
|
||||
lcr &= ~PNX8XXX_UART_LCR_TXBREAK;
|
||||
lcr |= PNX8XXX_UART_LCR_TX_RST | PNX8XXX_UART_LCR_RX_RST;
|
||||
serial_out(sport, PNX8XXX_LCR, lcr);
|
||||
|
||||
/*
|
||||
* Clear all interrupts
|
||||
*/
|
||||
serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX |
|
||||
PNX8XXX_UART_INT_ALLTX);
|
||||
|
||||
/*
|
||||
* Free the interrupt
|
||||
*/
|
||||
free_irq(sport->port.irq, sport);
|
||||
}
|
||||
|
||||
static void
|
||||
pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
unsigned long flags;
|
||||
unsigned int lcr_fcr, old_ien, baud, quot;
|
||||
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
|
||||
|
||||
/*
|
||||
* We only support CS7 and CS8.
|
||||
*/
|
||||
while ((termios->c_cflag & CSIZE) != CS7 &&
|
||||
(termios->c_cflag & CSIZE) != CS8) {
|
||||
termios->c_cflag &= ~CSIZE;
|
||||
termios->c_cflag |= old_csize;
|
||||
old_csize = CS8;
|
||||
}
|
||||
|
||||
if ((termios->c_cflag & CSIZE) == CS8)
|
||||
lcr_fcr = PNX8XXX_UART_LCR_8BIT;
|
||||
else
|
||||
lcr_fcr = 0;
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
lcr_fcr |= PNX8XXX_UART_LCR_2STOPB;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
lcr_fcr |= PNX8XXX_UART_LCR_PAREN;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
lcr_fcr |= PNX8XXX_UART_LCR_PAREVN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
sport->port.read_status_mask = ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN) |
|
||||
ISTAT_TO_SM(PNX8XXX_UART_INT_EMPTY) |
|
||||
ISTAT_TO_SM(PNX8XXX_UART_INT_RX);
|
||||
if (termios->c_iflag & INPCK)
|
||||
sport->port.read_status_mask |=
|
||||
FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) |
|
||||
FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR);
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
sport->port.read_status_mask |=
|
||||
ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK);
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
sport->port.ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
sport->port.ignore_status_mask |=
|
||||
FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) |
|
||||
FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR);
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
sport->port.ignore_status_mask |=
|
||||
ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK);
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
sport->port.ignore_status_mask |=
|
||||
ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN);
|
||||
}
|
||||
|
||||
/*
|
||||
* ignore all characters if CREAD is not set
|
||||
*/
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
sport->port.ignore_status_mask |=
|
||||
ISTAT_TO_SM(PNX8XXX_UART_INT_RX);
|
||||
|
||||
del_timer_sync(&sport->timer);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/*
|
||||
* disable interrupts and drain transmitter
|
||||
*/
|
||||
old_ien = serial_in(sport, PNX8XXX_IEN);
|
||||
serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX |
|
||||
PNX8XXX_UART_INT_ALLRX));
|
||||
|
||||
while (serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA)
|
||||
barrier();
|
||||
|
||||
/* then, disable everything */
|
||||
serial_out(sport, PNX8XXX_IEN, 0);
|
||||
|
||||
/* Reset the Rx and Tx FIFOs too */
|
||||
lcr_fcr |= PNX8XXX_UART_LCR_TX_RST;
|
||||
lcr_fcr |= PNX8XXX_UART_LCR_RX_RST;
|
||||
|
||||
/* set the parity, stop bits and data size */
|
||||
serial_out(sport, PNX8XXX_LCR, lcr_fcr);
|
||||
|
||||
/* set the baud rate */
|
||||
quot -= 1;
|
||||
serial_out(sport, PNX8XXX_BAUD, quot);
|
||||
|
||||
serial_out(sport, PNX8XXX_ICLR, -1);
|
||||
|
||||
serial_out(sport, PNX8XXX_IEN, old_ien);
|
||||
|
||||
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
|
||||
pnx8xxx_enable_ms(&sport->port);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static const char *pnx8xxx_type(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
|
||||
return sport->port.type == PORT_PNX8XXX ? "PNX8XXX" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory region(s) being used by 'port'.
|
||||
*/
|
||||
static void pnx8xxx_release_port(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
|
||||
release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Request the memory region(s) being used by 'port'.
|
||||
*/
|
||||
static int pnx8xxx_request_port(struct uart_port *port)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
|
||||
"pnx8xxx-uart") != NULL ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void pnx8xxx_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
|
||||
if (flags & UART_CONFIG_TYPE &&
|
||||
pnx8xxx_request_port(&sport->port) == 0)
|
||||
sport->port.type = PORT_PNX8XXX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the new serial_struct (for TIOCSSERIAL).
|
||||
* The only change we allow are to the flags and type, and
|
||||
* even then only between PORT_PNX8XXX and PORT_UNKNOWN
|
||||
*/
|
||||
static int
|
||||
pnx8xxx_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
int ret = 0;
|
||||
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_PNX8XXX)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.irq != ser->irq)
|
||||
ret = -EINVAL;
|
||||
if (ser->io_type != SERIAL_IO_MEM)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.uartclk / 16 != ser->baud_base)
|
||||
ret = -EINVAL;
|
||||
if ((void *)sport->port.mapbase != ser->iomem_base)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.iobase != ser->port)
|
||||
ret = -EINVAL;
|
||||
if (ser->hub6 != 0)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops pnx8xxx_pops = {
|
||||
.tx_empty = pnx8xxx_tx_empty,
|
||||
.set_mctrl = pnx8xxx_set_mctrl,
|
||||
.get_mctrl = pnx8xxx_get_mctrl,
|
||||
.stop_tx = pnx8xxx_stop_tx,
|
||||
.start_tx = pnx8xxx_start_tx,
|
||||
.stop_rx = pnx8xxx_stop_rx,
|
||||
.enable_ms = pnx8xxx_enable_ms,
|
||||
.break_ctl = pnx8xxx_break_ctl,
|
||||
.startup = pnx8xxx_startup,
|
||||
.shutdown = pnx8xxx_shutdown,
|
||||
.set_termios = pnx8xxx_set_termios,
|
||||
.type = pnx8xxx_type,
|
||||
.release_port = pnx8xxx_release_port,
|
||||
.request_port = pnx8xxx_request_port,
|
||||
.config_port = pnx8xxx_config_port,
|
||||
.verify_port = pnx8xxx_verify_port,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Setup the PNX8XXX serial ports.
|
||||
*
|
||||
* Note also that we support "console=ttySx" where "x" is either 0 or 1.
|
||||
*/
|
||||
static void __init pnx8xxx_init_ports(void)
|
||||
{
|
||||
static int first = 1;
|
||||
int i;
|
||||
|
||||
if (!first)
|
||||
return;
|
||||
first = 0;
|
||||
|
||||
for (i = 0; i < NR_PORTS; i++) {
|
||||
init_timer(&pnx8xxx_ports[i].timer);
|
||||
pnx8xxx_ports[i].timer.function = pnx8xxx_timeout;
|
||||
pnx8xxx_ports[i].timer.data = (unsigned long)&pnx8xxx_ports[i];
|
||||
pnx8xxx_ports[i].port.ops = &pnx8xxx_pops;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_PNX8XXX_CONSOLE
|
||||
|
||||
static void pnx8xxx_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
|
||||
int status;
|
||||
|
||||
do {
|
||||
/* Wait for UART_TX register to empty */
|
||||
status = serial_in(sport, PNX8XXX_FIFO);
|
||||
} while (status & PNX8XXX_UART_FIFO_TXFIFO);
|
||||
serial_out(sport, PNX8XXX_FIFO, ch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts are disabled on entering
|
||||
*/static void
|
||||
pnx8xxx_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct pnx8xxx_port *sport = &pnx8xxx_ports[co->index];
|
||||
unsigned int old_ien, status;
|
||||
|
||||
/*
|
||||
* First, save IEN and then disable interrupts
|
||||
*/
|
||||
old_ien = serial_in(sport, PNX8XXX_IEN);
|
||||
serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX |
|
||||
PNX8XXX_UART_INT_ALLRX));
|
||||
|
||||
uart_console_write(&sport->port, s, count, pnx8xxx_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore IEN
|
||||
*/
|
||||
do {
|
||||
/* Wait for UART_TX register to empty */
|
||||
status = serial_in(sport, PNX8XXX_FIFO);
|
||||
} while (status & PNX8XXX_UART_FIFO_TXFIFO);
|
||||
|
||||
/* Clear TX and EMPTY interrupt */
|
||||
serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_TX |
|
||||
PNX8XXX_UART_INT_EMPTY);
|
||||
|
||||
serial_out(sport, PNX8XXX_IEN, old_ien);
|
||||
}
|
||||
|
||||
static int __init
|
||||
pnx8xxx_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct pnx8xxx_port *sport;
|
||||
int baud = 38400;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
if (co->index == -1 || co->index >= NR_PORTS)
|
||||
co->index = 0;
|
||||
sport = &pnx8xxx_ports[co->index];
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
return uart_set_options(&sport->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver pnx8xxx_reg;
|
||||
static struct console pnx8xxx_console = {
|
||||
.name = "ttyS",
|
||||
.write = pnx8xxx_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = pnx8xxx_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &pnx8xxx_reg,
|
||||
};
|
||||
|
||||
static int __init pnx8xxx_rs_console_init(void)
|
||||
{
|
||||
pnx8xxx_init_ports();
|
||||
register_console(&pnx8xxx_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(pnx8xxx_rs_console_init);
|
||||
|
||||
#define PNX8XXX_CONSOLE &pnx8xxx_console
|
||||
#else
|
||||
#define PNX8XXX_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver pnx8xxx_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "ttyS",
|
||||
.dev_name = "ttyS",
|
||||
.major = SERIAL_PNX8XXX_MAJOR,
|
||||
.minor = MINOR_START,
|
||||
.nr = NR_PORTS,
|
||||
.cons = PNX8XXX_CONSOLE,
|
||||
};
|
||||
|
||||
static int pnx8xxx_serial_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
return uart_suspend_port(&pnx8xxx_reg, &sport->port);
|
||||
}
|
||||
|
||||
static int pnx8xxx_serial_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
return uart_resume_port(&pnx8xxx_reg, &sport->port);
|
||||
}
|
||||
|
||||
static int pnx8xxx_serial_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = pdev->resource;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pdev->num_resources; i++, res++) {
|
||||
if (!(res->flags & IORESOURCE_MEM))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < NR_PORTS; i++) {
|
||||
if (pnx8xxx_ports[i].port.mapbase != res->start)
|
||||
continue;
|
||||
|
||||
pnx8xxx_ports[i].port.dev = &pdev->dev;
|
||||
uart_add_one_port(&pnx8xxx_reg, &pnx8xxx_ports[i].port);
|
||||
platform_set_drvdata(pdev, &pnx8xxx_ports[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pnx8xxx_serial_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (sport)
|
||||
uart_remove_one_port(&pnx8xxx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pnx8xxx_serial_driver = {
|
||||
.driver = {
|
||||
.name = "pnx8xxx-uart",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = pnx8xxx_serial_probe,
|
||||
.remove = pnx8xxx_serial_remove,
|
||||
.suspend = pnx8xxx_serial_suspend,
|
||||
.resume = pnx8xxx_serial_resume,
|
||||
};
|
||||
|
||||
static int __init pnx8xxx_serial_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "Serial: PNX8XXX driver $Revision: 1.1.1.1 $\n");
|
||||
|
||||
pnx8xxx_init_ports();
|
||||
|
||||
ret = uart_register_driver(&pnx8xxx_reg);
|
||||
if (ret == 0) {
|
||||
ret = platform_driver_register(&pnx8xxx_serial_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&pnx8xxx_reg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit pnx8xxx_serial_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pnx8xxx_serial_driver);
|
||||
uart_unregister_driver(&pnx8xxx_reg);
|
||||
}
|
||||
|
||||
module_init(pnx8xxx_serial_init);
|
||||
module_exit(pnx8xxx_serial_exit);
|
||||
|
||||
MODULE_AUTHOR("Embedded Alley Solutions, Inc.");
|
||||
MODULE_DESCRIPTION("PNX8XXX SoCs serial port driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_PNX8XXX_MAJOR);
|
||||
863
drivers/serial/pxa.c
Normal file
863
drivers/serial/pxa.c
Normal file
@@ -0,0 +1,863 @@
|
||||
/*
|
||||
* linux/drivers/serial/pxa.c
|
||||
*
|
||||
* Based on drivers/serial/8250.c by Russell King.
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Feb 20, 2003
|
||||
* Copyright: (C) 2003 Monta Vista Software, Inc.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Note 1: This driver is made separate from the already too overloaded
|
||||
* 8250.c because it needs some kirks of its own and that'll make it
|
||||
* easier to add DMA support.
|
||||
*
|
||||
* Note 2: I'm too sick of device allocation policies for serial ports.
|
||||
* If someone else wants to request an "official" allocation of major/minor
|
||||
* for this driver please be my guest. And don't forget that new hardware
|
||||
* to come from Intel might have more than 3 or 4 of those UARTs. Let's
|
||||
* hope for a better port registration and dynamic device allocation scheme
|
||||
* with the serial core maintainer satisfaction to appear soon.
|
||||
*/
|
||||
|
||||
|
||||
#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
|
||||
|
||||
struct uart_pxa_port {
|
||||
struct uart_port port;
|
||||
unsigned char ier;
|
||||
unsigned char lcr;
|
||||
unsigned char mcr;
|
||||
unsigned int lsr_break_flag;
|
||||
unsigned int cken;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
|
||||
{
|
||||
offset <<= 2;
|
||||
return readl(up->port.membase + offset);
|
||||
}
|
||||
|
||||
static inline void serial_out(struct uart_pxa_port *up, int offset, int value)
|
||||
{
|
||||
offset <<= 2;
|
||||
writel(value, up->port.membase + offset);
|
||||
}
|
||||
|
||||
static void serial_pxa_enable_ms(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
|
||||
up->ier |= UART_IER_MSI;
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
}
|
||||
|
||||
static void serial_pxa_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
|
||||
if (up->ier & UART_IER_THRI) {
|
||||
up->ier &= ~UART_IER_THRI;
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_pxa_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
|
||||
up->ier &= ~UART_IER_RLSI;
|
||||
up->port.read_status_mask &= ~UART_LSR_DR;
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
}
|
||||
|
||||
static inline void receive_chars(struct uart_pxa_port *up, int *status)
|
||||
{
|
||||
struct tty_struct *tty = up->port.info->tty;
|
||||
unsigned int ch, flag;
|
||||
int max_count = 256;
|
||||
|
||||
do {
|
||||
ch = serial_in(up, UART_RX);
|
||||
flag = TTY_NORMAL;
|
||||
up->port.icount.rx++;
|
||||
|
||||
if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
|
||||
UART_LSR_FE | UART_LSR_OE))) {
|
||||
/*
|
||||
* For statistics only
|
||||
*/
|
||||
if (*status & UART_LSR_BI) {
|
||||
*status &= ~(UART_LSR_FE | UART_LSR_PE);
|
||||
up->port.icount.brk++;
|
||||
/*
|
||||
* We do the SysRQ and SAK checking
|
||||
* here because otherwise the break
|
||||
* may get masked by ignore_status_mask
|
||||
* or read_status_mask.
|
||||
*/
|
||||
if (uart_handle_break(&up->port))
|
||||
goto ignore_char;
|
||||
} else if (*status & UART_LSR_PE)
|
||||
up->port.icount.parity++;
|
||||
else if (*status & UART_LSR_FE)
|
||||
up->port.icount.frame++;
|
||||
if (*status & UART_LSR_OE)
|
||||
up->port.icount.overrun++;
|
||||
|
||||
/*
|
||||
* Mask off conditions which should be ignored.
|
||||
*/
|
||||
*status &= up->port.read_status_mask;
|
||||
|
||||
#ifdef CONFIG_SERIAL_PXA_CONSOLE
|
||||
if (up->port.line == up->port.cons->index) {
|
||||
/* Recover the break flag from console xmit */
|
||||
*status |= up->lsr_break_flag;
|
||||
up->lsr_break_flag = 0;
|
||||
}
|
||||
#endif
|
||||
if (*status & UART_LSR_BI) {
|
||||
flag = TTY_BREAK;
|
||||
} else if (*status & UART_LSR_PE)
|
||||
flag = TTY_PARITY;
|
||||
else if (*status & UART_LSR_FE)
|
||||
flag = TTY_FRAME;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(&up->port, ch))
|
||||
goto ignore_char;
|
||||
|
||||
uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
|
||||
|
||||
ignore_char:
|
||||
*status = serial_in(up, UART_LSR);
|
||||
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
||||
static void transmit_chars(struct uart_pxa_port *up)
|
||||
{
|
||||
struct circ_buf *xmit = &up->port.info->xmit;
|
||||
int count;
|
||||
|
||||
if (up->port.x_char) {
|
||||
serial_out(up, UART_TX, up->port.x_char);
|
||||
up->port.icount.tx++;
|
||||
up->port.x_char = 0;
|
||||
return;
|
||||
}
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
|
||||
serial_pxa_stop_tx(&up->port);
|
||||
return;
|
||||
}
|
||||
|
||||
count = up->port.fifosize / 2;
|
||||
do {
|
||||
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
up->port.icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (--count > 0);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&up->port);
|
||||
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
serial_pxa_stop_tx(&up->port);
|
||||
}
|
||||
|
||||
static void serial_pxa_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
|
||||
if (!(up->ier & UART_IER_THRI)) {
|
||||
up->ier |= UART_IER_THRI;
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void check_modem_status(struct uart_pxa_port *up)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = serial_in(up, UART_MSR);
|
||||
|
||||
if ((status & UART_MSR_ANY_DELTA) == 0)
|
||||
return;
|
||||
|
||||
if (status & UART_MSR_TERI)
|
||||
up->port.icount.rng++;
|
||||
if (status & UART_MSR_DDSR)
|
||||
up->port.icount.dsr++;
|
||||
if (status & UART_MSR_DDCD)
|
||||
uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
|
||||
if (status & UART_MSR_DCTS)
|
||||
uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
|
||||
|
||||
wake_up_interruptible(&up->port.info->delta_msr_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* This handles the interrupt from one port.
|
||||
*/
|
||||
static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_pxa_port *up = dev_id;
|
||||
unsigned int iir, lsr;
|
||||
|
||||
iir = serial_in(up, UART_IIR);
|
||||
if (iir & UART_IIR_NO_INT)
|
||||
return IRQ_NONE;
|
||||
lsr = serial_in(up, UART_LSR);
|
||||
if (lsr & UART_LSR_DR)
|
||||
receive_chars(up, &lsr);
|
||||
check_modem_status(up);
|
||||
if (lsr & UART_LSR_THRE)
|
||||
transmit_chars(up);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int serial_pxa_tx_empty(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
unsigned long flags;
|
||||
unsigned int ret;
|
||||
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int serial_pxa_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
unsigned char status;
|
||||
unsigned int ret;
|
||||
|
||||
status = serial_in(up, UART_MSR);
|
||||
|
||||
ret = 0;
|
||||
if (status & UART_MSR_DCD)
|
||||
ret |= TIOCM_CAR;
|
||||
if (status & UART_MSR_RI)
|
||||
ret |= TIOCM_RNG;
|
||||
if (status & UART_MSR_DSR)
|
||||
ret |= TIOCM_DSR;
|
||||
if (status & UART_MSR_CTS)
|
||||
ret |= TIOCM_CTS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
unsigned char mcr = 0;
|
||||
|
||||
if (mctrl & TIOCM_RTS)
|
||||
mcr |= UART_MCR_RTS;
|
||||
if (mctrl & TIOCM_DTR)
|
||||
mcr |= UART_MCR_DTR;
|
||||
if (mctrl & TIOCM_OUT1)
|
||||
mcr |= UART_MCR_OUT1;
|
||||
if (mctrl & TIOCM_OUT2)
|
||||
mcr |= UART_MCR_OUT2;
|
||||
if (mctrl & TIOCM_LOOP)
|
||||
mcr |= UART_MCR_LOOP;
|
||||
|
||||
mcr |= up->mcr;
|
||||
|
||||
serial_out(up, UART_MCR, mcr);
|
||||
}
|
||||
|
||||
static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
if (break_state == -1)
|
||||
up->lcr |= UART_LCR_SBC;
|
||||
else
|
||||
up->lcr &= ~UART_LCR_SBC;
|
||||
serial_out(up, UART_LCR, up->lcr);
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void serial_pxa_dma_init(struct pxa_uart *up)
|
||||
{
|
||||
up->rxdma =
|
||||
pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up);
|
||||
if (up->rxdma < 0)
|
||||
goto out;
|
||||
up->txdma =
|
||||
pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up);
|
||||
if (up->txdma < 0)
|
||||
goto err_txdma;
|
||||
up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL);
|
||||
if (!up->dmadesc)
|
||||
goto err_alloc;
|
||||
|
||||
/* ... */
|
||||
err_alloc:
|
||||
pxa_free_dma(up->txdma);
|
||||
err_rxdma:
|
||||
pxa_free_dma(up->rxdma);
|
||||
out:
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int serial_pxa_startup(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
if (port->line == 3) /* HWUART */
|
||||
up->mcr |= UART_MCR_AFE;
|
||||
else
|
||||
up->mcr = 0;
|
||||
|
||||
/*
|
||||
* Allocate the IRQ
|
||||
*/
|
||||
retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/*
|
||||
* Clear the FIFO buffers and disable them.
|
||||
* (they will be reenabled in set_termios())
|
||||
*/
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
||||
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
|
||||
serial_out(up, UART_FCR, 0);
|
||||
|
||||
/*
|
||||
* Clear the interrupt registers.
|
||||
*/
|
||||
(void) serial_in(up, UART_LSR);
|
||||
(void) serial_in(up, UART_RX);
|
||||
(void) serial_in(up, UART_IIR);
|
||||
(void) serial_in(up, UART_MSR);
|
||||
|
||||
/*
|
||||
* Now, initialize the UART
|
||||
*/
|
||||
serial_out(up, UART_LCR, UART_LCR_WLEN8);
|
||||
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
up->port.mctrl |= TIOCM_OUT2;
|
||||
serial_pxa_set_mctrl(&up->port, up->port.mctrl);
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
|
||||
/*
|
||||
* Finally, enable interrupts. Note: Modem status interrupts
|
||||
* are set via set_termios(), which will be occurring imminently
|
||||
* anyway, so we don't enable them here.
|
||||
*/
|
||||
up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
|
||||
/*
|
||||
* And clear the interrupt registers again for luck.
|
||||
*/
|
||||
(void) serial_in(up, UART_LSR);
|
||||
(void) serial_in(up, UART_RX);
|
||||
(void) serial_in(up, UART_IIR);
|
||||
(void) serial_in(up, UART_MSR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serial_pxa_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
unsigned long flags;
|
||||
|
||||
free_irq(up->port.irq, up);
|
||||
|
||||
/*
|
||||
* Disable interrupts from this port
|
||||
*/
|
||||
up->ier = 0;
|
||||
serial_out(up, UART_IER, 0);
|
||||
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
up->port.mctrl &= ~TIOCM_OUT2;
|
||||
serial_pxa_set_mctrl(&up->port, up->port.mctrl);
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
|
||||
/*
|
||||
* Disable break condition and FIFOs
|
||||
*/
|
||||
serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
||||
UART_FCR_CLEAR_RCVR |
|
||||
UART_FCR_CLEAR_XMIT);
|
||||
serial_out(up, UART_FCR, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
unsigned char cval, fcr = 0;
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot;
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
cval = UART_LCR_WLEN5;
|
||||
break;
|
||||
case CS6:
|
||||
cval = UART_LCR_WLEN6;
|
||||
break;
|
||||
case CS7:
|
||||
cval = UART_LCR_WLEN7;
|
||||
break;
|
||||
default:
|
||||
case CS8:
|
||||
cval = UART_LCR_WLEN8;
|
||||
break;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
cval |= UART_LCR_STOP;
|
||||
if (termios->c_cflag & PARENB)
|
||||
cval |= UART_LCR_PARITY;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
cval |= UART_LCR_EPAR;
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
if ((up->port.uartclk / quot) < (2400 * 16))
|
||||
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1;
|
||||
else if ((up->port.uartclk / quot) < (230400 * 16))
|
||||
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8;
|
||||
else
|
||||
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
|
||||
|
||||
/*
|
||||
* Ok, we're now changing the port state. Do it with
|
||||
* interrupts disabled.
|
||||
*/
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
|
||||
/*
|
||||
* Ensure the port will be enabled.
|
||||
* This is required especially for serial console.
|
||||
*/
|
||||
up->ier |= IER_UUE;
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
|
||||
if (termios->c_iflag & INPCK)
|
||||
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
up->port.read_status_mask |= UART_LSR_BI;
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
up->port.ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
up->port.ignore_status_mask |= UART_LSR_BI;
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
up->port.ignore_status_mask |= UART_LSR_OE;
|
||||
}
|
||||
|
||||
/*
|
||||
* ignore all characters if CREAD is not set
|
||||
*/
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
up->port.ignore_status_mask |= UART_LSR_DR;
|
||||
|
||||
/*
|
||||
* CTS flow control flag and modem status interrupts
|
||||
*/
|
||||
up->ier &= ~UART_IER_MSI;
|
||||
if (UART_ENABLE_MS(&up->port, termios->c_cflag))
|
||||
up->ier |= UART_IER_MSI;
|
||||
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
|
||||
serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
|
||||
serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */
|
||||
serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */
|
||||
serial_out(up, UART_LCR, cval); /* reset DLAB */
|
||||
up->lcr = cval; /* Save LCR */
|
||||
serial_pxa_set_mctrl(&up->port, up->port.mctrl);
|
||||
serial_out(up, UART_FCR, fcr);
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_pxa_pm(struct uart_port *port, unsigned int state,
|
||||
unsigned int oldstate)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
pxa_set_cken(up->cken, !state);
|
||||
if (!state)
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
static void serial_pxa_release_port(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static int serial_pxa_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serial_pxa_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
up->port.type = PORT_PXA;
|
||||
}
|
||||
|
||||
static int
|
||||
serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
/* we don't want the core code to modify any port params */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
serial_pxa_type(struct uart_port *port)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
return up->name;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_PXA_CONSOLE
|
||||
|
||||
static struct uart_pxa_port serial_pxa_ports[];
|
||||
static struct uart_driver serial_pxa_reg;
|
||||
|
||||
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
|
||||
|
||||
/*
|
||||
* Wait for transmitter & holding register to empty
|
||||
*/
|
||||
static inline void wait_for_xmitr(struct uart_pxa_port *up)
|
||||
{
|
||||
unsigned int status, tmout = 10000;
|
||||
|
||||
/* Wait up to 10ms for the character(s) to be sent. */
|
||||
do {
|
||||
status = serial_in(up, UART_LSR);
|
||||
|
||||
if (status & UART_LSR_BI)
|
||||
up->lsr_break_flag = UART_LSR_BI;
|
||||
|
||||
if (--tmout == 0)
|
||||
break;
|
||||
udelay(1);
|
||||
} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
|
||||
|
||||
/* Wait up to 1s for flow control if necessary */
|
||||
if (up->port.flags & UPF_CONS_FLOW) {
|
||||
tmout = 1000000;
|
||||
while (--tmout &&
|
||||
((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
|
||||
udelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_pxa_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
|
||||
|
||||
wait_for_xmitr(up);
|
||||
serial_out(up, UART_TX, ch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a string to the serial port trying not to disturb
|
||||
* any possible real use of the port...
|
||||
*
|
||||
* The console_lock must be held when we get here.
|
||||
*/
|
||||
static void
|
||||
serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct uart_pxa_port *up = &serial_pxa_ports[co->index];
|
||||
unsigned int ier;
|
||||
|
||||
/*
|
||||
* First save the IER then disable the interrupts
|
||||
*/
|
||||
ier = serial_in(up, UART_IER);
|
||||
serial_out(up, UART_IER, UART_IER_UUE);
|
||||
|
||||
uart_console_write(&up->port, s, count, serial_pxa_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore the IER
|
||||
*/
|
||||
wait_for_xmitr(up);
|
||||
serial_out(up, UART_IER, ier);
|
||||
}
|
||||
|
||||
static int __init
|
||||
serial_pxa_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_pxa_port *up;
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (co->index == -1 || co->index >= serial_pxa_reg.nr)
|
||||
co->index = 0;
|
||||
up = &serial_pxa_ports[co->index];
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
return uart_set_options(&up->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct console serial_pxa_console = {
|
||||
.name = "ttyS",
|
||||
.write = serial_pxa_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = serial_pxa_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &serial_pxa_reg,
|
||||
};
|
||||
|
||||
static int __init
|
||||
serial_pxa_console_init(void)
|
||||
{
|
||||
register_console(&serial_pxa_console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_initcall(serial_pxa_console_init);
|
||||
|
||||
#define PXA_CONSOLE &serial_pxa_console
|
||||
#else
|
||||
#define PXA_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
struct uart_ops serial_pxa_pops = {
|
||||
.tx_empty = serial_pxa_tx_empty,
|
||||
.set_mctrl = serial_pxa_set_mctrl,
|
||||
.get_mctrl = serial_pxa_get_mctrl,
|
||||
.stop_tx = serial_pxa_stop_tx,
|
||||
.start_tx = serial_pxa_start_tx,
|
||||
.stop_rx = serial_pxa_stop_rx,
|
||||
.enable_ms = serial_pxa_enable_ms,
|
||||
.break_ctl = serial_pxa_break_ctl,
|
||||
.startup = serial_pxa_startup,
|
||||
.shutdown = serial_pxa_shutdown,
|
||||
.set_termios = serial_pxa_set_termios,
|
||||
.pm = serial_pxa_pm,
|
||||
.type = serial_pxa_type,
|
||||
.release_port = serial_pxa_release_port,
|
||||
.request_port = serial_pxa_request_port,
|
||||
.config_port = serial_pxa_config_port,
|
||||
.verify_port = serial_pxa_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_pxa_port serial_pxa_ports[] = {
|
||||
{ /* FFUART */
|
||||
.name = "FFUART",
|
||||
.cken = CKEN6_FFUART,
|
||||
.port = {
|
||||
.type = PORT_PXA,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (void *)&FFUART,
|
||||
.mapbase = __PREG(FFUART),
|
||||
.irq = IRQ_FFUART,
|
||||
.uartclk = 921600 * 16,
|
||||
.fifosize = 64,
|
||||
.ops = &serial_pxa_pops,
|
||||
.line = 0,
|
||||
},
|
||||
}, { /* BTUART */
|
||||
.name = "BTUART",
|
||||
.cken = CKEN7_BTUART,
|
||||
.port = {
|
||||
.type = PORT_PXA,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (void *)&BTUART,
|
||||
.mapbase = __PREG(BTUART),
|
||||
.irq = IRQ_BTUART,
|
||||
.uartclk = 921600 * 16,
|
||||
.fifosize = 64,
|
||||
.ops = &serial_pxa_pops,
|
||||
.line = 1,
|
||||
},
|
||||
}, { /* STUART */
|
||||
.name = "STUART",
|
||||
.cken = CKEN5_STUART,
|
||||
.port = {
|
||||
.type = PORT_PXA,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (void *)&STUART,
|
||||
.mapbase = __PREG(STUART),
|
||||
.irq = IRQ_STUART,
|
||||
.uartclk = 921600 * 16,
|
||||
.fifosize = 64,
|
||||
.ops = &serial_pxa_pops,
|
||||
.line = 2,
|
||||
},
|
||||
}, { /* HWUART */
|
||||
.name = "HWUART",
|
||||
.cken = CKEN4_HWUART,
|
||||
.port = {
|
||||
.type = PORT_PXA,
|
||||
.iotype = UPIO_MEM,
|
||||
.membase = (void *)&HWUART,
|
||||
.mapbase = __PREG(HWUART),
|
||||
.irq = IRQ_HWUART,
|
||||
.uartclk = 921600 * 16,
|
||||
.fifosize = 64,
|
||||
.ops = &serial_pxa_pops,
|
||||
.line = 3,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static struct uart_driver serial_pxa_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "PXA serial",
|
||||
.dev_name = "ttyS",
|
||||
.major = TTY_MAJOR,
|
||||
.minor = 64,
|
||||
.nr = ARRAY_SIZE(serial_pxa_ports),
|
||||
.cons = PXA_CONSOLE,
|
||||
};
|
||||
|
||||
static int serial_pxa_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct uart_pxa_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
uart_suspend_port(&serial_pxa_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_pxa_resume(struct platform_device *dev)
|
||||
{
|
||||
struct uart_pxa_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
uart_resume_port(&serial_pxa_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_pxa_probe(struct platform_device *dev)
|
||||
{
|
||||
serial_pxa_ports[dev->id].port.dev = &dev->dev;
|
||||
uart_add_one_port(&serial_pxa_reg, &serial_pxa_ports[dev->id].port);
|
||||
platform_set_drvdata(dev, &serial_pxa_ports[dev->id]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_pxa_remove(struct platform_device *dev)
|
||||
{
|
||||
struct uart_pxa_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
platform_set_drvdata(dev, NULL);
|
||||
|
||||
if (sport)
|
||||
uart_remove_one_port(&serial_pxa_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver serial_pxa_driver = {
|
||||
.probe = serial_pxa_probe,
|
||||
.remove = serial_pxa_remove,
|
||||
|
||||
.suspend = serial_pxa_suspend,
|
||||
.resume = serial_pxa_resume,
|
||||
.driver = {
|
||||
.name = "pxa2xx-uart",
|
||||
},
|
||||
};
|
||||
|
||||
int __init serial_pxa_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uart_register_driver(&serial_pxa_reg);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&serial_pxa_driver);
|
||||
if (ret != 0)
|
||||
uart_unregister_driver(&serial_pxa_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __exit serial_pxa_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&serial_pxa_driver);
|
||||
uart_unregister_driver(&serial_pxa_reg);
|
||||
}
|
||||
|
||||
module_init(serial_pxa_init);
|
||||
module_exit(serial_pxa_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
1975
drivers/serial/s3c2410.c
Normal file
1975
drivers/serial/s3c2410.c
Normal file
File diff suppressed because it is too large
Load Diff
1679
drivers/serial/s3c6400.c
Normal file
1679
drivers/serial/s3c6400.c
Normal file
File diff suppressed because it is too large
Load Diff
919
drivers/serial/sa1100.c
Normal file
919
drivers/serial/sa1100.c
Normal file
@@ -0,0 +1,919 @@
|
||||
/*
|
||||
* linux/drivers/char/sa1100.c
|
||||
*
|
||||
* Driver for SA11x0 serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
*
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Id: sa1100.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/mach/serial_sa1100.h>
|
||||
|
||||
/* We've been assigned a range on the "Low-density serial ports" major */
|
||||
#define SERIAL_SA1100_MAJOR 204
|
||||
#define MINOR_START 5
|
||||
|
||||
#define NR_PORTS 3
|
||||
|
||||
#define SA1100_ISR_PASS_LIMIT 256
|
||||
|
||||
/*
|
||||
* Convert from ignore_status_mask or read_status_mask to UTSR[01]
|
||||
*/
|
||||
#define SM_TO_UTSR0(x) ((x) & 0xff)
|
||||
#define SM_TO_UTSR1(x) ((x) >> 8)
|
||||
#define UTSR0_TO_SM(x) ((x))
|
||||
#define UTSR1_TO_SM(x) ((x) << 8)
|
||||
|
||||
#define UART_GET_UTCR0(sport) __raw_readl((sport)->port.membase + UTCR0)
|
||||
#define UART_GET_UTCR1(sport) __raw_readl((sport)->port.membase + UTCR1)
|
||||
#define UART_GET_UTCR2(sport) __raw_readl((sport)->port.membase + UTCR2)
|
||||
#define UART_GET_UTCR3(sport) __raw_readl((sport)->port.membase + UTCR3)
|
||||
#define UART_GET_UTSR0(sport) __raw_readl((sport)->port.membase + UTSR0)
|
||||
#define UART_GET_UTSR1(sport) __raw_readl((sport)->port.membase + UTSR1)
|
||||
#define UART_GET_CHAR(sport) __raw_readl((sport)->port.membase + UTDR)
|
||||
|
||||
#define UART_PUT_UTCR0(sport,v) __raw_writel((v),(sport)->port.membase + UTCR0)
|
||||
#define UART_PUT_UTCR1(sport,v) __raw_writel((v),(sport)->port.membase + UTCR1)
|
||||
#define UART_PUT_UTCR2(sport,v) __raw_writel((v),(sport)->port.membase + UTCR2)
|
||||
#define UART_PUT_UTCR3(sport,v) __raw_writel((v),(sport)->port.membase + UTCR3)
|
||||
#define UART_PUT_UTSR0(sport,v) __raw_writel((v),(sport)->port.membase + UTSR0)
|
||||
#define UART_PUT_UTSR1(sport,v) __raw_writel((v),(sport)->port.membase + UTSR1)
|
||||
#define UART_PUT_CHAR(sport,v) __raw_writel((v),(sport)->port.membase + UTDR)
|
||||
|
||||
/*
|
||||
* This is the size of our serial port register set.
|
||||
*/
|
||||
#define UART_PORT_SIZE 0x24
|
||||
|
||||
/*
|
||||
* This determines how often we check the modem status signals
|
||||
* for any change. They generally aren't connected to an IRQ
|
||||
* so we have to poll them. We also check immediately before
|
||||
* filling the TX fifo incase CTS has been dropped.
|
||||
*/
|
||||
#define MCTRL_TIMEOUT (250*HZ/1000)
|
||||
|
||||
struct sa1100_port {
|
||||
struct uart_port port;
|
||||
struct timer_list timer;
|
||||
unsigned int old_status;
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle any change of modem status signal since we were last called.
|
||||
*/
|
||||
static void sa1100_mctrl_check(struct sa1100_port *sport)
|
||||
{
|
||||
unsigned int status, changed;
|
||||
|
||||
status = sport->port.ops->get_mctrl(&sport->port);
|
||||
changed = status ^ sport->old_status;
|
||||
|
||||
if (changed == 0)
|
||||
return;
|
||||
|
||||
sport->old_status = status;
|
||||
|
||||
if (changed & TIOCM_RI)
|
||||
sport->port.icount.rng++;
|
||||
if (changed & TIOCM_DSR)
|
||||
sport->port.icount.dsr++;
|
||||
if (changed & TIOCM_CAR)
|
||||
uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
|
||||
if (changed & TIOCM_CTS)
|
||||
uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
|
||||
|
||||
wake_up_interruptible(&sport->port.info->delta_msr_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is our per-port timeout handler, for checking the
|
||||
* modem status signals.
|
||||
*/
|
||||
static void sa1100_timeout(unsigned long data)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)data;
|
||||
unsigned long flags;
|
||||
|
||||
if (sport->port.info) {
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
sa1100_mctrl_check(sport);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
||||
mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupts disabled on entry
|
||||
*/
|
||||
static void sa1100_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
u32 utcr3;
|
||||
|
||||
utcr3 = UART_GET_UTCR3(sport);
|
||||
UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE);
|
||||
sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS);
|
||||
}
|
||||
|
||||
/*
|
||||
* port locked and interrupts disabled
|
||||
*/
|
||||
static void sa1100_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
u32 utcr3;
|
||||
|
||||
utcr3 = UART_GET_UTCR3(sport);
|
||||
sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS);
|
||||
UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts enabled
|
||||
*/
|
||||
static void sa1100_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
u32 utcr3;
|
||||
|
||||
utcr3 = UART_GET_UTCR3(sport);
|
||||
UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the modem control timer to fire immediately.
|
||||
*/
|
||||
static void sa1100_enable_ms(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
|
||||
mod_timer(&sport->timer, jiffies);
|
||||
}
|
||||
|
||||
static void
|
||||
sa1100_rx_chars(struct sa1100_port *sport)
|
||||
{
|
||||
struct tty_struct *tty = sport->port.info->tty;
|
||||
unsigned int status, ch, flg;
|
||||
|
||||
status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
|
||||
UTSR0_TO_SM(UART_GET_UTSR0(sport));
|
||||
while (status & UTSR1_TO_SM(UTSR1_RNE)) {
|
||||
ch = UART_GET_CHAR(sport);
|
||||
|
||||
sport->port.icount.rx++;
|
||||
|
||||
flg = TTY_NORMAL;
|
||||
|
||||
/*
|
||||
* note that the error handling code is
|
||||
* out of the main execution path
|
||||
*/
|
||||
if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) {
|
||||
if (status & UTSR1_TO_SM(UTSR1_PRE))
|
||||
sport->port.icount.parity++;
|
||||
else if (status & UTSR1_TO_SM(UTSR1_FRE))
|
||||
sport->port.icount.frame++;
|
||||
if (status & UTSR1_TO_SM(UTSR1_ROR))
|
||||
sport->port.icount.overrun++;
|
||||
|
||||
status &= sport->port.read_status_mask;
|
||||
|
||||
if (status & UTSR1_TO_SM(UTSR1_PRE))
|
||||
flg = TTY_PARITY;
|
||||
else if (status & UTSR1_TO_SM(UTSR1_FRE))
|
||||
flg = TTY_FRAME;
|
||||
|
||||
#ifdef SUPPORT_SYSRQ
|
||||
sport->port.sysrq = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(&sport->port, ch))
|
||||
goto ignore_char;
|
||||
|
||||
uart_insert_char(&sport->port, status, UTSR1_TO_SM(UTSR1_ROR), ch, flg);
|
||||
|
||||
ignore_char:
|
||||
status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
|
||||
UTSR0_TO_SM(UART_GET_UTSR0(sport));
|
||||
}
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
||||
static void sa1100_tx_chars(struct sa1100_port *sport)
|
||||
{
|
||||
struct circ_buf *xmit = &sport->port.info->xmit;
|
||||
|
||||
if (sport->port.x_char) {
|
||||
UART_PUT_CHAR(sport, sport->port.x_char);
|
||||
sport->port.icount.tx++;
|
||||
sport->port.x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the modem control lines before
|
||||
* transmitting anything.
|
||||
*/
|
||||
sa1100_mctrl_check(sport);
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
|
||||
sa1100_stop_tx(&sport->port);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tried using FIFO (not checking TNF) for fifo fill:
|
||||
* still had the '4 bytes repeated' problem.
|
||||
*/
|
||||
while (UART_GET_UTSR1(sport) & UTSR1_TNF) {
|
||||
UART_PUT_CHAR(sport, xmit->buf[xmit->tail]);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
sport->port.icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&sport->port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
sa1100_stop_tx(&sport->port);
|
||||
}
|
||||
|
||||
static irqreturn_t sa1100_int(int irq, void *dev_id)
|
||||
{
|
||||
struct sa1100_port *sport = dev_id;
|
||||
unsigned int status, pass_counter = 0;
|
||||
|
||||
spin_lock(&sport->port.lock);
|
||||
status = UART_GET_UTSR0(sport);
|
||||
status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS;
|
||||
do {
|
||||
if (status & (UTSR0_RFS | UTSR0_RID)) {
|
||||
/* Clear the receiver idle bit, if set */
|
||||
if (status & UTSR0_RID)
|
||||
UART_PUT_UTSR0(sport, UTSR0_RID);
|
||||
sa1100_rx_chars(sport);
|
||||
}
|
||||
|
||||
/* Clear the relevant break bits */
|
||||
if (status & (UTSR0_RBB | UTSR0_REB))
|
||||
UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB));
|
||||
|
||||
if (status & UTSR0_RBB)
|
||||
sport->port.icount.brk++;
|
||||
|
||||
if (status & UTSR0_REB)
|
||||
uart_handle_break(&sport->port);
|
||||
|
||||
if (status & UTSR0_TFS)
|
||||
sa1100_tx_chars(sport);
|
||||
if (pass_counter++ > SA1100_ISR_PASS_LIMIT)
|
||||
break;
|
||||
status = UART_GET_UTSR0(sport);
|
||||
status &= SM_TO_UTSR0(sport->port.read_status_mask) |
|
||||
~UTSR0_TFS;
|
||||
} while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID));
|
||||
spin_unlock(&sport->port.lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TIOCSER_TEMT when transmitter is not busy.
|
||||
*/
|
||||
static unsigned int sa1100_tx_empty(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
|
||||
return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static unsigned int sa1100_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
|
||||
}
|
||||
|
||||
static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts always disabled.
|
||||
*/
|
||||
static void sa1100_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
unsigned long flags;
|
||||
unsigned int utcr3;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
utcr3 = UART_GET_UTCR3(sport);
|
||||
if (break_state == -1)
|
||||
utcr3 |= UTCR3_BRK;
|
||||
else
|
||||
utcr3 &= ~UTCR3_BRK;
|
||||
UART_PUT_UTCR3(sport, utcr3);
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static int sa1100_startup(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Allocate the IRQ
|
||||
*/
|
||||
retval = request_irq(sport->port.irq, sa1100_int, 0,
|
||||
"sa11x0-uart", sport);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/*
|
||||
* Finally, clear and enable interrupts
|
||||
*/
|
||||
UART_PUT_UTSR0(sport, -1);
|
||||
UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE);
|
||||
|
||||
/*
|
||||
* Enable modem status interrupts
|
||||
*/
|
||||
spin_lock_irq(&sport->port.lock);
|
||||
sa1100_enable_ms(&sport->port);
|
||||
spin_unlock_irq(&sport->port.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sa1100_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
|
||||
/*
|
||||
* Stop our timer.
|
||||
*/
|
||||
del_timer_sync(&sport->timer);
|
||||
|
||||
/*
|
||||
* Free the interrupt
|
||||
*/
|
||||
free_irq(sport->port.irq, sport);
|
||||
|
||||
/*
|
||||
* Disable all interrupts, port and break condition.
|
||||
*/
|
||||
UART_PUT_UTCR3(sport, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
sa1100_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
unsigned long flags;
|
||||
unsigned int utcr0, old_utcr3, baud, quot;
|
||||
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
|
||||
|
||||
/*
|
||||
* We only support CS7 and CS8.
|
||||
*/
|
||||
while ((termios->c_cflag & CSIZE) != CS7 &&
|
||||
(termios->c_cflag & CSIZE) != CS8) {
|
||||
termios->c_cflag &= ~CSIZE;
|
||||
termios->c_cflag |= old_csize;
|
||||
old_csize = CS8;
|
||||
}
|
||||
|
||||
if ((termios->c_cflag & CSIZE) == CS8)
|
||||
utcr0 = UTCR0_DSS;
|
||||
else
|
||||
utcr0 = 0;
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
utcr0 |= UTCR0_SBS;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
utcr0 |= UTCR0_PE;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
utcr0 |= UTCR0_OES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ask the core to calculate the divisor for us.
|
||||
*/
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS);
|
||||
sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR);
|
||||
if (termios->c_iflag & INPCK)
|
||||
sport->port.read_status_mask |=
|
||||
UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
sport->port.read_status_mask |=
|
||||
UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB);
|
||||
|
||||
/*
|
||||
* Characters to ignore
|
||||
*/
|
||||
sport->port.ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
sport->port.ignore_status_mask |=
|
||||
UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
sport->port.ignore_status_mask |=
|
||||
UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB);
|
||||
/*
|
||||
* If we're ignoring parity and break indicators,
|
||||
* ignore overruns too (for real raw support).
|
||||
*/
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
sport->port.ignore_status_mask |=
|
||||
UTSR1_TO_SM(UTSR1_ROR);
|
||||
}
|
||||
|
||||
del_timer_sync(&sport->timer);
|
||||
|
||||
/*
|
||||
* Update the per-port timeout.
|
||||
*/
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/*
|
||||
* disable interrupts and drain transmitter
|
||||
*/
|
||||
old_utcr3 = UART_GET_UTCR3(sport);
|
||||
UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE));
|
||||
|
||||
while (UART_GET_UTSR1(sport) & UTSR1_TBY)
|
||||
barrier();
|
||||
|
||||
/* then, disable everything */
|
||||
UART_PUT_UTCR3(sport, 0);
|
||||
|
||||
/* set the parity, stop bits and data size */
|
||||
UART_PUT_UTCR0(sport, utcr0);
|
||||
|
||||
/* set the baud rate */
|
||||
quot -= 1;
|
||||
UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8));
|
||||
UART_PUT_UTCR2(sport, (quot & 0xff));
|
||||
|
||||
UART_PUT_UTSR0(sport, -1);
|
||||
|
||||
UART_PUT_UTCR3(sport, old_utcr3);
|
||||
|
||||
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
|
||||
sa1100_enable_ms(&sport->port);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static const char *sa1100_type(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
|
||||
return sport->port.type == PORT_SA1100 ? "SA1100" : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory region(s) being used by 'port'.
|
||||
*/
|
||||
static void sa1100_release_port(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
|
||||
release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Request the memory region(s) being used by 'port'.
|
||||
*/
|
||||
static int sa1100_request_port(struct uart_port *port)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
|
||||
return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
|
||||
"sa11x0-uart") != NULL ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void sa1100_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
|
||||
if (flags & UART_CONFIG_TYPE &&
|
||||
sa1100_request_port(&sport->port) == 0)
|
||||
sport->port.type = PORT_SA1100;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the new serial_struct (for TIOCSSERIAL).
|
||||
* The only change we allow are to the flags and type, and
|
||||
* even then only between PORT_SA1100 and PORT_UNKNOWN
|
||||
*/
|
||||
static int
|
||||
sa1100_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
int ret = 0;
|
||||
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.irq != ser->irq)
|
||||
ret = -EINVAL;
|
||||
if (ser->io_type != SERIAL_IO_MEM)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.uartclk / 16 != ser->baud_base)
|
||||
ret = -EINVAL;
|
||||
if ((void *)sport->port.mapbase != ser->iomem_base)
|
||||
ret = -EINVAL;
|
||||
if (sport->port.iobase != ser->port)
|
||||
ret = -EINVAL;
|
||||
if (ser->hub6 != 0)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops sa1100_pops = {
|
||||
.tx_empty = sa1100_tx_empty,
|
||||
.set_mctrl = sa1100_set_mctrl,
|
||||
.get_mctrl = sa1100_get_mctrl,
|
||||
.stop_tx = sa1100_stop_tx,
|
||||
.start_tx = sa1100_start_tx,
|
||||
.stop_rx = sa1100_stop_rx,
|
||||
.enable_ms = sa1100_enable_ms,
|
||||
.break_ctl = sa1100_break_ctl,
|
||||
.startup = sa1100_startup,
|
||||
.shutdown = sa1100_shutdown,
|
||||
.set_termios = sa1100_set_termios,
|
||||
.type = sa1100_type,
|
||||
.release_port = sa1100_release_port,
|
||||
.request_port = sa1100_request_port,
|
||||
.config_port = sa1100_config_port,
|
||||
.verify_port = sa1100_verify_port,
|
||||
};
|
||||
|
||||
static struct sa1100_port sa1100_ports[NR_PORTS];
|
||||
|
||||
/*
|
||||
* Setup the SA1100 serial ports. Note that we don't include the IrDA
|
||||
* port here since we have our own SIR/FIR driver (see drivers/net/irda)
|
||||
*
|
||||
* Note also that we support "console=ttySAx" where "x" is either 0 or 1.
|
||||
* Which serial port this ends up being depends on the machine you're
|
||||
* running this kernel on. I'm not convinced that this is a good idea,
|
||||
* but that's the way it traditionally works.
|
||||
*
|
||||
* Note that NanoEngine UART3 becomes UART2, and UART2 is no longer
|
||||
* used here.
|
||||
*/
|
||||
static void __init sa1100_init_ports(void)
|
||||
{
|
||||
static int first = 1;
|
||||
int i;
|
||||
|
||||
if (!first)
|
||||
return;
|
||||
first = 0;
|
||||
|
||||
for (i = 0; i < NR_PORTS; i++) {
|
||||
sa1100_ports[i].port.uartclk = 3686400;
|
||||
sa1100_ports[i].port.ops = &sa1100_pops;
|
||||
sa1100_ports[i].port.fifosize = 8;
|
||||
sa1100_ports[i].port.line = i;
|
||||
sa1100_ports[i].port.iotype = UPIO_MEM;
|
||||
init_timer(&sa1100_ports[i].timer);
|
||||
sa1100_ports[i].timer.function = sa1100_timeout;
|
||||
sa1100_ports[i].timer.data = (unsigned long)&sa1100_ports[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* make transmit lines outputs, so that when the port
|
||||
* is closed, the output is in the MARK state.
|
||||
*/
|
||||
PPDR |= PPC_TXD1 | PPC_TXD3;
|
||||
PPSR |= PPC_TXD1 | PPC_TXD3;
|
||||
}
|
||||
|
||||
void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns)
|
||||
{
|
||||
if (fns->get_mctrl)
|
||||
sa1100_pops.get_mctrl = fns->get_mctrl;
|
||||
if (fns->set_mctrl)
|
||||
sa1100_pops.set_mctrl = fns->set_mctrl;
|
||||
|
||||
sa1100_pops.pm = fns->pm;
|
||||
sa1100_pops.set_wake = fns->set_wake;
|
||||
}
|
||||
|
||||
void __init sa1100_register_uart(int idx, int port)
|
||||
{
|
||||
if (idx >= NR_PORTS) {
|
||||
printk(KERN_ERR "%s: bad index number %d\n", __FUNCTION__, idx);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (port) {
|
||||
case 1:
|
||||
sa1100_ports[idx].port.membase = (void __iomem *)&Ser1UTCR0;
|
||||
sa1100_ports[idx].port.mapbase = _Ser1UTCR0;
|
||||
sa1100_ports[idx].port.irq = IRQ_Ser1UART;
|
||||
sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
sa1100_ports[idx].port.membase = (void __iomem *)&Ser2UTCR0;
|
||||
sa1100_ports[idx].port.mapbase = _Ser2UTCR0;
|
||||
sa1100_ports[idx].port.irq = IRQ_Ser2ICP;
|
||||
sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
sa1100_ports[idx].port.membase = (void __iomem *)&Ser3UTCR0;
|
||||
sa1100_ports[idx].port.mapbase = _Ser3UTCR0;
|
||||
sa1100_ports[idx].port.irq = IRQ_Ser3UART;
|
||||
sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "%s: bad port number %d\n", __FUNCTION__, port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_SERIAL_SA1100_CONSOLE
|
||||
static void sa1100_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
struct sa1100_port *sport = (struct sa1100_port *)port;
|
||||
|
||||
while (!(UART_GET_UTSR1(sport) & UTSR1_TNF))
|
||||
barrier();
|
||||
UART_PUT_CHAR(sport, ch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts are disabled on entering
|
||||
*/
|
||||
static void
|
||||
sa1100_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct sa1100_port *sport = &sa1100_ports[co->index];
|
||||
unsigned int old_utcr3, status;
|
||||
|
||||
/*
|
||||
* First, save UTCR3 and then disable interrupts
|
||||
*/
|
||||
old_utcr3 = UART_GET_UTCR3(sport);
|
||||
UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) |
|
||||
UTCR3_TXE);
|
||||
|
||||
uart_console_write(&sport->port, s, count, sa1100_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore UTCR3
|
||||
*/
|
||||
do {
|
||||
status = UART_GET_UTSR1(sport);
|
||||
} while (status & UTSR1_TBY);
|
||||
UART_PUT_UTCR3(sport, old_utcr3);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the port was already initialised (eg, by a boot loader),
|
||||
* try to determine the current setup.
|
||||
*/
|
||||
static void __init
|
||||
sa1100_console_get_options(struct sa1100_port *sport, int *baud,
|
||||
int *parity, int *bits)
|
||||
{
|
||||
unsigned int utcr3;
|
||||
|
||||
utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE);
|
||||
if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) {
|
||||
/* ok, the port was enabled */
|
||||
unsigned int utcr0, quot;
|
||||
|
||||
utcr0 = UART_GET_UTCR0(sport);
|
||||
|
||||
*parity = 'n';
|
||||
if (utcr0 & UTCR0_PE) {
|
||||
if (utcr0 & UTCR0_OES)
|
||||
*parity = 'e';
|
||||
else
|
||||
*parity = 'o';
|
||||
}
|
||||
|
||||
if (utcr0 & UTCR0_DSS)
|
||||
*bits = 8;
|
||||
else
|
||||
*bits = 7;
|
||||
|
||||
quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8;
|
||||
quot &= 0xfff;
|
||||
*baud = sport->port.uartclk / (16 * (quot + 1));
|
||||
}
|
||||
}
|
||||
|
||||
static int __init
|
||||
sa1100_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct sa1100_port *sport;
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
/*
|
||||
* Check whether an invalid uart number has been specified, and
|
||||
* if so, search for the first available port that does have
|
||||
* console support.
|
||||
*/
|
||||
if (co->index == -1 || co->index >= NR_PORTS)
|
||||
co->index = 0;
|
||||
sport = &sa1100_ports[co->index];
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
sa1100_console_get_options(sport, &baud, &parity, &bits);
|
||||
|
||||
return uart_set_options(&sport->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver sa1100_reg;
|
||||
static struct console sa1100_console = {
|
||||
.name = "ttySA",
|
||||
.write = sa1100_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = sa1100_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &sa1100_reg,
|
||||
};
|
||||
|
||||
static int __init sa1100_rs_console_init(void)
|
||||
{
|
||||
sa1100_init_ports();
|
||||
register_console(&sa1100_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(sa1100_rs_console_init);
|
||||
|
||||
#define SA1100_CONSOLE &sa1100_console
|
||||
#else
|
||||
#define SA1100_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver sa1100_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "ttySA",
|
||||
.dev_name = "ttySA",
|
||||
.major = SERIAL_SA1100_MAJOR,
|
||||
.minor = MINOR_START,
|
||||
.nr = NR_PORTS,
|
||||
.cons = SA1100_CONSOLE,
|
||||
};
|
||||
|
||||
static int sa1100_serial_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct sa1100_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
uart_suspend_port(&sa1100_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sa1100_serial_resume(struct platform_device *dev)
|
||||
{
|
||||
struct sa1100_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
uart_resume_port(&sa1100_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sa1100_serial_probe(struct platform_device *dev)
|
||||
{
|
||||
struct resource *res = dev->resource;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->num_resources; i++, res++)
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
break;
|
||||
|
||||
if (i < dev->num_resources) {
|
||||
for (i = 0; i < NR_PORTS; i++) {
|
||||
if (sa1100_ports[i].port.mapbase != res->start)
|
||||
continue;
|
||||
|
||||
sa1100_ports[i].port.dev = &dev->dev;
|
||||
uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port);
|
||||
platform_set_drvdata(dev, &sa1100_ports[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sa1100_serial_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sa1100_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (sport)
|
||||
uart_remove_one_port(&sa1100_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sa11x0_serial_driver = {
|
||||
.probe = sa1100_serial_probe,
|
||||
.remove = sa1100_serial_remove,
|
||||
.suspend = sa1100_serial_suspend,
|
||||
.resume = sa1100_serial_resume,
|
||||
.driver = {
|
||||
.name = "sa11x0-uart",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init sa1100_serial_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.1.1.1 $\n");
|
||||
|
||||
sa1100_init_ports();
|
||||
|
||||
ret = uart_register_driver(&sa1100_reg);
|
||||
if (ret == 0) {
|
||||
ret = platform_driver_register(&sa11x0_serial_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&sa1100_reg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit sa1100_serial_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sa11x0_serial_driver);
|
||||
uart_unregister_driver(&sa1100_reg);
|
||||
}
|
||||
|
||||
module_init(sa1100_serial_init);
|
||||
module_exit(sa1100_serial_exit);
|
||||
|
||||
MODULE_AUTHOR("Deep Blue Solutions Ltd");
|
||||
MODULE_DESCRIPTION("SA1100 generic serial port driver $Revision: 1.1.1.1 $");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_SA1100_MAJOR);
|
||||
2427
drivers/serial/serial_core.c
Normal file
2427
drivers/serial/serial_core.c
Normal file
File diff suppressed because it is too large
Load Diff
975
drivers/serial/serial_cs.c
Normal file
975
drivers/serial/serial_cs.c
Normal file
@@ -0,0 +1,975 @@
|
||||
/*======================================================================
|
||||
|
||||
A driver for PCMCIA serial devices
|
||||
|
||||
serial_cs.c 1.134 2002/05/04 05:48:53
|
||||
|
||||
The contents of this file are subject to the Mozilla Public
|
||||
License Version 1.1 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a copy of
|
||||
the License at http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS
|
||||
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
rights and limitations under the License.
|
||||
|
||||
The initial developer of the original code is David A. Hinds
|
||||
<dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
|
||||
Alternatively, the contents of this file may be used under the
|
||||
terms of the GNU General Public License version 2 (the "GPL"), in which
|
||||
case the provisions of the GPL are applicable instead of the
|
||||
above. If you wish to allow the use of your version of this file
|
||||
only under the terms of the GPL and not to allow others to use
|
||||
your version of this file under the MPL, indicate your decision
|
||||
by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this
|
||||
file under either the MPL or the GPL.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/major.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/cs.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ciscode.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
#ifdef PCMCIA_DEBUG
|
||||
static int pc_debug = PCMCIA_DEBUG;
|
||||
module_param(pc_debug, int, 0644);
|
||||
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
|
||||
static char *version = "serial_cs.c 1.134 2002/05/04 05:48:53 (David Hinds)";
|
||||
#else
|
||||
#define DEBUG(n, args...)
|
||||
#endif
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
/* Parameters that can be set with 'insmod' */
|
||||
|
||||
/* Enable the speaker? */
|
||||
static int do_sound = 1;
|
||||
/* Skip strict UART tests? */
|
||||
static int buggy_uart;
|
||||
|
||||
module_param(do_sound, int, 0444);
|
||||
module_param(buggy_uart, int, 0444);
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
/* Table of multi-port card ID's */
|
||||
|
||||
struct serial_quirk {
|
||||
unsigned int manfid;
|
||||
unsigned int prodid;
|
||||
int multi; /* 1 = multifunction, > 1 = # ports */
|
||||
void (*config)(struct pcmcia_device *);
|
||||
void (*setup)(struct pcmcia_device *, struct uart_port *);
|
||||
void (*wakeup)(struct pcmcia_device *);
|
||||
int (*post)(struct pcmcia_device *);
|
||||
};
|
||||
|
||||
struct serial_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
int ndev;
|
||||
int multi;
|
||||
int slave;
|
||||
int manfid;
|
||||
int prodid;
|
||||
int c950ctrl;
|
||||
dev_node_t node[4];
|
||||
int line[4];
|
||||
const struct serial_quirk *quirk;
|
||||
};
|
||||
|
||||
struct serial_cfg_mem {
|
||||
tuple_t tuple;
|
||||
cisparse_t parse;
|
||||
u_char buf[256];
|
||||
};
|
||||
|
||||
/*
|
||||
* vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6"
|
||||
* manfid 0x0160, 0x0104
|
||||
* This card appears to have a 14.7456MHz clock.
|
||||
*/
|
||||
static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port)
|
||||
{
|
||||
port->uartclk = 14745600;
|
||||
}
|
||||
|
||||
static int quirk_post_ibm(struct pcmcia_device *link)
|
||||
{
|
||||
conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
|
||||
int last_ret, last_fn;
|
||||
|
||||
last_ret = pcmcia_access_configuration_register(link, ®);
|
||||
if (last_ret) {
|
||||
last_fn = AccessConfigurationRegister;
|
||||
goto cs_failed;
|
||||
}
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Value = reg.Value | 1;
|
||||
last_ret = pcmcia_access_configuration_register(link, ®);
|
||||
if (last_ret) {
|
||||
last_fn = AccessConfigurationRegister;
|
||||
goto cs_failed;
|
||||
}
|
||||
return 0;
|
||||
|
||||
cs_failed:
|
||||
cs_error(link, last_fn, last_ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nokia cards are not really multiport cards. Shouldn't this
|
||||
* be handled by setting the quirk entry .multi = 0 | 1 ?
|
||||
*/
|
||||
static void quirk_config_nokia(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
|
||||
if (info->multi > 1)
|
||||
info->multi = 1;
|
||||
}
|
||||
|
||||
static void quirk_wakeup_oxsemi(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
|
||||
outb(12, info->c950ctrl + 1);
|
||||
}
|
||||
|
||||
/* request_region? oxsemi branch does no request_region too... */
|
||||
/*
|
||||
* This sequence is needed to properly initialize MC45 attached to OXCF950.
|
||||
* I tried decreasing these msleep()s, but it worked properly (survived
|
||||
* 1000 stop/start operations) with these timeouts (or bigger).
|
||||
*/
|
||||
static void quirk_wakeup_possio_gcc(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
unsigned int ctrl = info->c950ctrl;
|
||||
|
||||
outb(0xA, ctrl + 1);
|
||||
msleep(100);
|
||||
outb(0xE, ctrl + 1);
|
||||
msleep(300);
|
||||
outb(0xC, ctrl + 1);
|
||||
msleep(100);
|
||||
outb(0xE, ctrl + 1);
|
||||
msleep(200);
|
||||
outb(0xF, ctrl + 1);
|
||||
msleep(100);
|
||||
outb(0xE, ctrl + 1);
|
||||
msleep(100);
|
||||
outb(0xC, ctrl + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Socket Dual IO: this enables irq's for second port
|
||||
*/
|
||||
static void quirk_config_socket(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
|
||||
if (info->multi) {
|
||||
link->conf.Present |= PRESENT_EXT_STATUS;
|
||||
link->conf.ExtStatus = ESR_REQ_ATTN_ENA;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct serial_quirk quirks[] = {
|
||||
{
|
||||
.manfid = 0x0160,
|
||||
.prodid = 0x0104,
|
||||
.multi = -1,
|
||||
.setup = quirk_setup_brainboxes_0104,
|
||||
}, {
|
||||
.manfid = MANFID_IBM,
|
||||
.prodid = ~0,
|
||||
.multi = -1,
|
||||
.post = quirk_post_ibm,
|
||||
}, {
|
||||
.manfid = MANFID_INTEL,
|
||||
.prodid = PRODID_INTEL_DUAL_RS232,
|
||||
.multi = 2,
|
||||
}, {
|
||||
.manfid = MANFID_NATINST,
|
||||
.prodid = PRODID_NATINST_QUAD_RS232,
|
||||
.multi = 4,
|
||||
}, {
|
||||
.manfid = MANFID_NOKIA,
|
||||
.prodid = ~0,
|
||||
.multi = -1,
|
||||
.config = quirk_config_nokia,
|
||||
}, {
|
||||
.manfid = MANFID_OMEGA,
|
||||
.prodid = PRODID_OMEGA_QSP_100,
|
||||
.multi = 4,
|
||||
}, {
|
||||
.manfid = MANFID_OXSEMI,
|
||||
.prodid = ~0,
|
||||
.multi = -1,
|
||||
.wakeup = quirk_wakeup_oxsemi,
|
||||
}, {
|
||||
.manfid = MANFID_POSSIO,
|
||||
.prodid = PRODID_POSSIO_GCC,
|
||||
.multi = -1,
|
||||
.wakeup = quirk_wakeup_possio_gcc,
|
||||
}, {
|
||||
.manfid = MANFID_QUATECH,
|
||||
.prodid = PRODID_QUATECH_DUAL_RS232,
|
||||
.multi = 2,
|
||||
}, {
|
||||
.manfid = MANFID_QUATECH,
|
||||
.prodid = PRODID_QUATECH_DUAL_RS232_D1,
|
||||
.multi = 2,
|
||||
}, {
|
||||
.manfid = MANFID_QUATECH,
|
||||
.prodid = PRODID_QUATECH_DUAL_RS232_G,
|
||||
.multi = 2,
|
||||
}, {
|
||||
.manfid = MANFID_QUATECH,
|
||||
.prodid = PRODID_QUATECH_QUAD_RS232,
|
||||
.multi = 4,
|
||||
}, {
|
||||
.manfid = MANFID_SOCKET,
|
||||
.prodid = PRODID_SOCKET_DUAL_RS232,
|
||||
.multi = 2,
|
||||
.config = quirk_config_socket,
|
||||
}, {
|
||||
.manfid = MANFID_SOCKET,
|
||||
.prodid = ~0,
|
||||
.multi = -1,
|
||||
.config = quirk_config_socket,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static int serial_config(struct pcmcia_device * link);
|
||||
|
||||
|
||||
/*======================================================================
|
||||
|
||||
After a card is removed, serial_remove() will unregister
|
||||
the serial device(s), and release the PCMCIA configuration.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
static void serial_remove(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
int i;
|
||||
|
||||
DEBUG(0, "serial_release(0x%p)\n", link);
|
||||
|
||||
/*
|
||||
* Recheck to see if the device is still configured.
|
||||
*/
|
||||
for (i = 0; i < info->ndev; i++)
|
||||
serial8250_unregister_port(info->line[i]);
|
||||
|
||||
info->p_dev->dev_node = NULL;
|
||||
|
||||
if (!info->slave)
|
||||
pcmcia_disable_device(link);
|
||||
}
|
||||
|
||||
static int serial_suspend(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < info->ndev; i++)
|
||||
serial8250_suspend_port(info->line[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_resume(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < info->ndev; i++)
|
||||
serial8250_resume_port(info->line[i]);
|
||||
|
||||
if (info->quirk && info->quirk->wakeup)
|
||||
info->quirk->wakeup(link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*======================================================================
|
||||
|
||||
serial_attach() creates an "instance" of the driver, allocating
|
||||
local data structures for one device. The device is registered
|
||||
with Card Services.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
static int serial_probe(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info;
|
||||
|
||||
DEBUG(0, "serial_attach()\n");
|
||||
|
||||
/* Create new serial device */
|
||||
info = kzalloc(sizeof (*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
info->p_dev = link;
|
||||
link->priv = info;
|
||||
|
||||
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
|
||||
link->io.NumPorts1 = 8;
|
||||
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
|
||||
link->irq.IRQInfo1 = IRQ_LEVEL_ID;
|
||||
link->conf.Attributes = CONF_ENABLE_IRQ;
|
||||
if (do_sound) {
|
||||
link->conf.Attributes |= CONF_ENABLE_SPKR;
|
||||
link->conf.Status = CCSR_AUDIO_ENA;
|
||||
}
|
||||
link->conf.IntType = INT_MEMORY_AND_IO;
|
||||
|
||||
return serial_config(link);
|
||||
}
|
||||
|
||||
/*======================================================================
|
||||
|
||||
This deletes a driver "instance". The device is de-registered
|
||||
with Card Services. If it has been released, all local data
|
||||
structures are freed. Otherwise, the structures will be freed
|
||||
when the device is released.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
static void serial_detach(struct pcmcia_device *link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
|
||||
DEBUG(0, "serial_detach(0x%p)\n", link);
|
||||
|
||||
/*
|
||||
* Ensure any outstanding scheduled tasks are completed.
|
||||
*/
|
||||
flush_scheduled_work();
|
||||
|
||||
/*
|
||||
* Ensure that the ports have been released.
|
||||
*/
|
||||
serial_remove(link);
|
||||
|
||||
/* free bits */
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
|
||||
kio_addr_t iobase, int irq)
|
||||
{
|
||||
struct uart_port port;
|
||||
int line;
|
||||
|
||||
memset(&port, 0, sizeof (struct uart_port));
|
||||
port.iobase = iobase;
|
||||
port.irq = irq;
|
||||
port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
|
||||
port.uartclk = 1843200;
|
||||
port.dev = &handle_to_dev(handle);
|
||||
if (buggy_uart)
|
||||
port.flags |= UPF_BUGGY_UART;
|
||||
|
||||
if (info->quirk && info->quirk->setup)
|
||||
info->quirk->setup(handle, &port);
|
||||
|
||||
line = serial8250_register_port(&port);
|
||||
if (line < 0) {
|
||||
printk(KERN_NOTICE "serial_cs: serial8250_register_port() at "
|
||||
"0x%04lx, irq %d failed\n", (u_long)iobase, irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info->line[info->ndev] = line;
|
||||
sprintf(info->node[info->ndev].dev_name, "ttyS%d", line);
|
||||
info->node[info->ndev].major = TTY_MAJOR;
|
||||
info->node[info->ndev].minor = 0x40 + line;
|
||||
if (info->ndev > 0)
|
||||
info->node[info->ndev - 1].next = &info->node[info->ndev];
|
||||
info->ndev++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
static int
|
||||
first_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse)
|
||||
{
|
||||
int i;
|
||||
i = pcmcia_get_first_tuple(handle, tuple);
|
||||
if (i != CS_SUCCESS)
|
||||
return CS_NO_MORE_ITEMS;
|
||||
i = pcmcia_get_tuple_data(handle, tuple);
|
||||
if (i != CS_SUCCESS)
|
||||
return i;
|
||||
return pcmcia_parse_tuple(handle, tuple, parse);
|
||||
}
|
||||
|
||||
static int
|
||||
next_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse)
|
||||
{
|
||||
int i;
|
||||
i = pcmcia_get_next_tuple(handle, tuple);
|
||||
if (i != CS_SUCCESS)
|
||||
return CS_NO_MORE_ITEMS;
|
||||
i = pcmcia_get_tuple_data(handle, tuple);
|
||||
if (i != CS_SUCCESS)
|
||||
return i;
|
||||
return pcmcia_parse_tuple(handle, tuple, parse);
|
||||
}
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
static int simple_config(struct pcmcia_device *link)
|
||||
{
|
||||
static const kio_addr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
|
||||
static const int size_table[2] = { 8, 16 };
|
||||
struct serial_info *info = link->priv;
|
||||
struct serial_cfg_mem *cfg_mem;
|
||||
tuple_t *tuple;
|
||||
u_char *buf;
|
||||
cisparse_t *parse;
|
||||
cistpl_cftable_entry_t *cf;
|
||||
config_info_t config;
|
||||
int i, j, try;
|
||||
int s;
|
||||
|
||||
cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL);
|
||||
if (!cfg_mem)
|
||||
return -1;
|
||||
|
||||
tuple = &cfg_mem->tuple;
|
||||
parse = &cfg_mem->parse;
|
||||
cf = &parse->cftable_entry;
|
||||
buf = cfg_mem->buf;
|
||||
|
||||
/* If the card is already configured, look up the port and irq */
|
||||
i = pcmcia_get_configuration_info(link, &config);
|
||||
if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) {
|
||||
kio_addr_t port = 0;
|
||||
if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) {
|
||||
port = config.BasePort2;
|
||||
info->slave = 1;
|
||||
} else if ((info->manfid == MANFID_OSITECH) &&
|
||||
(config.NumPorts1 == 0x40)) {
|
||||
port = config.BasePort1 + 0x28;
|
||||
info->slave = 1;
|
||||
}
|
||||
if (info->slave) {
|
||||
kfree(cfg_mem);
|
||||
return setup_serial(link, info, port, config.AssignedIRQ);
|
||||
}
|
||||
}
|
||||
|
||||
/* First pass: look for a config entry that looks normal. */
|
||||
tuple->TupleData = (cisdata_t *) buf;
|
||||
tuple->TupleOffset = 0;
|
||||
tuple->TupleDataMax = 255;
|
||||
tuple->Attributes = 0;
|
||||
tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
|
||||
/* Two tries: without IO aliases, then with aliases */
|
||||
for (s = 0; s < 2; s++) {
|
||||
for (try = 0; try < 2; try++) {
|
||||
i = first_tuple(link, tuple, parse);
|
||||
while (i != CS_NO_MORE_ITEMS) {
|
||||
if (i != CS_SUCCESS)
|
||||
goto next_entry;
|
||||
if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
|
||||
link->conf.Vpp =
|
||||
cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
|
||||
if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[s]) &&
|
||||
(cf->io.win[0].base != 0)) {
|
||||
link->conf.ConfigIndex = cf->index;
|
||||
link->io.BasePort1 = cf->io.win[0].base;
|
||||
link->io.IOAddrLines = (try == 0) ?
|
||||
16 : cf->io.flags & CISTPL_IO_LINES_MASK;
|
||||
i = pcmcia_request_io(link, &link->io);
|
||||
if (i == CS_SUCCESS)
|
||||
goto found_port;
|
||||
}
|
||||
next_entry:
|
||||
i = next_tuple(link, tuple, parse);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Second pass: try to find an entry that isn't picky about
|
||||
its base address, then try to grab any standard serial port
|
||||
address, and finally try to get any free port. */
|
||||
i = first_tuple(link, tuple, parse);
|
||||
while (i != CS_NO_MORE_ITEMS) {
|
||||
if ((i == CS_SUCCESS) && (cf->io.nwin > 0) &&
|
||||
((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
|
||||
link->conf.ConfigIndex = cf->index;
|
||||
for (j = 0; j < 5; j++) {
|
||||
link->io.BasePort1 = base[j];
|
||||
link->io.IOAddrLines = base[j] ? 16 : 3;
|
||||
i = pcmcia_request_io(link, &link->io);
|
||||
if (i == CS_SUCCESS)
|
||||
goto found_port;
|
||||
}
|
||||
}
|
||||
i = next_tuple(link, tuple, parse);
|
||||
}
|
||||
|
||||
found_port:
|
||||
if (i != CS_SUCCESS) {
|
||||
printk(KERN_NOTICE
|
||||
"serial_cs: no usable port range found, giving up\n");
|
||||
cs_error(link, RequestIO, i);
|
||||
kfree(cfg_mem);
|
||||
return -1;
|
||||
}
|
||||
|
||||
i = pcmcia_request_irq(link, &link->irq);
|
||||
if (i != CS_SUCCESS) {
|
||||
cs_error(link, RequestIRQ, i);
|
||||
link->irq.AssignedIRQ = 0;
|
||||
}
|
||||
if (info->multi && (info->manfid == MANFID_3COM))
|
||||
link->conf.ConfigIndex &= ~(0x08);
|
||||
|
||||
/*
|
||||
* Apply any configuration quirks.
|
||||
*/
|
||||
if (info->quirk && info->quirk->config)
|
||||
info->quirk->config(link);
|
||||
|
||||
i = pcmcia_request_configuration(link, &link->conf);
|
||||
if (i != CS_SUCCESS) {
|
||||
cs_error(link, RequestConfiguration, i);
|
||||
kfree(cfg_mem);
|
||||
return -1;
|
||||
}
|
||||
kfree(cfg_mem);
|
||||
return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ);
|
||||
}
|
||||
|
||||
static int multi_config(struct pcmcia_device * link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
struct serial_cfg_mem *cfg_mem;
|
||||
tuple_t *tuple;
|
||||
u_char *buf;
|
||||
cisparse_t *parse;
|
||||
cistpl_cftable_entry_t *cf;
|
||||
int i, rc, base2 = 0;
|
||||
|
||||
cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL);
|
||||
if (!cfg_mem)
|
||||
return -1;
|
||||
tuple = &cfg_mem->tuple;
|
||||
parse = &cfg_mem->parse;
|
||||
cf = &parse->cftable_entry;
|
||||
buf = cfg_mem->buf;
|
||||
|
||||
tuple->TupleData = (cisdata_t *) buf;
|
||||
tuple->TupleOffset = 0;
|
||||
tuple->TupleDataMax = 255;
|
||||
tuple->Attributes = 0;
|
||||
tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
|
||||
|
||||
/* First, look for a generic full-sized window */
|
||||
link->io.NumPorts1 = info->multi * 8;
|
||||
i = first_tuple(link, tuple, parse);
|
||||
while (i != CS_NO_MORE_ITEMS) {
|
||||
/* The quad port cards have bad CIS's, so just look for a
|
||||
window larger than 8 ports and assume it will be right */
|
||||
if ((i == CS_SUCCESS) && (cf->io.nwin == 1) &&
|
||||
(cf->io.win[0].len > 8)) {
|
||||
link->conf.ConfigIndex = cf->index;
|
||||
link->io.BasePort1 = cf->io.win[0].base;
|
||||
link->io.IOAddrLines =
|
||||
cf->io.flags & CISTPL_IO_LINES_MASK;
|
||||
i = pcmcia_request_io(link, &link->io);
|
||||
base2 = link->io.BasePort1 + 8;
|
||||
if (i == CS_SUCCESS)
|
||||
break;
|
||||
}
|
||||
i = next_tuple(link, tuple, parse);
|
||||
}
|
||||
|
||||
/* If that didn't work, look for two windows */
|
||||
if (i != CS_SUCCESS) {
|
||||
link->io.NumPorts1 = link->io.NumPorts2 = 8;
|
||||
info->multi = 2;
|
||||
i = first_tuple(link, tuple, parse);
|
||||
while (i != CS_NO_MORE_ITEMS) {
|
||||
if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) {
|
||||
link->conf.ConfigIndex = cf->index;
|
||||
link->io.BasePort1 = cf->io.win[0].base;
|
||||
link->io.BasePort2 = cf->io.win[1].base;
|
||||
link->io.IOAddrLines =
|
||||
cf->io.flags & CISTPL_IO_LINES_MASK;
|
||||
i = pcmcia_request_io(link, &link->io);
|
||||
base2 = link->io.BasePort2;
|
||||
if (i == CS_SUCCESS)
|
||||
break;
|
||||
}
|
||||
i = next_tuple(link, tuple, parse);
|
||||
}
|
||||
}
|
||||
|
||||
if (i != CS_SUCCESS) {
|
||||
cs_error(link, RequestIO, i);
|
||||
rc = -1;
|
||||
goto free_cfg_mem;
|
||||
}
|
||||
|
||||
i = pcmcia_request_irq(link, &link->irq);
|
||||
if (i != CS_SUCCESS) {
|
||||
printk(KERN_NOTICE
|
||||
"serial_cs: no usable port range found, giving up\n");
|
||||
cs_error(link, RequestIRQ, i);
|
||||
link->irq.AssignedIRQ = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply any configuration quirks.
|
||||
*/
|
||||
if (info->quirk && info->quirk->config)
|
||||
info->quirk->config(link);
|
||||
|
||||
i = pcmcia_request_configuration(link, &link->conf);
|
||||
if (i != CS_SUCCESS) {
|
||||
cs_error(link, RequestConfiguration, i);
|
||||
rc = -1;
|
||||
goto free_cfg_mem;
|
||||
}
|
||||
|
||||
/* The Oxford Semiconductor OXCF950 cards are in fact single-port:
|
||||
* 8 registers are for the UART, the others are extra registers.
|
||||
* Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too.
|
||||
*/
|
||||
if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO &&
|
||||
info->prodid == PRODID_POSSIO_GCC)) {
|
||||
int err;
|
||||
|
||||
if (cf->index == 1 || cf->index == 3) {
|
||||
err = setup_serial(link, info, base2,
|
||||
link->irq.AssignedIRQ);
|
||||
base2 = link->io.BasePort1;
|
||||
} else {
|
||||
err = setup_serial(link, info, link->io.BasePort1,
|
||||
link->irq.AssignedIRQ);
|
||||
}
|
||||
info->c950ctrl = base2;
|
||||
|
||||
/*
|
||||
* FIXME: We really should wake up the port prior to
|
||||
* handing it over to the serial layer.
|
||||
*/
|
||||
if (info->quirk && info->quirk->wakeup)
|
||||
info->quirk->wakeup(link);
|
||||
|
||||
rc = 0;
|
||||
goto free_cfg_mem;
|
||||
}
|
||||
|
||||
setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ);
|
||||
for (i = 0; i < info->multi - 1; i++)
|
||||
setup_serial(link, info, base2 + (8 * i),
|
||||
link->irq.AssignedIRQ);
|
||||
rc = 0;
|
||||
free_cfg_mem:
|
||||
kfree(cfg_mem);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*======================================================================
|
||||
|
||||
serial_config() is scheduled to run after a CARD_INSERTION event
|
||||
is received, to configure the PCMCIA socket, and to make the
|
||||
serial device available to the system.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
static int serial_config(struct pcmcia_device * link)
|
||||
{
|
||||
struct serial_info *info = link->priv;
|
||||
struct serial_cfg_mem *cfg_mem;
|
||||
tuple_t *tuple;
|
||||
u_char *buf;
|
||||
cisparse_t *parse;
|
||||
cistpl_cftable_entry_t *cf;
|
||||
int i;
|
||||
|
||||
DEBUG(0, "serial_config(0x%p)\n", link);
|
||||
|
||||
cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL);
|
||||
if (!cfg_mem)
|
||||
goto failed;
|
||||
|
||||
tuple = &cfg_mem->tuple;
|
||||
parse = &cfg_mem->parse;
|
||||
cf = &parse->cftable_entry;
|
||||
buf = cfg_mem->buf;
|
||||
|
||||
tuple->TupleData = (cisdata_t *) buf;
|
||||
tuple->TupleOffset = 0;
|
||||
tuple->TupleDataMax = 255;
|
||||
tuple->Attributes = 0;
|
||||
|
||||
/* Is this a compliant multifunction card? */
|
||||
tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
|
||||
tuple->Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
|
||||
info->multi = (first_tuple(link, tuple, parse) == CS_SUCCESS);
|
||||
|
||||
/* Is this a multiport card? */
|
||||
tuple->DesiredTuple = CISTPL_MANFID;
|
||||
info->manfid = link->manf_id;
|
||||
info->prodid = link->card_id;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(quirks); i++)
|
||||
if ((quirks[i].manfid == ~0 ||
|
||||
quirks[i].manfid == info->manfid) &&
|
||||
(quirks[i].prodid == ~0 ||
|
||||
quirks[i].prodid == info->prodid)) {
|
||||
info->quirk = &quirks[i];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Another check for dual-serial cards: look for either serial or
|
||||
multifunction cards that ask for appropriate IO port ranges */
|
||||
tuple->DesiredTuple = CISTPL_FUNCID;
|
||||
if ((info->multi == 0) &&
|
||||
(link->has_func_id) &&
|
||||
((link->func_id == CISTPL_FUNCID_MULTI) ||
|
||||
(link->func_id == CISTPL_FUNCID_SERIAL))) {
|
||||
tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
|
||||
if (first_tuple(link, tuple, parse) == CS_SUCCESS) {
|
||||
if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0))
|
||||
info->multi = cf->io.win[0].len >> 3;
|
||||
if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) &&
|
||||
(cf->io.win[1].len == 8))
|
||||
info->multi = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply any multi-port quirk.
|
||||
*/
|
||||
if (info->quirk && info->quirk->multi != -1)
|
||||
info->multi = info->quirk->multi;
|
||||
|
||||
if (info->multi > 1)
|
||||
multi_config(link);
|
||||
else
|
||||
simple_config(link);
|
||||
|
||||
if (info->ndev == 0)
|
||||
goto failed;
|
||||
|
||||
/*
|
||||
* Apply any post-init quirk. FIXME: This should really happen
|
||||
* before we register the port, since it might already be in use.
|
||||
*/
|
||||
if (info->quirk && info->quirk->post)
|
||||
if (info->quirk->post(link))
|
||||
goto failed;
|
||||
|
||||
link->dev_node = &info->node[0];
|
||||
kfree(cfg_mem);
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
serial_remove(link);
|
||||
kfree(cfg_mem);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct pcmcia_device_id serial_ids[] = {
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0140, 0x000a),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0x3341),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0xc0ab),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a),
|
||||
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM33", 0x2e3ee845, 0x80609023),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed),
|
||||
PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf),
|
||||
PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070),
|
||||
PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562),
|
||||
PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0104, 0x0070),
|
||||
PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x016c, 0x0020),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID123(1, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052),
|
||||
PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae),
|
||||
PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef),
|
||||
PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef),
|
||||
PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef),
|
||||
PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0),
|
||||
PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e),
|
||||
PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a),
|
||||
PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02),
|
||||
PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa),
|
||||
PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 28800 FAX/DATA MODEM", 0xa3a3062c, 0x8cbd7c76),
|
||||
PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95),
|
||||
PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed),
|
||||
PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65),
|
||||
PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6),
|
||||
PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400+", 0x816cc815, 0x412729fb),
|
||||
PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f),
|
||||
PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f),
|
||||
PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383),
|
||||
PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e),
|
||||
PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a),
|
||||
PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab),
|
||||
PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f),
|
||||
PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d),
|
||||
PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "PCMLM28.cis"),
|
||||
PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "PCMLM28.cis"),
|
||||
PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "PCMLM28.cis"),
|
||||
PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "PCMLM28.cis"),
|
||||
PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "PCMLM28.cis"),
|
||||
PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "DP83903.cis"),
|
||||
PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "DP83903.cis"),
|
||||
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "3CCFEM556.cis"),
|
||||
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "DP83903.cis"),
|
||||
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "3CXEM556.cis"),
|
||||
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "3CXEM556.cis"),
|
||||
PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0x0710, "SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */
|
||||
PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */
|
||||
PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */
|
||||
PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "MT5634ZLX.cis"),
|
||||
PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "COMpad4.cis"),
|
||||
PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "COMpad2.cis"),
|
||||
PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "RS-COM-2P.cis"),
|
||||
PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "GLOBETROTTER.cis"),
|
||||
PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100 1.00.",0x19ca78af,0xf964f42b),
|
||||
PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100",0x19ca78af,0x71d98e83),
|
||||
PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232 1.00.",0x19ca78af,0x69fb7490),
|
||||
PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232",0x19ca78af,0xb6bc0235),
|
||||
PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232",0x63f2e0bd,0xb9e175d3),
|
||||
PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232-5",0x63f2e0bd,0xfce33442),
|
||||
PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232",0x3beb8cf2,0x171e7190),
|
||||
PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232-5",0x3beb8cf2,0x20da4262),
|
||||
PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF428",0x3beb8cf2,0xea5dd57d),
|
||||
PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF500",0x3beb8cf2,0xd77255fa),
|
||||
PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: IC232",0x3beb8cf2,0x6a709903),
|
||||
PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: SL232",0x3beb8cf2,0x18430676),
|
||||
PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: XL232",0x3beb8cf2,0x6f933767),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(2,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(3,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4),
|
||||
/* too generic */
|
||||
/* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */
|
||||
/* PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0160, 0x0002), */
|
||||
PCMCIA_DEVICE_FUNC_ID(2),
|
||||
PCMCIA_DEVICE_NULL,
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, serial_ids);
|
||||
|
||||
static struct pcmcia_driver serial_cs_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.drv = {
|
||||
.name = "serial_cs",
|
||||
},
|
||||
.probe = serial_probe,
|
||||
.remove = serial_detach,
|
||||
.id_table = serial_ids,
|
||||
.suspend = serial_suspend,
|
||||
.resume = serial_resume,
|
||||
};
|
||||
|
||||
static int __init init_serial_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&serial_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_serial_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&serial_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_serial_cs);
|
||||
module_exit(exit_serial_cs);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
681
drivers/serial/serial_lh7a40x.c
Normal file
681
drivers/serial/serial_lh7a40x.c
Normal file
@@ -0,0 +1,681 @@
|
||||
/* drivers/serial/serial_lh7a40x.c
|
||||
*
|
||||
* Copyright (C) 2004 Coastal Environmental Systems
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Driver for Sharp LH7A40X embedded serial ports
|
||||
*
|
||||
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
|
||||
* Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* This driver supports the embedded UARTs of the Sharp LH7A40X series
|
||||
* CPUs. While similar to the 16550 and other UART chips, there is
|
||||
* nothing close to register compatibility. Moreover, some of the
|
||||
* modem control lines are not available, either in the chip or they
|
||||
* are lacking in the board-level implementation.
|
||||
*
|
||||
* - Use of SIRDIS
|
||||
* For simplicity, we disable the IR functions of any UART whenever
|
||||
* we enable it.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#define DEV_MAJOR 204
|
||||
#define DEV_MINOR 16
|
||||
#define DEV_NR 3
|
||||
|
||||
#define ISR_LOOP_LIMIT 256
|
||||
|
||||
#define UR(p,o) _UR ((p)->membase, o)
|
||||
#define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o))))
|
||||
#define BIT_CLR(p,o,m) UR(p,o) = UR(p,o) & (~(unsigned int)m)
|
||||
#define BIT_SET(p,o,m) UR(p,o) = UR(p,o) | ( (unsigned int)m)
|
||||
|
||||
#define UART_REG_SIZE 32
|
||||
|
||||
#define UART_R_DATA (0x00)
|
||||
#define UART_R_FCON (0x04)
|
||||
#define UART_R_BRCON (0x08)
|
||||
#define UART_R_CON (0x0c)
|
||||
#define UART_R_STATUS (0x10)
|
||||
#define UART_R_RAWISR (0x14)
|
||||
#define UART_R_INTEN (0x18)
|
||||
#define UART_R_ISR (0x1c)
|
||||
|
||||
#define UARTEN (0x01) /* UART enable */
|
||||
#define SIRDIS (0x02) /* Serial IR disable (UART1 only) */
|
||||
|
||||
#define RxEmpty (0x10)
|
||||
#define TxEmpty (0x80)
|
||||
#define TxFull (0x20)
|
||||
#define nRxRdy RxEmpty
|
||||
#define nTxRdy TxFull
|
||||
#define TxBusy (0x08)
|
||||
|
||||
#define RxBreak (0x0800)
|
||||
#define RxOverrunError (0x0400)
|
||||
#define RxParityError (0x0200)
|
||||
#define RxFramingError (0x0100)
|
||||
#define RxError (RxBreak | RxOverrunError | RxParityError | RxFramingError)
|
||||
|
||||
#define DCD (0x04)
|
||||
#define DSR (0x02)
|
||||
#define CTS (0x01)
|
||||
|
||||
#define RxInt (0x01)
|
||||
#define TxInt (0x02)
|
||||
#define ModemInt (0x04)
|
||||
#define RxTimeoutInt (0x08)
|
||||
|
||||
#define MSEOI (0x10)
|
||||
|
||||
#define WLEN_8 (0x60)
|
||||
#define WLEN_7 (0x40)
|
||||
#define WLEN_6 (0x20)
|
||||
#define WLEN_5 (0x00)
|
||||
#define WLEN (0x60) /* Mask for all word-length bits */
|
||||
#define STP2 (0x08)
|
||||
#define PEN (0x02) /* Parity Enable */
|
||||
#define EPS (0x04) /* Even Parity Set */
|
||||
#define FEN (0x10) /* FIFO Enable */
|
||||
#define BRK (0x01) /* Send Break */
|
||||
|
||||
|
||||
struct uart_port_lh7a40x {
|
||||
struct uart_port port;
|
||||
unsigned int statusPrev; /* Most recently read modem status */
|
||||
};
|
||||
|
||||
static void lh7a40xuart_stop_tx (struct uart_port* port)
|
||||
{
|
||||
BIT_CLR (port, UART_R_INTEN, TxInt);
|
||||
}
|
||||
|
||||
static void lh7a40xuart_start_tx (struct uart_port* port)
|
||||
{
|
||||
BIT_SET (port, UART_R_INTEN, TxInt);
|
||||
|
||||
/* *** FIXME: do I need to check for startup of the
|
||||
transmitter? The old driver did, but AMBA
|
||||
doesn't . */
|
||||
}
|
||||
|
||||
static void lh7a40xuart_stop_rx (struct uart_port* port)
|
||||
{
|
||||
BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
|
||||
}
|
||||
|
||||
static void lh7a40xuart_enable_ms (struct uart_port* port)
|
||||
{
|
||||
BIT_SET (port, UART_R_INTEN, ModemInt);
|
||||
}
|
||||
|
||||
static void lh7a40xuart_rx_chars (struct uart_port* port)
|
||||
{
|
||||
struct tty_struct* tty = port->info->tty;
|
||||
int cbRxMax = 256; /* (Gross) limit on receive */
|
||||
unsigned int data; /* Received data and status */
|
||||
unsigned int flag;
|
||||
|
||||
while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) {
|
||||
data = UR (port, UART_R_DATA);
|
||||
flag = TTY_NORMAL;
|
||||
++port->icount.rx;
|
||||
|
||||
if (unlikely(data & RxError)) {
|
||||
if (data & RxBreak) {
|
||||
data &= ~(RxFramingError | RxParityError);
|
||||
++port->icount.brk;
|
||||
if (uart_handle_break (port))
|
||||
continue;
|
||||
}
|
||||
else if (data & RxParityError)
|
||||
++port->icount.parity;
|
||||
else if (data & RxFramingError)
|
||||
++port->icount.frame;
|
||||
if (data & RxOverrunError)
|
||||
++port->icount.overrun;
|
||||
|
||||
/* Mask by termios, leave Rx'd byte */
|
||||
data &= port->read_status_mask | 0xff;
|
||||
|
||||
if (data & RxBreak)
|
||||
flag = TTY_BREAK;
|
||||
else if (data & RxParityError)
|
||||
flag = TTY_PARITY;
|
||||
else if (data & RxFramingError)
|
||||
flag = TTY_FRAME;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char (port, (unsigned char) data))
|
||||
continue;
|
||||
|
||||
uart_insert_char(port, data, RxOverrunError, data, flag);
|
||||
}
|
||||
tty_flip_buffer_push (tty);
|
||||
return;
|
||||
}
|
||||
|
||||
static void lh7a40xuart_tx_chars (struct uart_port* port)
|
||||
{
|
||||
struct circ_buf* xmit = &port->info->xmit;
|
||||
int cbTxMax = port->fifosize;
|
||||
|
||||
if (port->x_char) {
|
||||
UR (port, UART_R_DATA) = port->x_char;
|
||||
++port->icount.tx;
|
||||
port->x_char = 0;
|
||||
return;
|
||||
}
|
||||
if (uart_circ_empty (xmit) || uart_tx_stopped (port)) {
|
||||
lh7a40xuart_stop_tx (port);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unlike the AMBA UART, the lh7a40x UART does not guarantee
|
||||
that at least half of the FIFO is empty. Instead, we check
|
||||
status for every character. Using the AMBA method causes
|
||||
the transmitter to drop characters. */
|
||||
|
||||
do {
|
||||
UR (port, UART_R_DATA) = xmit->buf[xmit->tail];
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
++port->icount.tx;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
} while (!(UR (port, UART_R_STATUS) & nTxRdy)
|
||||
&& cbTxMax--);
|
||||
|
||||
if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup (port);
|
||||
|
||||
if (uart_circ_empty (xmit))
|
||||
lh7a40xuart_stop_tx (port);
|
||||
}
|
||||
|
||||
static void lh7a40xuart_modem_status (struct uart_port* port)
|
||||
{
|
||||
unsigned int status = UR (port, UART_R_STATUS);
|
||||
unsigned int delta
|
||||
= status ^ ((struct uart_port_lh7a40x*) port)->statusPrev;
|
||||
|
||||
BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */
|
||||
|
||||
if (!delta) /* Only happens if we missed 2 transitions */
|
||||
return;
|
||||
|
||||
((struct uart_port_lh7a40x*) port)->statusPrev = status;
|
||||
|
||||
if (delta & DCD)
|
||||
uart_handle_dcd_change (port, status & DCD);
|
||||
|
||||
if (delta & DSR)
|
||||
++port->icount.dsr;
|
||||
|
||||
if (delta & CTS)
|
||||
uart_handle_cts_change (port, status & CTS);
|
||||
|
||||
wake_up_interruptible (&port->info->delta_msr_wait);
|
||||
}
|
||||
|
||||
static irqreturn_t lh7a40xuart_int (int irq, void* dev_id)
|
||||
{
|
||||
struct uart_port* port = dev_id;
|
||||
unsigned int cLoopLimit = ISR_LOOP_LIMIT;
|
||||
unsigned int isr = UR (port, UART_R_ISR);
|
||||
|
||||
|
||||
do {
|
||||
if (isr & (RxInt | RxTimeoutInt))
|
||||
lh7a40xuart_rx_chars(port);
|
||||
if (isr & ModemInt)
|
||||
lh7a40xuart_modem_status (port);
|
||||
if (isr & TxInt)
|
||||
lh7a40xuart_tx_chars (port);
|
||||
|
||||
if (--cLoopLimit == 0)
|
||||
break;
|
||||
|
||||
isr = UR (port, UART_R_ISR);
|
||||
} while (isr & (RxInt | TxInt | RxTimeoutInt));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
|
||||
{
|
||||
return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
unsigned int status = UR (port, UART_R_STATUS);
|
||||
|
||||
if (status & DCD)
|
||||
result |= TIOCM_CAR;
|
||||
if (status & DSR)
|
||||
result |= TIOCM_DSR;
|
||||
if (status & CTS)
|
||||
result |= TIOCM_CTS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl)
|
||||
{
|
||||
/* None of the ports supports DTR. UART1 supports RTS through GPIO. */
|
||||
/* Note, kernel appears to be setting DTR and RTS on console. */
|
||||
|
||||
/* *** FIXME: this deserves more work. There's some work in
|
||||
tracing all of the IO pins. */
|
||||
#if 0
|
||||
if( port->mapbase == UART1_PHYS) {
|
||||
gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS);
|
||||
|
||||
if (mctrl & TIOCM_RTS)
|
||||
gpio->pbdr &= ~GPIOB_UART1_RTS;
|
||||
else
|
||||
gpio->pbdr |= GPIOB_UART1_RTS;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
if (break_state == -1)
|
||||
BIT_SET (port, UART_R_FCON, BRK); /* Assert break */
|
||||
else
|
||||
BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int lh7a40xuart_startup (struct uart_port* port)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = request_irq (port->irq, lh7a40xuart_int, 0,
|
||||
"serial_lh7a40x", port);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Initial modem control-line settings */
|
||||
((struct uart_port_lh7a40x*) port)->statusPrev
|
||||
= UR (port, UART_R_STATUS);
|
||||
|
||||
/* There is presently no configuration option to enable IR.
|
||||
Thus, we always disable it. */
|
||||
|
||||
BIT_SET (port, UART_R_CON, UARTEN | SIRDIS);
|
||||
BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lh7a40xuart_shutdown (struct uart_port* port)
|
||||
{
|
||||
free_irq (port->irq, port);
|
||||
BIT_CLR (port, UART_R_FCON, BRK | FEN);
|
||||
BIT_CLR (port, UART_R_CON, UARTEN);
|
||||
}
|
||||
|
||||
static void lh7a40xuart_set_termios (struct uart_port* port,
|
||||
struct ktermios* termios,
|
||||
struct ktermios* old)
|
||||
{
|
||||
unsigned int con;
|
||||
unsigned int inten;
|
||||
unsigned int fcon;
|
||||
unsigned long flags;
|
||||
unsigned int baud;
|
||||
unsigned int quot;
|
||||
|
||||
baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16);
|
||||
quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
fcon = WLEN_5;
|
||||
break;
|
||||
case CS6:
|
||||
fcon = WLEN_6;
|
||||
break;
|
||||
case CS7:
|
||||
fcon = WLEN_7;
|
||||
break;
|
||||
case CS8:
|
||||
default:
|
||||
fcon = WLEN_8;
|
||||
break;
|
||||
}
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
fcon |= STP2;
|
||||
if (termios->c_cflag & PARENB) {
|
||||
fcon |= PEN;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
fcon |= EPS;
|
||||
}
|
||||
if (port->fifosize > 1)
|
||||
fcon |= FEN;
|
||||
|
||||
spin_lock_irqsave (&port->lock, flags);
|
||||
|
||||
uart_update_timeout (port, termios->c_cflag, baud);
|
||||
|
||||
port->read_status_mask = RxOverrunError;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= RxFramingError | RxParityError;
|
||||
if (termios->c_iflag & (BRKINT | PARMRK))
|
||||
port->read_status_mask |= RxBreak;
|
||||
|
||||
/* Figure mask for status we ignore */
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= RxFramingError | RxParityError;
|
||||
if (termios->c_iflag & IGNBRK) {
|
||||
port->ignore_status_mask |= RxBreak;
|
||||
/* Ignore overrun when ignorning parity */
|
||||
/* *** FIXME: is this in the right place? */
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= RxOverrunError;
|
||||
}
|
||||
|
||||
/* Ignore all receive errors when receive disabled */
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |= RxError;
|
||||
|
||||
con = UR (port, UART_R_CON);
|
||||
inten = (UR (port, UART_R_INTEN) & ~ModemInt);
|
||||
|
||||
if (UART_ENABLE_MS (port, termios->c_cflag))
|
||||
inten |= ModemInt;
|
||||
|
||||
BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */
|
||||
UR (port, UART_R_INTEN) = 0; /* Disable interrupts */
|
||||
UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */
|
||||
UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */
|
||||
UR (port, UART_R_INTEN) = inten; /* Enable interrupts */
|
||||
UR (port, UART_R_CON) = con; /* Restore UART mode */
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char* lh7a40xuart_type (struct uart_port* port)
|
||||
{
|
||||
return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
|
||||
}
|
||||
|
||||
static void lh7a40xuart_release_port (struct uart_port* port)
|
||||
{
|
||||
release_mem_region (port->mapbase, UART_REG_SIZE);
|
||||
}
|
||||
|
||||
static int lh7a40xuart_request_port (struct uart_port* port)
|
||||
{
|
||||
return request_mem_region (port->mapbase, UART_REG_SIZE,
|
||||
"serial_lh7a40x") != NULL
|
||||
? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
static void lh7a40xuart_config_port (struct uart_port* port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE) {
|
||||
port->type = PORT_LH7A40X;
|
||||
lh7a40xuart_request_port (port);
|
||||
}
|
||||
}
|
||||
|
||||
static int lh7a40xuart_verify_port (struct uart_port* port,
|
||||
struct serial_struct* ser)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X)
|
||||
ret = -EINVAL;
|
||||
if (ser->irq < 0 || ser->irq >= NR_IRQS)
|
||||
ret = -EINVAL;
|
||||
if (ser->baud_base < 9600) /* *** FIXME: is this true? */
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct uart_ops lh7a40x_uart_ops = {
|
||||
.tx_empty = lh7a40xuart_tx_empty,
|
||||
.set_mctrl = lh7a40xuart_set_mctrl,
|
||||
.get_mctrl = lh7a40xuart_get_mctrl,
|
||||
.stop_tx = lh7a40xuart_stop_tx,
|
||||
.start_tx = lh7a40xuart_start_tx,
|
||||
.stop_rx = lh7a40xuart_stop_rx,
|
||||
.enable_ms = lh7a40xuart_enable_ms,
|
||||
.break_ctl = lh7a40xuart_break_ctl,
|
||||
.startup = lh7a40xuart_startup,
|
||||
.shutdown = lh7a40xuart_shutdown,
|
||||
.set_termios = lh7a40xuart_set_termios,
|
||||
.type = lh7a40xuart_type,
|
||||
.release_port = lh7a40xuart_release_port,
|
||||
.request_port = lh7a40xuart_request_port,
|
||||
.config_port = lh7a40xuart_config_port,
|
||||
.verify_port = lh7a40xuart_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = {
|
||||
{
|
||||
.port = {
|
||||
.membase = (void*) io_p2v (UART1_PHYS),
|
||||
.mapbase = UART1_PHYS,
|
||||
.iotype = UPIO_MEM,
|
||||
.irq = IRQ_UART1INTR,
|
||||
.uartclk = 14745600/2,
|
||||
.fifosize = 16,
|
||||
.ops = &lh7a40x_uart_ops,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.line = 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
.port = {
|
||||
.membase = (void*) io_p2v (UART2_PHYS),
|
||||
.mapbase = UART2_PHYS,
|
||||
.iotype = UPIO_MEM,
|
||||
.irq = IRQ_UART2INTR,
|
||||
.uartclk = 14745600/2,
|
||||
.fifosize = 16,
|
||||
.ops = &lh7a40x_uart_ops,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.line = 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
.port = {
|
||||
.membase = (void*) io_p2v (UART3_PHYS),
|
||||
.mapbase = UART3_PHYS,
|
||||
.iotype = UPIO_MEM,
|
||||
.irq = IRQ_UART3INTR,
|
||||
.uartclk = 14745600/2,
|
||||
.fifosize = 16,
|
||||
.ops = &lh7a40x_uart_ops,
|
||||
.flags = UPF_BOOT_AUTOCONF,
|
||||
.line = 2,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#ifndef CONFIG_SERIAL_LH7A40X_CONSOLE
|
||||
# define LH7A40X_CONSOLE NULL
|
||||
#else
|
||||
# define LH7A40X_CONSOLE &lh7a40x_console
|
||||
|
||||
static void lh7a40xuart_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
while (UR(port, UART_R_STATUS) & nTxRdy)
|
||||
;
|
||||
UR(port, UART_R_DATA) = ch;
|
||||
}
|
||||
|
||||
static void lh7a40xuart_console_write (struct console* co,
|
||||
const char* s,
|
||||
unsigned int count)
|
||||
{
|
||||
struct uart_port* port = &lh7a40x_ports[co->index].port;
|
||||
unsigned int con = UR (port, UART_R_CON);
|
||||
unsigned int inten = UR (port, UART_R_INTEN);
|
||||
|
||||
|
||||
UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */
|
||||
BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */
|
||||
|
||||
uart_console_write(port, s, count, lh7a40xuart_console_putchar);
|
||||
|
||||
/* Wait until all characters are sent */
|
||||
while (UR (port, UART_R_STATUS) & TxBusy)
|
||||
;
|
||||
|
||||
/* Restore control and interrupt mask */
|
||||
UR (port, UART_R_CON) = con;
|
||||
UR (port, UART_R_INTEN) = inten;
|
||||
}
|
||||
|
||||
static void __init lh7a40xuart_console_get_options (struct uart_port* port,
|
||||
int* baud,
|
||||
int* parity,
|
||||
int* bits)
|
||||
{
|
||||
if (UR (port, UART_R_CON) & UARTEN) {
|
||||
unsigned int fcon = UR (port, UART_R_FCON);
|
||||
unsigned int quot = UR (port, UART_R_BRCON) + 1;
|
||||
|
||||
switch (fcon & (PEN | EPS)) {
|
||||
default: *parity = 'n'; break;
|
||||
case PEN: *parity = 'o'; break;
|
||||
case PEN | EPS: *parity = 'e'; break;
|
||||
}
|
||||
|
||||
switch (fcon & WLEN) {
|
||||
default:
|
||||
case WLEN_8: *bits = 8; break;
|
||||
case WLEN_7: *bits = 7; break;
|
||||
case WLEN_6: *bits = 6; break;
|
||||
case WLEN_5: *bits = 5; break;
|
||||
}
|
||||
|
||||
*baud = port->uartclk/(16*quot);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init lh7a40xuart_console_setup (struct console* co, char* options)
|
||||
{
|
||||
struct uart_port* port;
|
||||
int baud = 38400;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (co->index >= DEV_NR) /* Bounds check on device number */
|
||||
co->index = 0;
|
||||
port = &lh7a40x_ports[co->index].port;
|
||||
|
||||
if (options)
|
||||
uart_parse_options (options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
lh7a40xuart_console_get_options (port, &baud, &parity, &bits);
|
||||
|
||||
return uart_set_options (port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver lh7a40x_reg;
|
||||
static struct console lh7a40x_console = {
|
||||
.name = "ttyAM",
|
||||
.write = lh7a40xuart_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = lh7a40xuart_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &lh7a40x_reg,
|
||||
};
|
||||
|
||||
static int __init lh7a40xuart_console_init(void)
|
||||
{
|
||||
register_console (&lh7a40x_console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_initcall (lh7a40xuart_console_init);
|
||||
|
||||
#endif
|
||||
|
||||
static struct uart_driver lh7a40x_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "ttyAM",
|
||||
.dev_name = "ttyAM",
|
||||
.major = DEV_MAJOR,
|
||||
.minor = DEV_MINOR,
|
||||
.nr = DEV_NR,
|
||||
.cons = LH7A40X_CONSOLE,
|
||||
};
|
||||
|
||||
static int __init lh7a40xuart_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk (KERN_INFO "serial: LH7A40X serial driver\n");
|
||||
|
||||
ret = uart_register_driver (&lh7a40x_reg);
|
||||
|
||||
if (ret == 0) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DEV_NR; i++) {
|
||||
/* UART3, when used, requires GPIO pin reallocation */
|
||||
if (lh7a40x_ports[i].port.mapbase == UART3_PHYS)
|
||||
GPIO_PINMUX |= 1<<3;
|
||||
uart_add_one_port (&lh7a40x_reg,
|
||||
&lh7a40x_ports[i].port);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit lh7a40xuart_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DEV_NR; i++)
|
||||
uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port);
|
||||
|
||||
uart_unregister_driver (&lh7a40x_reg);
|
||||
}
|
||||
|
||||
module_init (lh7a40xuart_init);
|
||||
module_exit (lh7a40xuart_exit);
|
||||
|
||||
MODULE_AUTHOR ("Marc Singer");
|
||||
MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
|
||||
MODULE_LICENSE ("GPL");
|
||||
1295
drivers/serial/serial_txx9.c
Normal file
1295
drivers/serial/serial_txx9.c
Normal file
File diff suppressed because it is too large
Load Diff
1477
drivers/serial/sh-sci.c
Normal file
1477
drivers/serial/sh-sci.c
Normal file
File diff suppressed because it is too large
Load Diff
647
drivers/serial/sh-sci.h
Normal file
647
drivers/serial/sh-sci.h
Normal file
@@ -0,0 +1,647 @@
|
||||
/* $Id: sh-sci.h,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*
|
||||
* linux/drivers/serial/sh-sci.h
|
||||
*
|
||||
* SuperH on-chip serial module support. (SCI with no FIFO / with FIFO)
|
||||
* Copyright (C) 1999, 2000 Niibe Yutaka
|
||||
* Copyright (C) 2000 Greg Banks
|
||||
* Copyright (C) 2002, 2003 Paul Mundt
|
||||
* Modified to support multiple serial ports. Stuart Menefy (May 2000).
|
||||
* Modified to support SH7300(SH-Mobile) SCIF. Takashi Kusuda (Jun 2003).
|
||||
* Modified to support H8/300 Series Yoshinori Sato (Feb 2004).
|
||||
*/
|
||||
#include <linux/serial_core.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#if defined(__H8300H__) || defined(__H8300S__)
|
||||
#include <asm/gpio.h>
|
||||
#if defined(CONFIG_H83007) || defined(CONFIG_H83068)
|
||||
#include <asm/regs306x.h>
|
||||
#endif
|
||||
#if defined(CONFIG_H8S2678)
|
||||
#include <asm/regs267x.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7708)
|
||||
# define SCSPTR 0xffffff7c /* 8 bit */
|
||||
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
|
||||
# define SCI_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7709) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7706)
|
||||
# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */
|
||||
# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */
|
||||
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
|
||||
# define SCI_AND_SCIF
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
|
||||
# define SCIF0 0xA4400000
|
||||
# define SCIF2 0xA4410000
|
||||
# define SCSMR_Ir 0xA44A0000
|
||||
# define IRDA_SCIF SCIF0
|
||||
# define SCPCR 0xA4000116
|
||||
# define SCPDR 0xA4000136
|
||||
|
||||
/* Set the clock source,
|
||||
* SCIF2 (0xA4410000) -> External clock, SCK pin used as clock input
|
||||
* SCIF0 (0xA4400000) -> Internal clock, SCK pin as serial clock output
|
||||
*/
|
||||
# define SCSCR_INIT(port) (port->mapbase == SCIF2) ? 0xF3 : 0xF0
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_SH_RTS7751R2D)
|
||||
# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751)
|
||||
# define SCSPTR1 0xffe0001c /* 8 bit SCI */
|
||||
# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \
|
||||
0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \
|
||||
0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ )
|
||||
# define SCI_AND_SCIF
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
|
||||
# define SCSPTR0 0xfe600024 /* 16 bit SCIF */
|
||||
# define SCSPTR1 0xfe610024 /* 16 bit SCIF */
|
||||
# define SCSPTR2 0xfe620024 /* 16 bit SCIF */
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7300)
|
||||
# define SCPCR 0xA4050116 /* 16 bit SCIF */
|
||||
# define SCPDR 0xA4050136 /* 16 bit SCIF */
|
||||
# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7710)
|
||||
# define SCSPTR0 0xA4400000 /* 16 bit SCIF */
|
||||
# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH73180)
|
||||
# define SCPDR 0xA4050138 /* 16 bit SCIF */
|
||||
# define SCSPTR2 SCPDR
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7343)
|
||||
# define SCSPTR0 0xffe00010 /* 16 bit SCIF */
|
||||
# define SCSPTR1 0xffe10010 /* 16 bit SCIF */
|
||||
# define SCSPTR2 0xffe20010 /* 16 bit SCIF */
|
||||
# define SCSPTR3 0xffe30010 /* 16 bit SCIF */
|
||||
# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
|
||||
# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */
|
||||
# define SCSPTR0 SCPDR0
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
# define PORT_PSCR 0xA405011E
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
|
||||
# define SCSPTR2 0xffe80020 /* 16 bit SCIF */
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
|
||||
# define SCSPTR1 0xffe00020 /* 16 bit SCIF */
|
||||
# define SCSPTR2 0xffe80020 /* 16 bit SCIF */
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
|
||||
# include <asm/hardware.h>
|
||||
# define SCIF_BASE_ADDR 0x01030000
|
||||
# define SCIF_ADDR_SH5 PHYS_PERIPHERAL_BLOCK+SCIF_BASE_ADDR
|
||||
# define SCIF_PTR2_OFFS 0x0000020
|
||||
# define SCIF_LSR2_OFFS 0x0000024
|
||||
# define SCSPTR2 ((port->mapbase)+SCIF_PTR2_OFFS) /* 16 bit SCIF */
|
||||
# define SCLSR2 ((port->mapbase)+SCIF_LSR2_OFFS) /* 16 bit SCIF */
|
||||
# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,
|
||||
TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_H83007) || defined(CONFIG_H83068)
|
||||
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
|
||||
# define SCI_ONLY
|
||||
# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port)
|
||||
#elif defined(CONFIG_H8S2678)
|
||||
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
|
||||
# define SCI_ONLY
|
||||
# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port)
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7770)
|
||||
# define SCSPTR0 0xff923020 /* 16 bit SCIF */
|
||||
# define SCSPTR1 0xff924020 /* 16 bit SCIF */
|
||||
# define SCSPTR2 0xff925020 /* 16 bit SCIF */
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x3c /* TIE=0,RIE=0,TE=1,RE=1,REIE=1,cke=2 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
|
||||
# define SCSPTR0 0xffe00024 /* 16 bit SCIF */
|
||||
# define SCSPTR1 0xffe10024 /* 16 bit SCIF */
|
||||
# define SCIF_ORER 0x0001 /* Overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7206)
|
||||
# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */
|
||||
# define SCSPTR1 0xfffe8820 /* 16 bit SCIF */
|
||||
# define SCSPTR2 0xfffe9020 /* 16 bit SCIF */
|
||||
# define SCSPTR3 0xfffe9820 /* 16 bit SCIF */
|
||||
# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
|
||||
# define SCSPTR0 0xf8400020 /* 16 bit SCIF */
|
||||
# define SCSPTR1 0xf8410020 /* 16 bit SCIF */
|
||||
# define SCSPTR2 0xf8420020 /* 16 bit SCIF */
|
||||
# define SCIF_ORER 0x0001 /* overrun error bit */
|
||||
# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
|
||||
# define SCIF_ONLY
|
||||
#else
|
||||
# error CPU subtype not defined
|
||||
#endif
|
||||
|
||||
/* SCSCR */
|
||||
#define SCI_CTRL_FLAGS_TIE 0x80 /* all */
|
||||
#define SCI_CTRL_FLAGS_RIE 0x40 /* all */
|
||||
#define SCI_CTRL_FLAGS_TE 0x20 /* all */
|
||||
#define SCI_CTRL_FLAGS_RE 0x10 /* all */
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH7780)
|
||||
#define SCI_CTRL_FLAGS_REIE 0x08 /* 7750 SCIF */
|
||||
#else
|
||||
#define SCI_CTRL_FLAGS_REIE 0
|
||||
#endif
|
||||
/* SCI_CTRL_FLAGS_MPIE 0x08 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
/* SCI_CTRL_FLAGS_TEIE 0x04 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
/* SCI_CTRL_FLAGS_CKE1 0x02 * all */
|
||||
/* SCI_CTRL_FLAGS_CKE0 0x01 * 7707 SCI/SCIF, 7708 SCI, 7709 SCI/SCIF, 7750 SCI */
|
||||
|
||||
/* SCxSR SCI */
|
||||
#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
|
||||
|
||||
#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER)
|
||||
|
||||
/* SCxSR SCIF */
|
||||
#define SCIF_ER 0x0080 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
|
||||
#define SCIF_TEND 0x0040 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
|
||||
#define SCIF_TDFE 0x0020 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
|
||||
#define SCIF_BRK 0x0010 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
|
||||
#define SCIF_FER 0x0008 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
|
||||
#define SCIF_PER 0x0004 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
|
||||
#define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
|
||||
#define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
|
||||
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
|
||||
#define SCIF_ORER 0x0200
|
||||
#define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER)
|
||||
#define SCIF_RFDC_MASK 0x007f
|
||||
#define SCIF_TXROOM_MAX 64
|
||||
#else
|
||||
#define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK)
|
||||
#define SCIF_RFDC_MASK 0x001f
|
||||
#define SCIF_TXROOM_MAX 16
|
||||
#endif
|
||||
|
||||
#if defined(SCI_ONLY)
|
||||
# define SCxSR_TEND(port) SCI_TEND
|
||||
# define SCxSR_ERRORS(port) SCI_ERRORS
|
||||
# define SCxSR_RDxF(port) SCI_RDRF
|
||||
# define SCxSR_TDxE(port) SCI_TDRE
|
||||
# define SCxSR_ORER(port) SCI_ORER
|
||||
# define SCxSR_FER(port) SCI_FER
|
||||
# define SCxSR_PER(port) SCI_PER
|
||||
# define SCxSR_BRK(port) 0x00
|
||||
# define SCxSR_RDxF_CLEAR(port) 0xbc
|
||||
# define SCxSR_ERROR_CLEAR(port) 0xc4
|
||||
# define SCxSR_TDxE_CLEAR(port) 0x78
|
||||
# define SCxSR_BREAK_CLEAR(port) 0xc4
|
||||
#elif defined(SCIF_ONLY)
|
||||
# define SCxSR_TEND(port) SCIF_TEND
|
||||
# define SCxSR_ERRORS(port) SCIF_ERRORS
|
||||
# define SCxSR_RDxF(port) SCIF_RDF
|
||||
# define SCxSR_TDxE(port) SCIF_TDFE
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
|
||||
# define SCxSR_ORER(port) SCIF_ORER
|
||||
#else
|
||||
# define SCxSR_ORER(port) 0x0000
|
||||
#endif
|
||||
# define SCxSR_FER(port) SCIF_FER
|
||||
# define SCxSR_PER(port) SCIF_PER
|
||||
# define SCxSR_BRK(port) SCIF_BRK
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
|
||||
# define SCxSR_RDxF_CLEAR(port) (sci_in(port,SCxSR)&0xfffc)
|
||||
# define SCxSR_ERROR_CLEAR(port) (sci_in(port,SCxSR)&0xfd73)
|
||||
# define SCxSR_TDxE_CLEAR(port) (sci_in(port,SCxSR)&0xffdf)
|
||||
# define SCxSR_BREAK_CLEAR(port) (sci_in(port,SCxSR)&0xffe3)
|
||||
#else
|
||||
/* SH7705 can also use this, clearing is same between 7705 and 7709 and 7300 */
|
||||
# define SCxSR_RDxF_CLEAR(port) 0x00fc
|
||||
# define SCxSR_ERROR_CLEAR(port) 0x0073
|
||||
# define SCxSR_TDxE_CLEAR(port) 0x00df
|
||||
# define SCxSR_BREAK_CLEAR(port) 0x00e3
|
||||
#endif
|
||||
#else
|
||||
# define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND)
|
||||
# define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS)
|
||||
# define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF)
|
||||
# define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE)
|
||||
# define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : 0x0000)
|
||||
# define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER)
|
||||
# define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER)
|
||||
# define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK)
|
||||
# define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc)
|
||||
# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073)
|
||||
# define SCxSR_TDxE_CLEAR(port) (((port)->type == PORT_SCI) ? 0x78 : 0x00df)
|
||||
# define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3)
|
||||
#endif
|
||||
|
||||
/* SCFCR */
|
||||
#define SCFCR_RFRST 0x0002
|
||||
#define SCFCR_TFRST 0x0004
|
||||
#define SCFCR_TCRST 0x4000
|
||||
#define SCFCR_MCE 0x0008
|
||||
|
||||
#define SCI_MAJOR 204
|
||||
#define SCI_MINOR_START 8
|
||||
|
||||
/* Generic serial flags */
|
||||
#define SCI_RX_THROTTLE 0x0000001
|
||||
|
||||
#define SCI_MAGIC 0xbabeface
|
||||
|
||||
/*
|
||||
* Events are used to schedule things to happen at timer-interrupt
|
||||
* time, instead of at rs interrupt time.
|
||||
*/
|
||||
#define SCI_EVENT_WRITE_WAKEUP 0
|
||||
|
||||
#define SCI_IN(size, offset) \
|
||||
unsigned int addr = port->mapbase + (offset); \
|
||||
if ((size) == 8) { \
|
||||
return ctrl_inb(addr); \
|
||||
} else { \
|
||||
return ctrl_inw(addr); \
|
||||
}
|
||||
#define SCI_OUT(size, offset, value) \
|
||||
unsigned int addr = port->mapbase + (offset); \
|
||||
if ((size) == 8) { \
|
||||
ctrl_outb(value, addr); \
|
||||
} else { \
|
||||
ctrl_outw(value, addr); \
|
||||
}
|
||||
|
||||
#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\
|
||||
static inline unsigned int sci_##name##_in(struct uart_port *port) \
|
||||
{ \
|
||||
if (port->type == PORT_SCI) { \
|
||||
SCI_IN(sci_size, sci_offset) \
|
||||
} else { \
|
||||
SCI_IN(scif_size, scif_offset); \
|
||||
} \
|
||||
} \
|
||||
static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
|
||||
{ \
|
||||
if (port->type == PORT_SCI) { \
|
||||
SCI_OUT(sci_size, sci_offset, value) \
|
||||
} else { \
|
||||
SCI_OUT(scif_size, scif_offset, value); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CPU_SCIF_FNS(name, scif_offset, scif_size) \
|
||||
static inline unsigned int sci_##name##_in(struct uart_port *port) \
|
||||
{ \
|
||||
SCI_IN(scif_size, scif_offset); \
|
||||
} \
|
||||
static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
|
||||
{ \
|
||||
SCI_OUT(scif_size, scif_offset, value); \
|
||||
}
|
||||
|
||||
#define CPU_SCI_FNS(name, sci_offset, sci_size) \
|
||||
static inline unsigned int sci_##name##_in(struct uart_port* port) \
|
||||
{ \
|
||||
SCI_IN(sci_size, sci_offset); \
|
||||
} \
|
||||
static inline void sci_##name##_out(struct uart_port* port, unsigned int value) \
|
||||
{ \
|
||||
SCI_OUT(sci_size, sci_offset, value); \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_SH3
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7300) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7705) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7710)
|
||||
#define SCIF_FNS(name, scif_offset, scif_size) \
|
||||
CPU_SCIF_FNS(name, scif_offset, scif_size)
|
||||
#else
|
||||
#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \
|
||||
sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \
|
||||
h8_sci_offset, h8_sci_size) \
|
||||
CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size)
|
||||
#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \
|
||||
CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size)
|
||||
#endif
|
||||
#elif defined(__H8300H__) || defined(__H8300S__)
|
||||
#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \
|
||||
sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \
|
||||
h8_sci_offset, h8_sci_size) \
|
||||
CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size)
|
||||
#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size)
|
||||
#else
|
||||
#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \
|
||||
sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \
|
||||
h8_sci_offset, h8_sci_size) \
|
||||
CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size)
|
||||
#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \
|
||||
CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7300) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7705) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7710)
|
||||
SCIF_FNS(SCSMR, 0x00, 16)
|
||||
SCIF_FNS(SCBRR, 0x04, 8)
|
||||
SCIF_FNS(SCSCR, 0x08, 16)
|
||||
SCIF_FNS(SCTDSR, 0x0c, 8)
|
||||
SCIF_FNS(SCFER, 0x10, 16)
|
||||
SCIF_FNS(SCxSR, 0x14, 16)
|
||||
SCIF_FNS(SCFCR, 0x18, 16)
|
||||
SCIF_FNS(SCFDR, 0x1c, 16)
|
||||
SCIF_FNS(SCxTDR, 0x20, 8)
|
||||
SCIF_FNS(SCxRDR, 0x24, 8)
|
||||
SCIF_FNS(SCLSR, 0x24, 16)
|
||||
#else
|
||||
/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/
|
||||
/* name off sz off sz off sz off sz off sz*/
|
||||
SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8)
|
||||
SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8)
|
||||
SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8)
|
||||
SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8)
|
||||
SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8)
|
||||
SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8)
|
||||
SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16)
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7760) || defined(CONFIG_CPU_SUBTYPE_SH7780)
|
||||
SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
|
||||
SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16)
|
||||
SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16)
|
||||
SCIF_FNS(SCSPTR, 0, 0, 0x24, 16)
|
||||
SCIF_FNS(SCLSR, 0, 0, 0x28, 16)
|
||||
#else
|
||||
SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
|
||||
SCIF_FNS(SCSPTR, 0, 0, 0x20, 16)
|
||||
SCIF_FNS(SCLSR, 0, 0, 0x24, 16)
|
||||
#endif
|
||||
#endif
|
||||
#define sci_in(port, reg) sci_##reg##_in(port)
|
||||
#define sci_out(port, reg, value) sci_##reg##_out(port, value)
|
||||
|
||||
/* H8/300 series SCI pins assignment */
|
||||
#if defined(__H8300H__) || defined(__H8300S__)
|
||||
static const struct __attribute__((packed)) {
|
||||
int port; /* GPIO port no */
|
||||
unsigned short rx,tx; /* GPIO bit no */
|
||||
} h8300_sci_pins[] = {
|
||||
#if defined(CONFIG_H83007) || defined(CONFIG_H83068)
|
||||
{ /* SCI0 */
|
||||
.port = H8300_GPIO_P9,
|
||||
.rx = H8300_GPIO_B2,
|
||||
.tx = H8300_GPIO_B0,
|
||||
},
|
||||
{ /* SCI1 */
|
||||
.port = H8300_GPIO_P9,
|
||||
.rx = H8300_GPIO_B3,
|
||||
.tx = H8300_GPIO_B1,
|
||||
},
|
||||
{ /* SCI2 */
|
||||
.port = H8300_GPIO_PB,
|
||||
.rx = H8300_GPIO_B7,
|
||||
.tx = H8300_GPIO_B6,
|
||||
}
|
||||
#elif defined(CONFIG_H8S2678)
|
||||
{ /* SCI0 */
|
||||
.port = H8300_GPIO_P3,
|
||||
.rx = H8300_GPIO_B2,
|
||||
.tx = H8300_GPIO_B0,
|
||||
},
|
||||
{ /* SCI1 */
|
||||
.port = H8300_GPIO_P3,
|
||||
.rx = H8300_GPIO_B3,
|
||||
.tx = H8300_GPIO_B1,
|
||||
},
|
||||
{ /* SCI2 */
|
||||
.port = H8300_GPIO_P5,
|
||||
.rx = H8300_GPIO_B1,
|
||||
.tx = H8300_GPIO_B0,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7708)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xfffffe80)
|
||||
return ctrl_inb(SCSPTR)&0x01 ? 1 : 0; /* SCI */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7709) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7706)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xfffffe80)
|
||||
return ctrl_inb(SCPDR)&0x01 ? 1 : 0; /* SCI */
|
||||
if (port->mapbase == 0xa4000150)
|
||||
return ctrl_inb(SCPDR)&0x10 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xa4000140)
|
||||
return ctrl_inb(SCPDR)&0x04 ? 1 : 0; /* IRDA */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == SCIF0)
|
||||
return ctrl_inb(SCPDR)&0x04 ? 1 : 0; /* IRDA */
|
||||
if (port->mapbase == SCIF2)
|
||||
return ctrl_inb(SCPDR)&0x10 ? 1 : 0; /* SCIF */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7710)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == SCSPTR0)
|
||||
return ctrl_inw(SCSPTR0 + 0x10) & 0x01 ? 1 : 0;
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH7751) || \
|
||||
defined(CONFIG_CPU_SUBTYPE_SH4_202)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
#ifndef SCIF_ONLY
|
||||
if (port->mapbase == 0xffe00000)
|
||||
return ctrl_inb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */
|
||||
#endif
|
||||
#ifndef SCI_ONLY
|
||||
if (port->mapbase == 0xffe80000)
|
||||
return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xfe600000)
|
||||
return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xfe610000)
|
||||
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xfe620000)
|
||||
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7300)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xa4430000)
|
||||
return ctrl_inb(SCPDR)&0x01 ? 1 : 0; /* SCIF0 */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH73180)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
return ctrl_inb(SCPDR)&0x01 ? 1 : 0; /* SCIF0 */
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7343)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xffe00000)
|
||||
return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xffe10000)
|
||||
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xffe20000)
|
||||
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xffe30000)
|
||||
return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xffe00000)
|
||||
return ctrl_inb(SCPDR0) & 0x0001 ? 1 : 0; /* SCIF0 */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xffe00000)
|
||||
return ctrl_inw(SCSPTR1)&0x0001 ? 1 : 0; /* SCIF */
|
||||
else
|
||||
return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */
|
||||
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
return sci_in(port, SCSPTR)&0x0001 ? 1 : 0; /* SCIF */
|
||||
}
|
||||
#elif defined(__H8300H__) || defined(__H8300S__)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
int ch = (port->mapbase - SMR0) >> 3;
|
||||
return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7770)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xff923000)
|
||||
return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xff924000)
|
||||
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xff925000)
|
||||
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xffe00000)
|
||||
return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xffe10000)
|
||||
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7206)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xfffe8000)
|
||||
return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xfffe8800)
|
||||
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xfffe9000)
|
||||
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xfffe9800)
|
||||
return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
return 1;
|
||||
}
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
|
||||
static inline int sci_rxd_in(struct uart_port *port)
|
||||
{
|
||||
if (port->mapbase == 0xf8400000)
|
||||
return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xf8410000)
|
||||
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
if (port->mapbase == 0xf8420000)
|
||||
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Values for the BitRate Register (SCBRR)
|
||||
*
|
||||
* The values are actually divisors for a frequency which can
|
||||
* be internal to the SH3 (14.7456MHz) or derived from an external
|
||||
* clock source. This driver assumes the internal clock is used;
|
||||
* to support using an external clock source, config options or
|
||||
* possibly command-line options would need to be added.
|
||||
*
|
||||
* Also, to support speeds below 2400 (why?) the lower 2 bits of
|
||||
* the SCSMR register would also need to be set to non-zero values.
|
||||
*
|
||||
* -- Greg Banks 27Feb2000
|
||||
*
|
||||
* Answer: The SCBRR register is only eight bits, and the value in
|
||||
* it gets larger with lower baud rates. At around 2400 (depending on
|
||||
* the peripherial module clock) you run out of bits. However the
|
||||
* lower two bits of SCSMR allow the module clock to be divided down,
|
||||
* scaling the value which is needed in SCBRR.
|
||||
*
|
||||
* -- Stuart Menefy - 23 May 2000
|
||||
*
|
||||
* I meant, why would anyone bother with bitrates below 2400.
|
||||
*
|
||||
* -- Greg Banks - 7Jul2000
|
||||
*
|
||||
* You "speedist"! How will I use my 110bps ASR-33 teletype with paper
|
||||
* tape reader as a console!
|
||||
*
|
||||
* -- Mitch Davis - 15 Jul 2000
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7780)
|
||||
#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1)
|
||||
#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
|
||||
#define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1)
|
||||
#elif defined(__H8300H__) || defined(__H8300S__)
|
||||
#define SCBRR_VALUE(bps) (((CONFIG_CPU_CLOCK*1000/32)/bps)-1)
|
||||
#elif defined(CONFIG_SUPERH64)
|
||||
#define SCBRR_VALUE(bps) ((current_cpu_data.module_clock+16*bps)/(32*bps)-1)
|
||||
#else /* Generic SH */
|
||||
#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1)
|
||||
#endif
|
||||
1085
drivers/serial/sn_console.c
Normal file
1085
drivers/serial/sn_console.c
Normal file
File diff suppressed because it is too large
Load Diff
247
drivers/serial/suncore.c
Normal file
247
drivers/serial/suncore.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/* suncore.c
|
||||
*
|
||||
* Common SUN serial routines. Based entirely
|
||||
* upon drivers/sbus/char/sunserial.c which is:
|
||||
*
|
||||
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
|
||||
*
|
||||
* Adaptation to new UART layer is:
|
||||
*
|
||||
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/oplib.h>
|
||||
|
||||
#include "suncore.h"
|
||||
|
||||
int sunserial_current_minor = 64;
|
||||
|
||||
EXPORT_SYMBOL(sunserial_current_minor);
|
||||
|
||||
void
|
||||
sunserial_console_termios(struct console *con)
|
||||
{
|
||||
char mode[16], buf[16], *s;
|
||||
char mode_prop[] = "ttyX-mode";
|
||||
char cd_prop[] = "ttyX-ignore-cd";
|
||||
char dtr_prop[] = "ttyX-rts-dtr-off";
|
||||
char *ssp_console_modes_prop = "ssp-console-modes";
|
||||
int baud, bits, stop, cflag;
|
||||
char parity;
|
||||
int carrier = 0;
|
||||
int rtsdtr = 1;
|
||||
int topnd, nd;
|
||||
|
||||
if (!serial_console)
|
||||
return;
|
||||
|
||||
switch (serial_console) {
|
||||
case PROMDEV_OTTYA:
|
||||
mode_prop[3] = 'a';
|
||||
cd_prop[3] = 'a';
|
||||
dtr_prop[3] = 'a';
|
||||
break;
|
||||
|
||||
case PROMDEV_OTTYB:
|
||||
mode_prop[3] = 'b';
|
||||
cd_prop[3] = 'b';
|
||||
dtr_prop[3] = 'b';
|
||||
break;
|
||||
|
||||
case PROMDEV_ORSC:
|
||||
|
||||
nd = prom_pathtoinode("rsc");
|
||||
if (!nd) {
|
||||
strcpy(mode, "115200,8,n,1,-");
|
||||
goto no_options;
|
||||
}
|
||||
|
||||
if (!prom_node_has_property(nd, ssp_console_modes_prop)) {
|
||||
strcpy(mode, "115200,8,n,1,-");
|
||||
goto no_options;
|
||||
}
|
||||
|
||||
memset(mode, 0, sizeof(mode));
|
||||
prom_getstring(nd, ssp_console_modes_prop, mode, sizeof(mode));
|
||||
goto no_options;
|
||||
|
||||
default:
|
||||
strcpy(mode, "9600,8,n,1,-");
|
||||
goto no_options;
|
||||
}
|
||||
|
||||
topnd = prom_getchild(prom_root_node);
|
||||
nd = prom_searchsiblings(topnd, "options");
|
||||
if (!nd) {
|
||||
strcpy(mode, "9600,8,n,1,-");
|
||||
goto no_options;
|
||||
}
|
||||
|
||||
if (!prom_node_has_property(nd, mode_prop)) {
|
||||
strcpy(mode, "9600,8,n,1,-");
|
||||
goto no_options;
|
||||
}
|
||||
|
||||
memset(mode, 0, sizeof(mode));
|
||||
prom_getstring(nd, mode_prop, mode, sizeof(mode));
|
||||
|
||||
if (prom_node_has_property(nd, cd_prop)) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
prom_getstring(nd, cd_prop, buf, sizeof(buf));
|
||||
if (!strcmp(buf, "false"))
|
||||
carrier = 1;
|
||||
|
||||
/* XXX: this is unused below. */
|
||||
}
|
||||
|
||||
if (prom_node_has_property(nd, dtr_prop)) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
prom_getstring(nd, dtr_prop, buf, sizeof(buf));
|
||||
if (!strcmp(buf, "false"))
|
||||
rtsdtr = 0;
|
||||
|
||||
/* XXX: this is unused below. */
|
||||
}
|
||||
|
||||
no_options:
|
||||
cflag = CREAD | HUPCL | CLOCAL;
|
||||
|
||||
s = mode;
|
||||
baud = simple_strtoul(s, NULL, 0);
|
||||
s = strchr(s, ',');
|
||||
bits = simple_strtoul(++s, NULL, 0);
|
||||
s = strchr(s, ',');
|
||||
parity = *(++s);
|
||||
s = strchr(s, ',');
|
||||
stop = simple_strtoul(++s, NULL, 0);
|
||||
s = strchr(s, ',');
|
||||
/* XXX handshake is not handled here. */
|
||||
|
||||
switch (baud) {
|
||||
case 150: cflag |= B150; break;
|
||||
case 300: cflag |= B300; break;
|
||||
case 600: cflag |= B600; break;
|
||||
case 1200: cflag |= B1200; break;
|
||||
case 2400: cflag |= B2400; break;
|
||||
case 4800: cflag |= B4800; break;
|
||||
case 9600: cflag |= B9600; break;
|
||||
case 19200: cflag |= B19200; break;
|
||||
case 38400: cflag |= B38400; break;
|
||||
case 57600: cflag |= B57600; break;
|
||||
case 115200: cflag |= B115200; break;
|
||||
case 230400: cflag |= B230400; break;
|
||||
case 460800: cflag |= B460800; break;
|
||||
default: baud = 9600; cflag |= B9600; break;
|
||||
}
|
||||
|
||||
switch (bits) {
|
||||
case 5: cflag |= CS5; break;
|
||||
case 6: cflag |= CS6; break;
|
||||
case 7: cflag |= CS7; break;
|
||||
case 8: cflag |= CS8; break;
|
||||
default: cflag |= CS8; break;
|
||||
}
|
||||
|
||||
switch (parity) {
|
||||
case 'o': cflag |= (PARENB | PARODD); break;
|
||||
case 'e': cflag |= PARENB; break;
|
||||
case 'n': default: break;
|
||||
}
|
||||
|
||||
switch (stop) {
|
||||
case 2: cflag |= CSTOPB; break;
|
||||
case 1: default: break;
|
||||
}
|
||||
|
||||
con->cflag = cflag;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(sunserial_console_termios);
|
||||
|
||||
/* Sun serial MOUSE auto baud rate detection. */
|
||||
static struct mouse_baud_cflag {
|
||||
int baud;
|
||||
unsigned int cflag;
|
||||
} mouse_baud_table[] = {
|
||||
{ 1200, B1200 },
|
||||
{ 2400, B2400 },
|
||||
{ 4800, B4800 },
|
||||
{ 9600, B9600 },
|
||||
{ -1, ~0 },
|
||||
{ -1, ~0 },
|
||||
};
|
||||
|
||||
unsigned int suncore_mouse_baud_cflag_next(unsigned int cflag, int *new_baud)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; mouse_baud_table[i].baud != -1; i++)
|
||||
if (mouse_baud_table[i].cflag == (cflag & CBAUD))
|
||||
break;
|
||||
|
||||
i += 1;
|
||||
if (mouse_baud_table[i].baud == -1)
|
||||
i = 0;
|
||||
|
||||
*new_baud = mouse_baud_table[i].baud;
|
||||
return mouse_baud_table[i].cflag;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(suncore_mouse_baud_cflag_next);
|
||||
|
||||
/* Basically, when the baud rate is wrong the mouse spits out
|
||||
* breaks to us.
|
||||
*/
|
||||
int suncore_mouse_baud_detection(unsigned char ch, int is_break)
|
||||
{
|
||||
static int mouse_got_break = 0;
|
||||
static int ctr = 0;
|
||||
|
||||
if (is_break) {
|
||||
/* Let a few normal bytes go by before we jump the gun
|
||||
* and say we need to try another baud rate.
|
||||
*/
|
||||
if (mouse_got_break && ctr < 8)
|
||||
return 1;
|
||||
|
||||
/* Ok, we need to try another baud. */
|
||||
ctr = 0;
|
||||
mouse_got_break = 1;
|
||||
return 2;
|
||||
}
|
||||
if (mouse_got_break) {
|
||||
ctr++;
|
||||
if (ch == 0x87) {
|
||||
/* Correct baud rate determined. */
|
||||
mouse_got_break = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(suncore_mouse_baud_detection);
|
||||
|
||||
static int __init suncore_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit suncore_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(suncore_init);
|
||||
module_exit(suncore_exit);
|
||||
|
||||
MODULE_AUTHOR("Eddie C. Dost, David S. Miller");
|
||||
MODULE_DESCRIPTION("Sun serial common layer");
|
||||
MODULE_LICENSE("GPL");
|
||||
29
drivers/serial/suncore.h
Normal file
29
drivers/serial/suncore.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* suncore.h
|
||||
*
|
||||
* Generic SUN serial/kbd/ms layer. Based entirely
|
||||
* upon drivers/sbus/char/sunserial.h which is:
|
||||
*
|
||||
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
|
||||
*
|
||||
* Port to new UART layer is:
|
||||
*
|
||||
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
|
||||
*/
|
||||
|
||||
#ifndef _SERIAL_SUN_H
|
||||
#define _SERIAL_SUN_H
|
||||
|
||||
/* Serial keyboard defines for L1-A processing... */
|
||||
#define SUNKBD_RESET 0xff
|
||||
#define SUNKBD_L1 0x01
|
||||
#define SUNKBD_UP 0x80
|
||||
#define SUNKBD_A 0x4d
|
||||
|
||||
extern unsigned int suncore_mouse_baud_cflag_next(unsigned int, int *);
|
||||
extern int suncore_mouse_baud_detection(unsigned char, int);
|
||||
|
||||
extern int sunserial_current_minor;
|
||||
|
||||
extern void sunserial_console_termios(struct console *);
|
||||
|
||||
#endif /* !(_SERIAL_SUN_H) */
|
||||
530
drivers/serial/sunhv.c
Normal file
530
drivers/serial/sunhv.c
Normal file
@@ -0,0 +1,530 @@
|
||||
/* sunhv.c: Serial driver for SUN4V hypervisor console.
|
||||
*
|
||||
* Copyright (C) 2006 David S. Miller (davem@davemloft.net)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/hypervisor.h>
|
||||
#include <asm/spitfire.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/of_device.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#if defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
#include "suncore.h"
|
||||
|
||||
#define CON_BREAK ((long)-1)
|
||||
#define CON_HUP ((long)-2)
|
||||
|
||||
static inline long hypervisor_con_getchar(long *status)
|
||||
{
|
||||
register unsigned long func asm("%o5");
|
||||
register unsigned long arg0 asm("%o0");
|
||||
register unsigned long arg1 asm("%o1");
|
||||
|
||||
func = HV_FAST_CONS_GETCHAR;
|
||||
arg0 = 0;
|
||||
arg1 = 0;
|
||||
__asm__ __volatile__("ta %6"
|
||||
: "=&r" (func), "=&r" (arg0), "=&r" (arg1)
|
||||
: "0" (func), "1" (arg0), "2" (arg1),
|
||||
"i" (HV_FAST_TRAP));
|
||||
|
||||
*status = arg0;
|
||||
|
||||
return (long) arg1;
|
||||
}
|
||||
|
||||
static inline long hypervisor_con_putchar(long ch)
|
||||
{
|
||||
register unsigned long func asm("%o5");
|
||||
register unsigned long arg0 asm("%o0");
|
||||
|
||||
func = HV_FAST_CONS_PUTCHAR;
|
||||
arg0 = ch;
|
||||
__asm__ __volatile__("ta %4"
|
||||
: "=&r" (func), "=&r" (arg0)
|
||||
: "0" (func), "1" (arg0), "i" (HV_FAST_TRAP));
|
||||
|
||||
return (long) arg0;
|
||||
}
|
||||
|
||||
#define IGNORE_BREAK 0x1
|
||||
#define IGNORE_ALL 0x2
|
||||
|
||||
static int hung_up = 0;
|
||||
|
||||
static struct tty_struct *receive_chars(struct uart_port *port)
|
||||
{
|
||||
struct tty_struct *tty = NULL;
|
||||
int saw_console_brk = 0;
|
||||
int limit = 10000;
|
||||
|
||||
if (port->info != NULL) /* Unopened serial console */
|
||||
tty = port->info->tty;
|
||||
|
||||
while (limit-- > 0) {
|
||||
long status;
|
||||
long c = hypervisor_con_getchar(&status);
|
||||
unsigned char flag;
|
||||
|
||||
if (status == HV_EWOULDBLOCK)
|
||||
break;
|
||||
|
||||
if (c == CON_BREAK) {
|
||||
if (uart_handle_break(port))
|
||||
continue;
|
||||
saw_console_brk = 1;
|
||||
c = 0;
|
||||
}
|
||||
|
||||
if (c == CON_HUP) {
|
||||
hung_up = 1;
|
||||
uart_handle_dcd_change(port, 0);
|
||||
} else if (hung_up) {
|
||||
hung_up = 0;
|
||||
uart_handle_dcd_change(port, 1);
|
||||
}
|
||||
|
||||
if (tty == NULL) {
|
||||
uart_handle_sysrq_char(port, c);
|
||||
continue;
|
||||
}
|
||||
|
||||
flag = TTY_NORMAL;
|
||||
port->icount.rx++;
|
||||
if (c == CON_BREAK) {
|
||||
port->icount.brk++;
|
||||
if (uart_handle_break(port))
|
||||
continue;
|
||||
flag = TTY_BREAK;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char(port, c))
|
||||
continue;
|
||||
|
||||
if ((port->ignore_status_mask & IGNORE_ALL) ||
|
||||
((port->ignore_status_mask & IGNORE_BREAK) &&
|
||||
(c == CON_BREAK)))
|
||||
continue;
|
||||
|
||||
tty_insert_flip_char(tty, c, flag);
|
||||
}
|
||||
|
||||
if (saw_console_brk)
|
||||
sun_do_break();
|
||||
|
||||
return tty;
|
||||
}
|
||||
|
||||
static void transmit_chars(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit;
|
||||
|
||||
if (!port->info)
|
||||
return;
|
||||
|
||||
xmit = &port->info->xmit;
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
|
||||
return;
|
||||
|
||||
while (!uart_circ_empty(xmit)) {
|
||||
long status = hypervisor_con_putchar(xmit->buf[xmit->tail]);
|
||||
|
||||
if (status != HV_EOK)
|
||||
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);
|
||||
}
|
||||
|
||||
static irqreturn_t sunhv_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
struct tty_struct *tty;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
tty = receive_chars(port);
|
||||
transmit_chars(port);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
if (tty)
|
||||
tty_flip_buffer_push(tty);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* port->lock is not held. */
|
||||
static unsigned int sunhv_tx_empty(struct uart_port *port)
|
||||
{
|
||||
/* Transmitter is always empty for us. If the circ buffer
|
||||
* is non-empty or there is an x_char pending, our caller
|
||||
* will do the right thing and ignore what we return here.
|
||||
*/
|
||||
return TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
/* port->lock held by caller. */
|
||||
static void sunhv_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* port->lock is held by caller and interrupts are disabled. */
|
||||
static unsigned int sunhv_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
|
||||
}
|
||||
|
||||
/* port->lock held by caller. */
|
||||
static void sunhv_stop_tx(struct uart_port *port)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* port->lock held by caller. */
|
||||
static void sunhv_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
while (!uart_circ_empty(xmit)) {
|
||||
long status = hypervisor_con_putchar(xmit->buf[xmit->tail]);
|
||||
|
||||
if (status != HV_EOK)
|
||||
break;
|
||||
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* port->lock is not held. */
|
||||
static void sunhv_send_xchar(struct uart_port *port, char ch)
|
||||
{
|
||||
unsigned long flags;
|
||||
int limit = 10000;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
while (limit-- > 0) {
|
||||
long status = hypervisor_con_putchar(ch);
|
||||
if (status == HV_EOK)
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
/* port->lock held by caller. */
|
||||
static void sunhv_stop_rx(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
/* port->lock held by caller. */
|
||||
static void sunhv_enable_ms(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
/* port->lock is not held. */
|
||||
static void sunhv_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
if (break_state) {
|
||||
unsigned long flags;
|
||||
int limit = 1000000;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
while (limit-- > 0) {
|
||||
long status = hypervisor_con_putchar(CON_BREAK);
|
||||
if (status == HV_EOK)
|
||||
break;
|
||||
udelay(2);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* port->lock is not held. */
|
||||
static int sunhv_startup(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* port->lock is not held. */
|
||||
static void sunhv_shutdown(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
/* port->lock is not held. */
|
||||
static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
|
||||
unsigned int quot = uart_get_divisor(port, baud);
|
||||
unsigned int iflag, cflag;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
iflag = termios->c_iflag;
|
||||
cflag = termios->c_cflag;
|
||||
|
||||
port->ignore_status_mask = 0;
|
||||
if (iflag & IGNBRK)
|
||||
port->ignore_status_mask |= IGNORE_BREAK;
|
||||
if ((cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |= IGNORE_ALL;
|
||||
|
||||
/* XXX */
|
||||
uart_update_timeout(port, cflag,
|
||||
(port->uartclk / (16 * quot)));
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *sunhv_type(struct uart_port *port)
|
||||
{
|
||||
return "SUN4V HCONS";
|
||||
}
|
||||
|
||||
static void sunhv_release_port(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static int sunhv_request_port(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunhv_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
}
|
||||
|
||||
static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct uart_ops sunhv_pops = {
|
||||
.tx_empty = sunhv_tx_empty,
|
||||
.set_mctrl = sunhv_set_mctrl,
|
||||
.get_mctrl = sunhv_get_mctrl,
|
||||
.stop_tx = sunhv_stop_tx,
|
||||
.start_tx = sunhv_start_tx,
|
||||
.send_xchar = sunhv_send_xchar,
|
||||
.stop_rx = sunhv_stop_rx,
|
||||
.enable_ms = sunhv_enable_ms,
|
||||
.break_ctl = sunhv_break_ctl,
|
||||
.startup = sunhv_startup,
|
||||
.shutdown = sunhv_shutdown,
|
||||
.set_termios = sunhv_set_termios,
|
||||
.type = sunhv_type,
|
||||
.release_port = sunhv_release_port,
|
||||
.request_port = sunhv_request_port,
|
||||
.config_port = sunhv_config_port,
|
||||
.verify_port = sunhv_verify_port,
|
||||
};
|
||||
|
||||
static struct uart_driver sunhv_reg = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "serial",
|
||||
.dev_name = "ttyS",
|
||||
.major = TTY_MAJOR,
|
||||
};
|
||||
|
||||
static struct uart_port *sunhv_port;
|
||||
|
||||
static inline void sunhv_console_putchar(struct uart_port *port, char c)
|
||||
{
|
||||
unsigned long flags;
|
||||
int limit = 1000000;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
while (limit-- > 0) {
|
||||
long status = hypervisor_con_putchar(c);
|
||||
if (status == HV_EOK)
|
||||
break;
|
||||
udelay(2);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static void sunhv_console_write(struct console *con, const char *s, unsigned n)
|
||||
{
|
||||
struct uart_port *port = sunhv_port;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (*s == '\n')
|
||||
sunhv_console_putchar(port, '\r');
|
||||
sunhv_console_putchar(port, *s++);
|
||||
}
|
||||
}
|
||||
|
||||
static struct console sunhv_console = {
|
||||
.name = "ttyHV",
|
||||
.write = sunhv_console_write,
|
||||
.device = uart_console_device,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &sunhv_reg,
|
||||
};
|
||||
|
||||
static inline struct console *SUNHV_CONSOLE(void)
|
||||
{
|
||||
if (con_is_present())
|
||||
return NULL;
|
||||
|
||||
sunhv_console.index = 0;
|
||||
|
||||
return &sunhv_console;
|
||||
}
|
||||
|
||||
static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int err;
|
||||
|
||||
if (op->irqs[0] == 0xffffffff)
|
||||
return -ENODEV;
|
||||
|
||||
port = kzalloc(sizeof(struct uart_port), GFP_KERNEL);
|
||||
if (unlikely(!port))
|
||||
return -ENOMEM;
|
||||
|
||||
sunhv_port = port;
|
||||
|
||||
port->line = 0;
|
||||
port->ops = &sunhv_pops;
|
||||
port->type = PORT_SUNHV;
|
||||
port->uartclk = ( 29491200 / 16 ); /* arbitrary */
|
||||
|
||||
port->membase = (unsigned char __iomem *) __pa(port);
|
||||
|
||||
port->irq = op->irqs[0];
|
||||
|
||||
port->dev = &op->dev;
|
||||
|
||||
sunhv_reg.minor = sunserial_current_minor;
|
||||
sunhv_reg.nr = 1;
|
||||
|
||||
err = uart_register_driver(&sunhv_reg);
|
||||
if (err)
|
||||
goto out_free_port;
|
||||
|
||||
sunhv_reg.tty_driver->name_base = sunhv_reg.minor - 64;
|
||||
sunserial_current_minor += 1;
|
||||
|
||||
sunhv_reg.cons = SUNHV_CONSOLE();
|
||||
|
||||
err = uart_add_one_port(&sunhv_reg, port);
|
||||
if (err)
|
||||
goto out_unregister_driver;
|
||||
|
||||
err = request_irq(port->irq, sunhv_interrupt, 0, "hvcons", port);
|
||||
if (err)
|
||||
goto out_remove_port;
|
||||
|
||||
dev_set_drvdata(&op->dev, port);
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove_port:
|
||||
uart_remove_one_port(&sunhv_reg, port);
|
||||
|
||||
out_unregister_driver:
|
||||
sunserial_current_minor -= 1;
|
||||
uart_unregister_driver(&sunhv_reg);
|
||||
|
||||
out_free_port:
|
||||
kfree(port);
|
||||
sunhv_port = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit hv_remove(struct of_device *dev)
|
||||
{
|
||||
struct uart_port *port = dev_get_drvdata(&dev->dev);
|
||||
|
||||
free_irq(port->irq, port);
|
||||
|
||||
uart_remove_one_port(&sunhv_reg, port);
|
||||
|
||||
sunserial_current_minor -= 1;
|
||||
uart_unregister_driver(&sunhv_reg);
|
||||
|
||||
kfree(port);
|
||||
sunhv_port = NULL;
|
||||
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id hv_match[] = {
|
||||
{
|
||||
.name = "console",
|
||||
.compatible = "qcn",
|
||||
},
|
||||
{
|
||||
.name = "console",
|
||||
.compatible = "SUNW,sun4v-console",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hv_match);
|
||||
|
||||
static struct of_platform_driver hv_driver = {
|
||||
.name = "hv",
|
||||
.match_table = hv_match,
|
||||
.probe = hv_probe,
|
||||
.remove = __devexit_p(hv_remove),
|
||||
};
|
||||
|
||||
static int __init sunhv_init(void)
|
||||
{
|
||||
if (tlb_type != hypervisor)
|
||||
return -ENODEV;
|
||||
|
||||
return of_register_driver(&hv_driver, &of_bus_type);
|
||||
}
|
||||
|
||||
static void __exit sunhv_exit(void)
|
||||
{
|
||||
of_unregister_driver(&hv_driver);
|
||||
}
|
||||
|
||||
module_init(sunhv_init);
|
||||
module_exit(sunhv_exit);
|
||||
|
||||
MODULE_AUTHOR("David S. Miller");
|
||||
MODULE_DESCRIPTION("SUN4V Hypervisor console driver");
|
||||
MODULE_VERSION("2.0");
|
||||
MODULE_LICENSE("GPL");
|
||||
1182
drivers/serial/sunsab.c
Normal file
1182
drivers/serial/sunsab.c
Normal file
File diff suppressed because it is too large
Load Diff
322
drivers/serial/sunsab.h
Normal file
322
drivers/serial/sunsab.h
Normal file
@@ -0,0 +1,322 @@
|
||||
/* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC
|
||||
*
|
||||
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
|
||||
*/
|
||||
|
||||
#ifndef _SUNSAB_H
|
||||
#define _SUNSAB_H
|
||||
|
||||
struct sab82532_async_rd_regs {
|
||||
u8 rfifo[0x20]; /* Receive FIFO */
|
||||
u8 star; /* Status Register */
|
||||
u8 __pad1;
|
||||
u8 mode; /* Mode Register */
|
||||
u8 timr; /* Timer Register */
|
||||
u8 xon; /* XON Character */
|
||||
u8 xoff; /* XOFF Character */
|
||||
u8 tcr; /* Termination Character Register */
|
||||
u8 dafo; /* Data Format */
|
||||
u8 rfc; /* RFIFO Control Register */
|
||||
u8 __pad2;
|
||||
u8 rbcl; /* Receive Byte Count Low */
|
||||
u8 rbch; /* Receive Byte Count High */
|
||||
u8 ccr0; /* Channel Configuration Register 0 */
|
||||
u8 ccr1; /* Channel Configuration Register 1 */
|
||||
u8 ccr2; /* Channel Configuration Register 2 */
|
||||
u8 ccr3; /* Channel Configuration Register 3 */
|
||||
u8 __pad3[4];
|
||||
u8 vstr; /* Version Status Register */
|
||||
u8 __pad4[3];
|
||||
u8 gis; /* Global Interrupt Status */
|
||||
u8 ipc; /* Interrupt Port Configuration */
|
||||
u8 isr0; /* Interrupt Status 0 */
|
||||
u8 isr1; /* Interrupt Status 1 */
|
||||
u8 pvr; /* Port Value Register */
|
||||
u8 pis; /* Port Interrupt Status */
|
||||
u8 pcr; /* Port Configuration Register */
|
||||
u8 ccr4; /* Channel Configuration Register 4 */
|
||||
};
|
||||
|
||||
struct sab82532_async_wr_regs {
|
||||
u8 xfifo[0x20]; /* Transmit FIFO */
|
||||
u8 cmdr; /* Command Register */
|
||||
u8 __pad1;
|
||||
u8 mode;
|
||||
u8 timr;
|
||||
u8 xon;
|
||||
u8 xoff;
|
||||
u8 tcr;
|
||||
u8 dafo;
|
||||
u8 rfc;
|
||||
u8 __pad2;
|
||||
u8 xbcl; /* Transmit Byte Count Low */
|
||||
u8 xbch; /* Transmit Byte Count High */
|
||||
u8 ccr0;
|
||||
u8 ccr1;
|
||||
u8 ccr2;
|
||||
u8 ccr3;
|
||||
u8 tsax; /* Time-Slot Assignment Reg. Transmit */
|
||||
u8 tsar; /* Time-Slot Assignment Reg. Receive */
|
||||
u8 xccr; /* Transmit Channel Capacity Register */
|
||||
u8 rccr; /* Receive Channel Capacity Register */
|
||||
u8 bgr; /* Baud Rate Generator Register */
|
||||
u8 tic; /* Transmit Immediate Character */
|
||||
u8 mxn; /* Mask XON Character */
|
||||
u8 mxf; /* Mask XOFF Character */
|
||||
u8 iva; /* Interrupt Vector Address */
|
||||
u8 ipc;
|
||||
u8 imr0; /* Interrupt Mask Register 0 */
|
||||
u8 imr1; /* Interrupt Mask Register 1 */
|
||||
u8 pvr;
|
||||
u8 pim; /* Port Interrupt Mask */
|
||||
u8 pcr;
|
||||
u8 ccr4;
|
||||
};
|
||||
|
||||
struct sab82532_async_rw_regs { /* Read/Write registers */
|
||||
u8 __pad1[0x20];
|
||||
u8 __pad2;
|
||||
u8 __pad3;
|
||||
u8 mode;
|
||||
u8 timr;
|
||||
u8 xon;
|
||||
u8 xoff;
|
||||
u8 tcr;
|
||||
u8 dafo;
|
||||
u8 rfc;
|
||||
u8 __pad4;
|
||||
u8 __pad5;
|
||||
u8 __pad6;
|
||||
u8 ccr0;
|
||||
u8 ccr1;
|
||||
u8 ccr2;
|
||||
u8 ccr3;
|
||||
u8 __pad7;
|
||||
u8 __pad8;
|
||||
u8 __pad9;
|
||||
u8 __pad10;
|
||||
u8 __pad11;
|
||||
u8 __pad12;
|
||||
u8 __pad13;
|
||||
u8 __pad14;
|
||||
u8 __pad15;
|
||||
u8 ipc;
|
||||
u8 __pad16;
|
||||
u8 __pad17;
|
||||
u8 pvr;
|
||||
u8 __pad18;
|
||||
u8 pcr;
|
||||
u8 ccr4;
|
||||
};
|
||||
|
||||
union sab82532_async_regs {
|
||||
__volatile__ struct sab82532_async_rd_regs r;
|
||||
__volatile__ struct sab82532_async_wr_regs w;
|
||||
__volatile__ struct sab82532_async_rw_regs rw;
|
||||
};
|
||||
|
||||
union sab82532_irq_status {
|
||||
unsigned short stat;
|
||||
struct {
|
||||
unsigned char isr0;
|
||||
unsigned char isr1;
|
||||
} sreg;
|
||||
};
|
||||
|
||||
/* irqflags bits */
|
||||
#define SAB82532_ALLS 0x00000001
|
||||
#define SAB82532_XPR 0x00000002
|
||||
#define SAB82532_REGS_PENDING 0x00000004
|
||||
|
||||
/* RFIFO Status Byte */
|
||||
#define SAB82532_RSTAT_PE 0x80
|
||||
#define SAB82532_RSTAT_FE 0x40
|
||||
#define SAB82532_RSTAT_PARITY 0x01
|
||||
|
||||
/* Status Register (STAR) */
|
||||
#define SAB82532_STAR_XDOV 0x80
|
||||
#define SAB82532_STAR_XFW 0x40
|
||||
#define SAB82532_STAR_RFNE 0x20
|
||||
#define SAB82532_STAR_FCS 0x10
|
||||
#define SAB82532_STAR_TEC 0x08
|
||||
#define SAB82532_STAR_CEC 0x04
|
||||
#define SAB82532_STAR_CTS 0x02
|
||||
|
||||
/* Command Register (CMDR) */
|
||||
#define SAB82532_CMDR_RMC 0x80
|
||||
#define SAB82532_CMDR_RRES 0x40
|
||||
#define SAB82532_CMDR_RFRD 0x20
|
||||
#define SAB82532_CMDR_STI 0x10
|
||||
#define SAB82532_CMDR_XF 0x08
|
||||
#define SAB82532_CMDR_XRES 0x01
|
||||
|
||||
/* Mode Register (MODE) */
|
||||
#define SAB82532_MODE_FRTS 0x40
|
||||
#define SAB82532_MODE_FCTS 0x20
|
||||
#define SAB82532_MODE_FLON 0x10
|
||||
#define SAB82532_MODE_RAC 0x08
|
||||
#define SAB82532_MODE_RTS 0x04
|
||||
#define SAB82532_MODE_TRS 0x02
|
||||
#define SAB82532_MODE_TLP 0x01
|
||||
|
||||
/* Timer Register (TIMR) */
|
||||
#define SAB82532_TIMR_CNT_MASK 0xe0
|
||||
#define SAB82532_TIMR_VALUE_MASK 0x1f
|
||||
|
||||
/* Data Format (DAFO) */
|
||||
#define SAB82532_DAFO_XBRK 0x40
|
||||
#define SAB82532_DAFO_STOP 0x20
|
||||
#define SAB82532_DAFO_PAR_SPACE 0x00
|
||||
#define SAB82532_DAFO_PAR_ODD 0x08
|
||||
#define SAB82532_DAFO_PAR_EVEN 0x10
|
||||
#define SAB82532_DAFO_PAR_MARK 0x18
|
||||
#define SAB82532_DAFO_PARE 0x04
|
||||
#define SAB82532_DAFO_CHL8 0x00
|
||||
#define SAB82532_DAFO_CHL7 0x01
|
||||
#define SAB82532_DAFO_CHL6 0x02
|
||||
#define SAB82532_DAFO_CHL5 0x03
|
||||
|
||||
/* RFIFO Control Register (RFC) */
|
||||
#define SAB82532_RFC_DPS 0x40
|
||||
#define SAB82532_RFC_DXS 0x20
|
||||
#define SAB82532_RFC_RFDF 0x10
|
||||
#define SAB82532_RFC_RFTH_1 0x00
|
||||
#define SAB82532_RFC_RFTH_4 0x04
|
||||
#define SAB82532_RFC_RFTH_16 0x08
|
||||
#define SAB82532_RFC_RFTH_32 0x0c
|
||||
#define SAB82532_RFC_TCDE 0x01
|
||||
|
||||
/* Received Byte Count High (RBCH) */
|
||||
#define SAB82532_RBCH_DMA 0x80
|
||||
#define SAB82532_RBCH_CAS 0x20
|
||||
|
||||
/* Transmit Byte Count High (XBCH) */
|
||||
#define SAB82532_XBCH_DMA 0x80
|
||||
#define SAB82532_XBCH_CAS 0x20
|
||||
#define SAB82532_XBCH_XC 0x10
|
||||
|
||||
/* Channel Configuration Register 0 (CCR0) */
|
||||
#define SAB82532_CCR0_PU 0x80
|
||||
#define SAB82532_CCR0_MCE 0x40
|
||||
#define SAB82532_CCR0_SC_NRZ 0x00
|
||||
#define SAB82532_CCR0_SC_NRZI 0x08
|
||||
#define SAB82532_CCR0_SC_FM0 0x10
|
||||
#define SAB82532_CCR0_SC_FM1 0x14
|
||||
#define SAB82532_CCR0_SC_MANCH 0x18
|
||||
#define SAB82532_CCR0_SM_HDLC 0x00
|
||||
#define SAB82532_CCR0_SM_SDLC_LOOP 0x01
|
||||
#define SAB82532_CCR0_SM_BISYNC 0x02
|
||||
#define SAB82532_CCR0_SM_ASYNC 0x03
|
||||
|
||||
/* Channel Configuration Register 1 (CCR1) */
|
||||
#define SAB82532_CCR1_ODS 0x10
|
||||
#define SAB82532_CCR1_BCR 0x08
|
||||
#define SAB82532_CCR1_CM_MASK 0x07
|
||||
|
||||
/* Channel Configuration Register 2 (CCR2) */
|
||||
#define SAB82532_CCR2_SOC1 0x80
|
||||
#define SAB82532_CCR2_SOC0 0x40
|
||||
#define SAB82532_CCR2_BR9 0x80
|
||||
#define SAB82532_CCR2_BR8 0x40
|
||||
#define SAB82532_CCR2_BDF 0x20
|
||||
#define SAB82532_CCR2_SSEL 0x10
|
||||
#define SAB82532_CCR2_XCS0 0x20
|
||||
#define SAB82532_CCR2_RCS0 0x10
|
||||
#define SAB82532_CCR2_TOE 0x08
|
||||
#define SAB82532_CCR2_RWX 0x04
|
||||
#define SAB82532_CCR2_DIV 0x01
|
||||
|
||||
/* Channel Configuration Register 3 (CCR3) */
|
||||
#define SAB82532_CCR3_PSD 0x01
|
||||
|
||||
/* Time Slot Assignment Register Transmit (TSAX) */
|
||||
#define SAB82532_TSAX_TSNX_MASK 0xfc
|
||||
#define SAB82532_TSAX_XCS2 0x02 /* see also CCR2 */
|
||||
#define SAB82532_TSAX_XCS1 0x01
|
||||
|
||||
/* Time Slot Assignment Register Receive (TSAR) */
|
||||
#define SAB82532_TSAR_TSNR_MASK 0xfc
|
||||
#define SAB82532_TSAR_RCS2 0x02 /* see also CCR2 */
|
||||
#define SAB82532_TSAR_RCS1 0x01
|
||||
|
||||
/* Version Status Register (VSTR) */
|
||||
#define SAB82532_VSTR_CD 0x80
|
||||
#define SAB82532_VSTR_DPLA 0x40
|
||||
#define SAB82532_VSTR_VN_MASK 0x0f
|
||||
#define SAB82532_VSTR_VN_1 0x00
|
||||
#define SAB82532_VSTR_VN_2 0x01
|
||||
#define SAB82532_VSTR_VN_3_2 0x02
|
||||
|
||||
/* Global Interrupt Status Register (GIS) */
|
||||
#define SAB82532_GIS_PI 0x80
|
||||
#define SAB82532_GIS_ISA1 0x08
|
||||
#define SAB82532_GIS_ISA0 0x04
|
||||
#define SAB82532_GIS_ISB1 0x02
|
||||
#define SAB82532_GIS_ISB0 0x01
|
||||
|
||||
/* Interrupt Vector Address (IVA) */
|
||||
#define SAB82532_IVA_MASK 0xf1
|
||||
|
||||
/* Interrupt Port Configuration (IPC) */
|
||||
#define SAB82532_IPC_VIS 0x80
|
||||
#define SAB82532_IPC_SLA1 0x10
|
||||
#define SAB82532_IPC_SLA0 0x08
|
||||
#define SAB82532_IPC_CASM 0x04
|
||||
#define SAB82532_IPC_IC_OPEN_DRAIN 0x00
|
||||
#define SAB82532_IPC_IC_ACT_LOW 0x01
|
||||
#define SAB82532_IPC_IC_ACT_HIGH 0x03
|
||||
|
||||
/* Interrupt Status Register 0 (ISR0) */
|
||||
#define SAB82532_ISR0_TCD 0x80
|
||||
#define SAB82532_ISR0_TIME 0x40
|
||||
#define SAB82532_ISR0_PERR 0x20
|
||||
#define SAB82532_ISR0_FERR 0x10
|
||||
#define SAB82532_ISR0_PLLA 0x08
|
||||
#define SAB82532_ISR0_CDSC 0x04
|
||||
#define SAB82532_ISR0_RFO 0x02
|
||||
#define SAB82532_ISR0_RPF 0x01
|
||||
|
||||
/* Interrupt Status Register 1 (ISR1) */
|
||||
#define SAB82532_ISR1_BRK 0x80
|
||||
#define SAB82532_ISR1_BRKT 0x40
|
||||
#define SAB82532_ISR1_ALLS 0x20
|
||||
#define SAB82532_ISR1_XOFF 0x10
|
||||
#define SAB82532_ISR1_TIN 0x08
|
||||
#define SAB82532_ISR1_CSC 0x04
|
||||
#define SAB82532_ISR1_XON 0x02
|
||||
#define SAB82532_ISR1_XPR 0x01
|
||||
|
||||
/* Interrupt Mask Register 0 (IMR0) */
|
||||
#define SAB82532_IMR0_TCD 0x80
|
||||
#define SAB82532_IMR0_TIME 0x40
|
||||
#define SAB82532_IMR0_PERR 0x20
|
||||
#define SAB82532_IMR0_FERR 0x10
|
||||
#define SAB82532_IMR0_PLLA 0x08
|
||||
#define SAB82532_IMR0_CDSC 0x04
|
||||
#define SAB82532_IMR0_RFO 0x02
|
||||
#define SAB82532_IMR0_RPF 0x01
|
||||
|
||||
/* Interrupt Mask Register 1 (IMR1) */
|
||||
#define SAB82532_IMR1_BRK 0x80
|
||||
#define SAB82532_IMR1_BRKT 0x40
|
||||
#define SAB82532_IMR1_ALLS 0x20
|
||||
#define SAB82532_IMR1_XOFF 0x10
|
||||
#define SAB82532_IMR1_TIN 0x08
|
||||
#define SAB82532_IMR1_CSC 0x04
|
||||
#define SAB82532_IMR1_XON 0x02
|
||||
#define SAB82532_IMR1_XPR 0x01
|
||||
|
||||
/* Port Interrupt Status Register (PIS) */
|
||||
#define SAB82532_PIS_SYNC_B 0x08
|
||||
#define SAB82532_PIS_DTR_B 0x04
|
||||
#define SAB82532_PIS_DTR_A 0x02
|
||||
#define SAB82532_PIS_SYNC_A 0x01
|
||||
|
||||
/* Channel Configuration Register 4 (CCR4) */
|
||||
#define SAB82532_CCR4_MCK4 0x80
|
||||
#define SAB82532_CCR4_EBRG 0x40
|
||||
#define SAB82532_CCR4_TST1 0x20
|
||||
#define SAB82532_CCR4_ICD 0x10
|
||||
|
||||
|
||||
#endif /* !(_SUNSAB_H) */
|
||||
1583
drivers/serial/sunsu.c
Normal file
1583
drivers/serial/sunsu.c
Normal file
File diff suppressed because it is too large
Load Diff
1534
drivers/serial/sunzilog.c
Normal file
1534
drivers/serial/sunzilog.c
Normal file
File diff suppressed because it is too large
Load Diff
272
drivers/serial/sunzilog.h
Normal file
272
drivers/serial/sunzilog.h
Normal file
@@ -0,0 +1,272 @@
|
||||
#ifndef _SUNZILOG_H
|
||||
#define _SUNZILOG_H
|
||||
|
||||
struct zilog_channel {
|
||||
volatile unsigned char control;
|
||||
volatile unsigned char __pad1;
|
||||
volatile unsigned char data;
|
||||
volatile unsigned char __pad2;
|
||||
};
|
||||
|
||||
struct zilog_layout {
|
||||
struct zilog_channel channelB;
|
||||
struct zilog_channel channelA;
|
||||
};
|
||||
|
||||
#define NUM_ZSREGS 16
|
||||
|
||||
/* Conversion routines to/from brg time constants from/to bits
|
||||
* per second.
|
||||
*/
|
||||
#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
|
||||
#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
|
||||
|
||||
/* The Zilog register set */
|
||||
|
||||
#define FLAG 0x7e
|
||||
|
||||
/* Write Register 0 */
|
||||
#define R0 0 /* Register selects */
|
||||
#define R1 1
|
||||
#define R2 2
|
||||
#define R3 3
|
||||
#define R4 4
|
||||
#define R5 5
|
||||
#define R6 6
|
||||
#define R7 7
|
||||
#define R8 8
|
||||
#define R9 9
|
||||
#define R10 10
|
||||
#define R11 11
|
||||
#define R12 12
|
||||
#define R13 13
|
||||
#define R14 14
|
||||
#define R15 15
|
||||
|
||||
#define NULLCODE 0 /* Null Code */
|
||||
#define POINT_HIGH 0x8 /* Select upper half of registers */
|
||||
#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
|
||||
#define SEND_ABORT 0x18 /* HDLC Abort */
|
||||
#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
|
||||
#define RES_Tx_P 0x28 /* Reset TxINT Pending */
|
||||
#define ERR_RES 0x30 /* Error Reset */
|
||||
#define RES_H_IUS 0x38 /* Reset highest IUS */
|
||||
|
||||
#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
|
||||
#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
|
||||
#define RES_EOM_L 0xC0 /* Reset EOM latch */
|
||||
|
||||
/* Write Register 1 */
|
||||
|
||||
#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
|
||||
#define TxINT_ENAB 0x2 /* Tx Int Enable */
|
||||
#define PAR_SPEC 0x4 /* Parity is special condition */
|
||||
|
||||
#define RxINT_DISAB 0 /* Rx Int Disable */
|
||||
#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
|
||||
#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
|
||||
#define INT_ERR_Rx 0x18 /* Int on error only */
|
||||
#define RxINT_MASK 0x18
|
||||
|
||||
#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
|
||||
#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
|
||||
#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
|
||||
|
||||
/* Write Register #2 (Interrupt Vector) */
|
||||
|
||||
/* Write Register 3 */
|
||||
|
||||
#define RxENAB 0x1 /* Rx Enable */
|
||||
#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
|
||||
#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
|
||||
#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
|
||||
#define ENT_HM 0x10 /* Enter Hunt Mode */
|
||||
#define AUTO_ENAB 0x20 /* Auto Enables */
|
||||
#define Rx5 0x0 /* Rx 5 Bits/Character */
|
||||
#define Rx7 0x40 /* Rx 7 Bits/Character */
|
||||
#define Rx6 0x80 /* Rx 6 Bits/Character */
|
||||
#define Rx8 0xc0 /* Rx 8 Bits/Character */
|
||||
#define RxN_MASK 0xc0
|
||||
|
||||
/* Write Register 4 */
|
||||
|
||||
#define PAR_ENAB 0x1 /* Parity Enable */
|
||||
#define PAR_EVEN 0x2 /* Parity Even/Odd* */
|
||||
|
||||
#define SYNC_ENAB 0 /* Sync Modes Enable */
|
||||
#define SB1 0x4 /* 1 stop bit/char */
|
||||
#define SB15 0x8 /* 1.5 stop bits/char */
|
||||
#define SB2 0xc /* 2 stop bits/char */
|
||||
|
||||
#define MONSYNC 0 /* 8 Bit Sync character */
|
||||
#define BISYNC 0x10 /* 16 bit sync character */
|
||||
#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
|
||||
#define EXTSYNC 0x30 /* External Sync Mode */
|
||||
|
||||
#define X1CLK 0x0 /* x1 clock mode */
|
||||
#define X16CLK 0x40 /* x16 clock mode */
|
||||
#define X32CLK 0x80 /* x32 clock mode */
|
||||
#define X64CLK 0xC0 /* x64 clock mode */
|
||||
#define XCLK_MASK 0xC0
|
||||
|
||||
/* Write Register 5 */
|
||||
|
||||
#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
|
||||
#define RTS 0x2 /* RTS */
|
||||
#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
|
||||
#define TxENAB 0x8 /* Tx Enable */
|
||||
#define SND_BRK 0x10 /* Send Break */
|
||||
#define Tx5 0x0 /* Tx 5 bits (or less)/character */
|
||||
#define Tx7 0x20 /* Tx 7 bits/character */
|
||||
#define Tx6 0x40 /* Tx 6 bits/character */
|
||||
#define Tx8 0x60 /* Tx 8 bits/character */
|
||||
#define TxN_MASK 0x60
|
||||
#define DTR 0x80 /* DTR */
|
||||
|
||||
/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
|
||||
|
||||
/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
|
||||
|
||||
/* Write Register 8 (transmit buffer) */
|
||||
|
||||
/* Write Register 9 (Master interrupt control) */
|
||||
#define VIS 1 /* Vector Includes Status */
|
||||
#define NV 2 /* No Vector */
|
||||
#define DLC 4 /* Disable Lower Chain */
|
||||
#define MIE 8 /* Master Interrupt Enable */
|
||||
#define STATHI 0x10 /* Status high */
|
||||
#define NORESET 0 /* No reset on write to R9 */
|
||||
#define CHRB 0x40 /* Reset channel B */
|
||||
#define CHRA 0x80 /* Reset channel A */
|
||||
#define FHWRES 0xc0 /* Force hardware reset */
|
||||
|
||||
/* Write Register 10 (misc control bits) */
|
||||
#define BIT6 1 /* 6 bit/8bit sync */
|
||||
#define LOOPMODE 2 /* SDLC Loop mode */
|
||||
#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
|
||||
#define MARKIDLE 8 /* Mark/flag on idle */
|
||||
#define GAOP 0x10 /* Go active on poll */
|
||||
#define NRZ 0 /* NRZ mode */
|
||||
#define NRZI 0x20 /* NRZI mode */
|
||||
#define FM1 0x40 /* FM1 (transition = 1) */
|
||||
#define FM0 0x60 /* FM0 (transition = 0) */
|
||||
#define CRCPS 0x80 /* CRC Preset I/O */
|
||||
|
||||
/* Write Register 11 (Clock Mode control) */
|
||||
#define TRxCXT 0 /* TRxC = Xtal output */
|
||||
#define TRxCTC 1 /* TRxC = Transmit clock */
|
||||
#define TRxCBR 2 /* TRxC = BR Generator Output */
|
||||
#define TRxCDP 3 /* TRxC = DPLL output */
|
||||
#define TRxCOI 4 /* TRxC O/I */
|
||||
#define TCRTxCP 0 /* Transmit clock = RTxC pin */
|
||||
#define TCTRxCP 8 /* Transmit clock = TRxC pin */
|
||||
#define TCBR 0x10 /* Transmit clock = BR Generator output */
|
||||
#define TCDPLL 0x18 /* Transmit clock = DPLL output */
|
||||
#define RCRTxCP 0 /* Receive clock = RTxC pin */
|
||||
#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
|
||||
#define RCBR 0x40 /* Receive clock = BR Generator output */
|
||||
#define RCDPLL 0x60 /* Receive clock = DPLL output */
|
||||
#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
|
||||
|
||||
/* Write Register 12 (lower byte of baud rate generator time constant) */
|
||||
|
||||
/* Write Register 13 (upper byte of baud rate generator time constant) */
|
||||
|
||||
/* Write Register 14 (Misc control bits) */
|
||||
#define BRENAB 1 /* Baud rate generator enable */
|
||||
#define BRSRC 2 /* Baud rate generator source */
|
||||
#define DTRREQ 4 /* DTR/Request function */
|
||||
#define AUTOECHO 8 /* Auto Echo */
|
||||
#define LOOPBAK 0x10 /* Local loopback */
|
||||
#define SEARCH 0x20 /* Enter search mode */
|
||||
#define RMC 0x40 /* Reset missing clock */
|
||||
#define DISDPLL 0x60 /* Disable DPLL */
|
||||
#define SSBR 0x80 /* Set DPLL source = BR generator */
|
||||
#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
|
||||
#define SFMM 0xc0 /* Set FM mode */
|
||||
#define SNRZI 0xe0 /* Set NRZI mode */
|
||||
|
||||
/* Write Register 15 (external/status interrupt control) */
|
||||
#define ZCIE 2 /* Zero count IE */
|
||||
#define DCDIE 8 /* DCD IE */
|
||||
#define SYNCIE 0x10 /* Sync/hunt IE */
|
||||
#define CTSIE 0x20 /* CTS IE */
|
||||
#define TxUIE 0x40 /* Tx Underrun/EOM IE */
|
||||
#define BRKIE 0x80 /* Break/Abort IE */
|
||||
|
||||
|
||||
/* Read Register 0 */
|
||||
#define Rx_CH_AV 0x1 /* Rx Character Available */
|
||||
#define ZCOUNT 0x2 /* Zero count */
|
||||
#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
|
||||
#define DCD 0x8 /* DCD */
|
||||
#define SYNC 0x10 /* Sync/hunt */
|
||||
#define CTS 0x20 /* CTS */
|
||||
#define TxEOM 0x40 /* Tx underrun */
|
||||
#define BRK_ABRT 0x80 /* Break/Abort */
|
||||
|
||||
/* Read Register 1 */
|
||||
#define ALL_SNT 0x1 /* All sent */
|
||||
/* Residue Data for 8 Rx bits/char programmed */
|
||||
#define RES3 0x8 /* 0/3 */
|
||||
#define RES4 0x4 /* 0/4 */
|
||||
#define RES5 0xc /* 0/5 */
|
||||
#define RES6 0x2 /* 0/6 */
|
||||
#define RES7 0xa /* 0/7 */
|
||||
#define RES8 0x6 /* 0/8 */
|
||||
#define RES18 0xe /* 1/8 */
|
||||
#define RES28 0x0 /* 2/8 */
|
||||
/* Special Rx Condition Interrupts */
|
||||
#define PAR_ERR 0x10 /* Parity error */
|
||||
#define Rx_OVR 0x20 /* Rx Overrun Error */
|
||||
#define CRC_ERR 0x40 /* CRC/Framing Error */
|
||||
#define END_FR 0x80 /* End of Frame (SDLC) */
|
||||
|
||||
/* Read Register 2 (channel b only) - Interrupt vector */
|
||||
#define CHB_Tx_EMPTY 0x00
|
||||
#define CHB_EXT_STAT 0x02
|
||||
#define CHB_Rx_AVAIL 0x04
|
||||
#define CHB_SPECIAL 0x06
|
||||
#define CHA_Tx_EMPTY 0x08
|
||||
#define CHA_EXT_STAT 0x0a
|
||||
#define CHA_Rx_AVAIL 0x0c
|
||||
#define CHA_SPECIAL 0x0e
|
||||
#define STATUS_MASK 0x0e
|
||||
|
||||
/* Read Register 3 (interrupt pending register) ch a only */
|
||||
#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
|
||||
#define CHBTxIP 0x2 /* Channel B Tx IP */
|
||||
#define CHBRxIP 0x4 /* Channel B Rx IP */
|
||||
#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
|
||||
#define CHATxIP 0x10 /* Channel A Tx IP */
|
||||
#define CHARxIP 0x20 /* Channel A Rx IP */
|
||||
|
||||
/* Read Register 8 (receive data register) */
|
||||
|
||||
/* Read Register 10 (misc status bits) */
|
||||
#define ONLOOP 2 /* On loop */
|
||||
#define LOOPSEND 0x10 /* Loop sending */
|
||||
#define CLK2MIS 0x40 /* Two clocks missing */
|
||||
#define CLK1MIS 0x80 /* One clock missing */
|
||||
|
||||
/* Read Register 12 (lower byte of baud rate generator constant) */
|
||||
|
||||
/* Read Register 13 (upper byte of baud rate generator constant) */
|
||||
|
||||
/* Read Register 15 (value of WR 15) */
|
||||
|
||||
/* Misc macros */
|
||||
#define ZS_CLEARERR(channel) do { sbus_writeb(ERR_RES, &channel->control); \
|
||||
udelay(5); } while(0)
|
||||
|
||||
#define ZS_CLEARSTAT(channel) do { sbus_writeb(RES_EXT_INT, &channel->control); \
|
||||
udelay(5); } while(0)
|
||||
|
||||
#define ZS_CLEARFIFO(channel) do { sbus_readb(&channel->data); \
|
||||
udelay(2); \
|
||||
sbus_readb(&channel->data); \
|
||||
udelay(2); \
|
||||
sbus_readb(&channel->data); \
|
||||
udelay(2); } while(0)
|
||||
|
||||
#endif /* _SUNZILOG_H */
|
||||
505
drivers/serial/uartlite.c
Normal file
505
drivers/serial/uartlite.c
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* uartlite.c: Serial driver for Xilinx uartlite serial controller
|
||||
*
|
||||
* Peter Korsgaard <jacmet@sunsite.dk>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define ULITE_MAJOR 204
|
||||
#define ULITE_MINOR 187
|
||||
#define ULITE_NR_UARTS 4
|
||||
|
||||
/* For register details see datasheet:
|
||||
http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf
|
||||
*/
|
||||
#define ULITE_RX 0x00
|
||||
#define ULITE_TX 0x04
|
||||
#define ULITE_STATUS 0x08
|
||||
#define ULITE_CONTROL 0x0c
|
||||
|
||||
#define ULITE_REGION 16
|
||||
|
||||
#define ULITE_STATUS_RXVALID 0x01
|
||||
#define ULITE_STATUS_RXFULL 0x02
|
||||
#define ULITE_STATUS_TXEMPTY 0x04
|
||||
#define ULITE_STATUS_TXFULL 0x08
|
||||
#define ULITE_STATUS_IE 0x10
|
||||
#define ULITE_STATUS_OVERRUN 0x20
|
||||
#define ULITE_STATUS_FRAME 0x40
|
||||
#define ULITE_STATUS_PARITY 0x80
|
||||
|
||||
#define ULITE_CONTROL_RST_TX 0x01
|
||||
#define ULITE_CONTROL_RST_RX 0x02
|
||||
#define ULITE_CONTROL_IE 0x10
|
||||
|
||||
|
||||
static struct uart_port ports[ULITE_NR_UARTS];
|
||||
|
||||
static int ulite_receive(struct uart_port *port, int stat)
|
||||
{
|
||||
struct tty_struct *tty = port->info->tty;
|
||||
unsigned char ch = 0;
|
||||
char flag = TTY_NORMAL;
|
||||
|
||||
if ((stat & (ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
|
||||
| ULITE_STATUS_FRAME)) == 0)
|
||||
return 0;
|
||||
|
||||
/* stats */
|
||||
if (stat & ULITE_STATUS_RXVALID) {
|
||||
port->icount.rx++;
|
||||
ch = readb(port->membase + ULITE_RX);
|
||||
|
||||
if (stat & ULITE_STATUS_PARITY)
|
||||
port->icount.parity++;
|
||||
}
|
||||
|
||||
if (stat & ULITE_STATUS_OVERRUN)
|
||||
port->icount.overrun++;
|
||||
|
||||
if (stat & ULITE_STATUS_FRAME)
|
||||
port->icount.frame++;
|
||||
|
||||
|
||||
/* drop byte with parity error if IGNPAR specificed */
|
||||
if (stat & port->ignore_status_mask & ULITE_STATUS_PARITY)
|
||||
stat &= ~ULITE_STATUS_RXVALID;
|
||||
|
||||
stat &= port->read_status_mask;
|
||||
|
||||
if (stat & ULITE_STATUS_PARITY)
|
||||
flag = TTY_PARITY;
|
||||
|
||||
|
||||
stat &= ~port->ignore_status_mask;
|
||||
|
||||
if (stat & ULITE_STATUS_RXVALID)
|
||||
tty_insert_flip_char(tty, ch, flag);
|
||||
|
||||
if (stat & ULITE_STATUS_FRAME)
|
||||
tty_insert_flip_char(tty, 0, TTY_FRAME);
|
||||
|
||||
if (stat & ULITE_STATUS_OVERRUN)
|
||||
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ulite_transmit(struct uart_port *port, int stat)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
if (stat & ULITE_STATUS_TXFULL)
|
||||
return 0;
|
||||
|
||||
if (port->x_char) {
|
||||
writeb(port->x_char, port->membase + ULITE_TX);
|
||||
port->x_char = 0;
|
||||
port->icount.tx++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
|
||||
return 0;
|
||||
|
||||
writeb(xmit->buf[xmit->tail], port->membase + ULITE_TX);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
|
||||
port->icount.tx++;
|
||||
|
||||
/* wake up */
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static irqreturn_t ulite_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = (struct uart_port *)dev_id;
|
||||
int busy;
|
||||
|
||||
do {
|
||||
int stat = readb(port->membase + ULITE_STATUS);
|
||||
busy = ulite_receive(port, stat);
|
||||
busy |= ulite_transmit(port, stat);
|
||||
} while (busy);
|
||||
|
||||
tty_flip_buffer_push(port->info->tty);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int ulite_tx_empty(struct uart_port *port)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int ret;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
ret = readb(port->membase + ULITE_STATUS);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
static unsigned int ulite_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
|
||||
}
|
||||
|
||||
static void ulite_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
/* N/A */
|
||||
}
|
||||
|
||||
static void ulite_stop_tx(struct uart_port *port)
|
||||
{
|
||||
/* N/A */
|
||||
}
|
||||
|
||||
static void ulite_start_tx(struct uart_port *port)
|
||||
{
|
||||
ulite_transmit(port, readb(port->membase + ULITE_STATUS));
|
||||
}
|
||||
|
||||
static void ulite_stop_rx(struct uart_port *port)
|
||||
{
|
||||
/* don't forward any more data (like !CREAD) */
|
||||
port->ignore_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY
|
||||
| ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
|
||||
}
|
||||
|
||||
static void ulite_enable_ms(struct uart_port *port)
|
||||
{
|
||||
/* N/A */
|
||||
}
|
||||
|
||||
static void ulite_break_ctl(struct uart_port *port, int ctl)
|
||||
{
|
||||
/* N/A */
|
||||
}
|
||||
|
||||
static int ulite_startup(struct uart_port *port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = request_irq(port->irq, ulite_isr,
|
||||
IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "uartlite", port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writeb(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX,
|
||||
port->membase + ULITE_CONTROL);
|
||||
writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ulite_shutdown(struct uart_port *port)
|
||||
{
|
||||
writeb(0, port->membase + ULITE_CONTROL);
|
||||
readb(port->membase + ULITE_CONTROL); /* dummy */
|
||||
free_irq(port->irq, port);
|
||||
}
|
||||
|
||||
static void ulite_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int baud;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
|
||||
| ULITE_STATUS_TXFULL;
|
||||
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |=
|
||||
ULITE_STATUS_PARITY | ULITE_STATUS_FRAME;
|
||||
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= ULITE_STATUS_PARITY
|
||||
| ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
|
||||
|
||||
/* ignore all characters if CREAD is not set */
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |=
|
||||
ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY
|
||||
| ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
|
||||
|
||||
/* update timeout */
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, 460800);
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *ulite_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_UARTLITE ? "uartlite" : NULL;
|
||||
}
|
||||
|
||||
static void ulite_release_port(struct uart_port *port)
|
||||
{
|
||||
release_mem_region(port->mapbase, ULITE_REGION);
|
||||
iounmap(port->membase);
|
||||
port->membase = NULL;
|
||||
}
|
||||
|
||||
static int ulite_request_port(struct uart_port *port)
|
||||
{
|
||||
if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) {
|
||||
dev_err(port->dev, "Memory region busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
port->membase = ioremap(port->mapbase, ULITE_REGION);
|
||||
if (!port->membase) {
|
||||
dev_err(port->dev, "Unable to map registers\n");
|
||||
release_mem_region(port->mapbase, ULITE_REGION);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ulite_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (!ulite_request_port(port))
|
||||
port->type = PORT_UARTLITE;
|
||||
}
|
||||
|
||||
static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
/* we don't want the core code to modify any port params */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct uart_ops ulite_ops = {
|
||||
.tx_empty = ulite_tx_empty,
|
||||
.set_mctrl = ulite_set_mctrl,
|
||||
.get_mctrl = ulite_get_mctrl,
|
||||
.stop_tx = ulite_stop_tx,
|
||||
.start_tx = ulite_start_tx,
|
||||
.stop_rx = ulite_stop_rx,
|
||||
.enable_ms = ulite_enable_ms,
|
||||
.break_ctl = ulite_break_ctl,
|
||||
.startup = ulite_startup,
|
||||
.shutdown = ulite_shutdown,
|
||||
.set_termios = ulite_set_termios,
|
||||
.type = ulite_type,
|
||||
.release_port = ulite_release_port,
|
||||
.request_port = ulite_request_port,
|
||||
.config_port = ulite_config_port,
|
||||
.verify_port = ulite_verify_port
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
|
||||
static void ulite_console_wait_tx(struct uart_port *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* wait up to 10ms for the character(s) to be sent */
|
||||
for (i = 0; i < 10000; i++) {
|
||||
if (readb(port->membase + ULITE_STATUS) & ULITE_STATUS_TXEMPTY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void ulite_console_putchar(struct uart_port *port, int ch)
|
||||
{
|
||||
ulite_console_wait_tx(port);
|
||||
writeb(ch, port->membase + ULITE_TX);
|
||||
}
|
||||
|
||||
static void ulite_console_write(struct console *co, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
struct uart_port *port = &ports[co->index];
|
||||
unsigned long flags;
|
||||
unsigned int ier;
|
||||
int locked = 1;
|
||||
|
||||
if (oops_in_progress) {
|
||||
locked = spin_trylock_irqsave(&port->lock, flags);
|
||||
} else
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/* save and disable interrupt */
|
||||
ier = readb(port->membase + ULITE_STATUS) & ULITE_STATUS_IE;
|
||||
writeb(0, port->membase + ULITE_CONTROL);
|
||||
|
||||
uart_console_write(port, s, count, ulite_console_putchar);
|
||||
|
||||
ulite_console_wait_tx(port);
|
||||
|
||||
/* restore interrupt state */
|
||||
if (ier)
|
||||
writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
|
||||
|
||||
if (locked)
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int __init ulite_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (co->index < 0 || co->index >= ULITE_NR_UARTS)
|
||||
return -EINVAL;
|
||||
|
||||
port = &ports[co->index];
|
||||
|
||||
/* not initialized yet? */
|
||||
if (!port->membase)
|
||||
return -ENODEV;
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
static struct uart_driver ulite_uart_driver;
|
||||
|
||||
static struct console ulite_console = {
|
||||
.name = "ttyUL",
|
||||
.write = ulite_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = ulite_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1, /* Specified on the cmdline (e.g. console=ttyUL0 ) */
|
||||
.data = &ulite_uart_driver,
|
||||
};
|
||||
|
||||
static int __init ulite_console_init(void)
|
||||
{
|
||||
register_console(&ulite_console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_initcall(ulite_console_init);
|
||||
|
||||
#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */
|
||||
|
||||
static struct uart_driver ulite_uart_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "uartlite",
|
||||
.dev_name = "ttyUL",
|
||||
.major = ULITE_MAJOR,
|
||||
.minor = ULITE_MINOR,
|
||||
.nr = ULITE_NR_UARTS,
|
||||
#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
|
||||
.cons = &ulite_console,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __devinit ulite_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res, *res2;
|
||||
struct uart_port *port;
|
||||
|
||||
if (pdev->id < 0 || pdev->id >= ULITE_NR_UARTS)
|
||||
return -EINVAL;
|
||||
|
||||
if (ports[pdev->id].membase)
|
||||
return -EBUSY;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res2)
|
||||
return -ENODEV;
|
||||
|
||||
port = &ports[pdev->id];
|
||||
|
||||
port->fifosize = 16;
|
||||
port->regshift = 2;
|
||||
port->iotype = UPIO_MEM;
|
||||
port->iobase = 1; /* mark port in use */
|
||||
port->mapbase = res->start;
|
||||
port->membase = NULL;
|
||||
port->ops = &ulite_ops;
|
||||
port->irq = res2->start;
|
||||
port->flags = UPF_BOOT_AUTOCONF;
|
||||
port->dev = &pdev->dev;
|
||||
port->type = PORT_UNKNOWN;
|
||||
port->line = pdev->id;
|
||||
|
||||
uart_add_one_port(&ulite_uart_driver, port);
|
||||
platform_set_drvdata(pdev, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ulite_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *port = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (port)
|
||||
uart_remove_one_port(&ulite_uart_driver, port);
|
||||
|
||||
/* mark port as free */
|
||||
port->membase = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ulite_platform_driver = {
|
||||
.probe = ulite_probe,
|
||||
.remove = ulite_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "uartlite",
|
||||
},
|
||||
};
|
||||
|
||||
int __init ulite_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uart_register_driver(&ulite_uart_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&ulite_platform_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&ulite_uart_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __exit ulite_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ulite_platform_driver);
|
||||
uart_unregister_driver(&ulite_uart_driver);
|
||||
}
|
||||
|
||||
module_init(ulite_init);
|
||||
module_exit(ulite_exit);
|
||||
|
||||
MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
|
||||
MODULE_DESCRIPTION("Xilinx uartlite serial driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
548
drivers/serial/v850e_uart.c
Normal file
548
drivers/serial/v850e_uart.c
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* drivers/serial/v850e_uart.c -- Serial I/O using V850E on-chip UART or UARTB
|
||||
*
|
||||
* Copyright (C) 2001,02,03 NEC Electronics Corporation
|
||||
* Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file COPYING in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* Written by Miles Bader <miles@gnu.org>
|
||||
*/
|
||||
|
||||
/* This driver supports both the original V850E UART interface (called
|
||||
merely `UART' in the docs) and the newer `UARTB' interface, which is
|
||||
roughly a superset of the first one. The selection is made at
|
||||
configure time -- if CONFIG_V850E_UARTB is defined, then UARTB is
|
||||
presumed, otherwise the old UART -- as these are on-CPU UARTS, a system
|
||||
can never have both.
|
||||
|
||||
The UARTB interface also has a 16-entry FIFO mode, which is not
|
||||
yet supported by this driver. */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
#include <asm/v850e_uart.h>
|
||||
|
||||
/* Initial UART state. This may be overridden by machine-dependent headers. */
|
||||
#ifndef V850E_UART_INIT_BAUD
|
||||
#define V850E_UART_INIT_BAUD 115200
|
||||
#endif
|
||||
#ifndef V850E_UART_INIT_CFLAGS
|
||||
#define V850E_UART_INIT_CFLAGS (B115200 | CS8 | CREAD)
|
||||
#endif
|
||||
|
||||
/* A string used for prefixing printed descriptions; since the same UART
|
||||
macro is actually used on other chips than the V850E. This must be a
|
||||
constant string. */
|
||||
#ifndef V850E_UART_CHIP_NAME
|
||||
#define V850E_UART_CHIP_NAME "V850E"
|
||||
#endif
|
||||
|
||||
#define V850E_UART_MINOR_BASE 64 /* First tty minor number */
|
||||
|
||||
|
||||
/* Low-level UART functions. */
|
||||
|
||||
/* Configure and turn on uart channel CHAN, using the termios `control
|
||||
modes' bits in CFLAGS, and a baud-rate of BAUD. */
|
||||
void v850e_uart_configure (unsigned chan, unsigned cflags, unsigned baud)
|
||||
{
|
||||
int flags;
|
||||
v850e_uart_speed_t old_speed;
|
||||
v850e_uart_config_t old_config;
|
||||
v850e_uart_speed_t new_speed = v850e_uart_calc_speed (baud);
|
||||
v850e_uart_config_t new_config = v850e_uart_calc_config (cflags);
|
||||
|
||||
/* Disable interrupts while we're twiddling the hardware. */
|
||||
local_irq_save (flags);
|
||||
|
||||
#ifdef V850E_UART_PRE_CONFIGURE
|
||||
V850E_UART_PRE_CONFIGURE (chan, cflags, baud);
|
||||
#endif
|
||||
|
||||
old_config = V850E_UART_CONFIG (chan);
|
||||
old_speed = v850e_uart_speed (chan);
|
||||
|
||||
if (! v850e_uart_speed_eq (old_speed, new_speed)) {
|
||||
/* The baud rate has changed. First, disable the UART. */
|
||||
V850E_UART_CONFIG (chan) = V850E_UART_CONFIG_FINI;
|
||||
old_config = 0; /* Force the uart to be re-initialized. */
|
||||
|
||||
/* Reprogram the baud-rate generator. */
|
||||
v850e_uart_set_speed (chan, new_speed);
|
||||
}
|
||||
|
||||
if (! (old_config & V850E_UART_CONFIG_ENABLED)) {
|
||||
/* If we are using the uart for the first time, start by
|
||||
enabling it, which must be done before turning on any
|
||||
other bits. */
|
||||
V850E_UART_CONFIG (chan) = V850E_UART_CONFIG_INIT;
|
||||
/* See the initial state. */
|
||||
old_config = V850E_UART_CONFIG (chan);
|
||||
}
|
||||
|
||||
if (new_config != old_config) {
|
||||
/* Which of the TXE/RXE bits we'll temporarily turn off
|
||||
before changing other control bits. */
|
||||
unsigned temp_disable = 0;
|
||||
/* Which of the TXE/RXE bits will be enabled. */
|
||||
unsigned enable = 0;
|
||||
unsigned changed_bits = new_config ^ old_config;
|
||||
|
||||
/* Which of RX/TX will be enabled in the new configuration. */
|
||||
if (new_config & V850E_UART_CONFIG_RX_BITS)
|
||||
enable |= (new_config & V850E_UART_CONFIG_RX_ENABLE);
|
||||
if (new_config & V850E_UART_CONFIG_TX_BITS)
|
||||
enable |= (new_config & V850E_UART_CONFIG_TX_ENABLE);
|
||||
|
||||
/* Figure out which of RX/TX needs to be disabled; note
|
||||
that this will only happen if they're not already
|
||||
disabled. */
|
||||
if (changed_bits & V850E_UART_CONFIG_RX_BITS)
|
||||
temp_disable
|
||||
|= (old_config & V850E_UART_CONFIG_RX_ENABLE);
|
||||
if (changed_bits & V850E_UART_CONFIG_TX_BITS)
|
||||
temp_disable
|
||||
|= (old_config & V850E_UART_CONFIG_TX_ENABLE);
|
||||
|
||||
/* We have to turn off RX and/or TX mode before changing
|
||||
any associated control bits. */
|
||||
if (temp_disable)
|
||||
V850E_UART_CONFIG (chan) = old_config & ~temp_disable;
|
||||
|
||||
/* Write the new control bits, while RX/TX are disabled. */
|
||||
if (changed_bits & ~enable)
|
||||
V850E_UART_CONFIG (chan) = new_config & ~enable;
|
||||
|
||||
v850e_uart_config_delay (new_config, new_speed);
|
||||
|
||||
/* Write the final version, with enable bits turned on. */
|
||||
V850E_UART_CONFIG (chan) = new_config;
|
||||
}
|
||||
|
||||
local_irq_restore (flags);
|
||||
}
|
||||
|
||||
|
||||
/* Low-level console. */
|
||||
|
||||
#ifdef CONFIG_V850E_UART_CONSOLE
|
||||
|
||||
static void v850e_uart_cons_write (struct console *co,
|
||||
const char *s, unsigned count)
|
||||
{
|
||||
if (count > 0) {
|
||||
unsigned chan = co->index;
|
||||
unsigned irq = V850E_UART_TX_IRQ (chan);
|
||||
int irq_was_enabled, irq_was_pending, flags;
|
||||
|
||||
/* We don't want to get `transmission completed'
|
||||
interrupts, since we're busy-waiting, so we disable them
|
||||
while sending (we don't disable interrupts entirely
|
||||
because sending over a serial line is really slow). We
|
||||
save the status of the tx interrupt and restore it when
|
||||
we're done so that using printk doesn't interfere with
|
||||
normal serial transmission (other than interleaving the
|
||||
output, of course!). This should work correctly even if
|
||||
this function is interrupted and the interrupt printks
|
||||
something. */
|
||||
|
||||
/* Disable interrupts while fiddling with tx interrupt. */
|
||||
local_irq_save (flags);
|
||||
/* Get current tx interrupt status. */
|
||||
irq_was_enabled = v850e_intc_irq_enabled (irq);
|
||||
irq_was_pending = v850e_intc_irq_pending (irq);
|
||||
/* Disable tx interrupt if necessary. */
|
||||
if (irq_was_enabled)
|
||||
v850e_intc_disable_irq (irq);
|
||||
/* Turn interrupts back on. */
|
||||
local_irq_restore (flags);
|
||||
|
||||
/* Send characters. */
|
||||
while (count > 0) {
|
||||
int ch = *s++;
|
||||
|
||||
if (ch == '\n') {
|
||||
/* We don't have the benefit of a tty
|
||||
driver, so translate NL into CR LF. */
|
||||
v850e_uart_wait_for_xmit_ok (chan);
|
||||
v850e_uart_putc (chan, '\r');
|
||||
}
|
||||
|
||||
v850e_uart_wait_for_xmit_ok (chan);
|
||||
v850e_uart_putc (chan, ch);
|
||||
|
||||
count--;
|
||||
}
|
||||
|
||||
/* Restore saved tx interrupt status. */
|
||||
if (irq_was_enabled) {
|
||||
/* Wait for the last character we sent to be
|
||||
completely transmitted (as we'll get an
|
||||
interrupt interrupt at that point). */
|
||||
v850e_uart_wait_for_xmit_done (chan);
|
||||
/* Clear pending interrupts received due
|
||||
to our transmission, unless there was already
|
||||
one pending, in which case we want the
|
||||
handler to be called. */
|
||||
if (! irq_was_pending)
|
||||
v850e_intc_clear_pending_irq (irq);
|
||||
/* ... and then turn back on handling. */
|
||||
v850e_intc_enable_irq (irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern struct uart_driver v850e_uart_driver;
|
||||
static struct console v850e_uart_cons =
|
||||
{
|
||||
.name = "ttyS",
|
||||
.write = v850e_uart_cons_write,
|
||||
.device = uart_console_device,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.cflag = V850E_UART_INIT_CFLAGS,
|
||||
.index = -1,
|
||||
.data = &v850e_uart_driver,
|
||||
};
|
||||
|
||||
void v850e_uart_cons_init (unsigned chan)
|
||||
{
|
||||
v850e_uart_configure (chan, V850E_UART_INIT_CFLAGS,
|
||||
V850E_UART_INIT_BAUD);
|
||||
v850e_uart_cons.index = chan;
|
||||
register_console (&v850e_uart_cons);
|
||||
printk ("Console: %s on-chip UART channel %d\n",
|
||||
V850E_UART_CHIP_NAME, chan);
|
||||
}
|
||||
|
||||
/* This is what the init code actually calls. */
|
||||
static int v850e_uart_console_init (void)
|
||||
{
|
||||
v850e_uart_cons_init (V850E_UART_CONSOLE_CHANNEL);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(v850e_uart_console_init);
|
||||
|
||||
#define V850E_UART_CONSOLE &v850e_uart_cons
|
||||
|
||||
#else /* !CONFIG_V850E_UART_CONSOLE */
|
||||
#define V850E_UART_CONSOLE 0
|
||||
#endif /* CONFIG_V850E_UART_CONSOLE */
|
||||
|
||||
/* TX/RX interrupt handlers. */
|
||||
|
||||
static void v850e_uart_stop_tx (struct uart_port *port);
|
||||
|
||||
void v850e_uart_tx (struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
int stopped = uart_tx_stopped (port);
|
||||
|
||||
if (v850e_uart_xmit_ok (port->line)) {
|
||||
int tx_ch;
|
||||
|
||||
if (port->x_char) {
|
||||
tx_ch = port->x_char;
|
||||
port->x_char = 0;
|
||||
} else if (!uart_circ_empty (xmit) && !stopped) {
|
||||
tx_ch = xmit->buf[xmit->tail];
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
} else
|
||||
goto no_xmit;
|
||||
|
||||
v850e_uart_putc (port->line, tx_ch);
|
||||
port->icount.tx++;
|
||||
|
||||
if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup (port);
|
||||
}
|
||||
|
||||
no_xmit:
|
||||
if (uart_circ_empty (xmit) || stopped)
|
||||
v850e_uart_stop_tx (port, stopped);
|
||||
}
|
||||
|
||||
static irqreturn_t v850e_uart_tx_irq(int irq, void *data)
|
||||
{
|
||||
struct uart_port *port = data;
|
||||
v850e_uart_tx (port);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t v850e_uart_rx_irq(int irq, void *data)
|
||||
{
|
||||
struct uart_port *port = data;
|
||||
unsigned ch_stat = TTY_NORMAL;
|
||||
unsigned ch = v850e_uart_getc (port->line);
|
||||
unsigned err = v850e_uart_err (port->line);
|
||||
|
||||
if (err) {
|
||||
if (err & V850E_UART_ERR_OVERRUN) {
|
||||
ch_stat = TTY_OVERRUN;
|
||||
port->icount.overrun++;
|
||||
} else if (err & V850E_UART_ERR_FRAME) {
|
||||
ch_stat = TTY_FRAME;
|
||||
port->icount.frame++;
|
||||
} else if (err & V850E_UART_ERR_PARITY) {
|
||||
ch_stat = TTY_PARITY;
|
||||
port->icount.parity++;
|
||||
}
|
||||
}
|
||||
|
||||
port->icount.rx++;
|
||||
|
||||
tty_insert_flip_char (port->info->tty, ch, ch_stat);
|
||||
tty_schedule_flip (port->info->tty);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/* Control functions for the serial framework. */
|
||||
|
||||
static void v850e_uart_nop (struct uart_port *port) { }
|
||||
static int v850e_uart_success (struct uart_port *port) { return 0; }
|
||||
|
||||
static unsigned v850e_uart_tx_empty (struct uart_port *port)
|
||||
{
|
||||
return TIOCSER_TEMT; /* Can't detect. */
|
||||
}
|
||||
|
||||
static void v850e_uart_set_mctrl (struct uart_port *port, unsigned mctrl)
|
||||
{
|
||||
#ifdef V850E_UART_SET_RTS
|
||||
V850E_UART_SET_RTS (port->line, (mctrl & TIOCM_RTS));
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned v850e_uart_get_mctrl (struct uart_port *port)
|
||||
{
|
||||
/* We don't support DCD or DSR, so consider them permanently active. */
|
||||
int mctrl = TIOCM_CAR | TIOCM_DSR;
|
||||
|
||||
/* We may support CTS. */
|
||||
#ifdef V850E_UART_CTS
|
||||
mctrl |= V850E_UART_CTS(port->line) ? TIOCM_CTS : 0;
|
||||
#else
|
||||
mctrl |= TIOCM_CTS;
|
||||
#endif
|
||||
|
||||
return mctrl;
|
||||
}
|
||||
|
||||
static void v850e_uart_start_tx (struct uart_port *port)
|
||||
{
|
||||
v850e_intc_disable_irq (V850E_UART_TX_IRQ (port->line));
|
||||
v850e_uart_tx (port);
|
||||
v850e_intc_enable_irq (V850E_UART_TX_IRQ (port->line));
|
||||
}
|
||||
|
||||
static void v850e_uart_stop_tx (struct uart_port *port)
|
||||
{
|
||||
v850e_intc_disable_irq (V850E_UART_TX_IRQ (port->line));
|
||||
}
|
||||
|
||||
static void v850e_uart_start_rx (struct uart_port *port)
|
||||
{
|
||||
v850e_intc_enable_irq (V850E_UART_RX_IRQ (port->line));
|
||||
}
|
||||
|
||||
static void v850e_uart_stop_rx (struct uart_port *port)
|
||||
{
|
||||
v850e_intc_disable_irq (V850E_UART_RX_IRQ (port->line));
|
||||
}
|
||||
|
||||
static void v850e_uart_break_ctl (struct uart_port *port, int break_ctl)
|
||||
{
|
||||
/* Umm, do this later. */
|
||||
}
|
||||
|
||||
static int v850e_uart_startup (struct uart_port *port)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Alloc RX irq. */
|
||||
err = request_irq (V850E_UART_RX_IRQ (port->line), v850e_uart_rx_irq,
|
||||
IRQF_DISABLED, "v850e_uart", port);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Alloc TX irq. */
|
||||
err = request_irq (V850E_UART_TX_IRQ (port->line), v850e_uart_tx_irq,
|
||||
IRQF_DISABLED, "v850e_uart", port);
|
||||
if (err) {
|
||||
free_irq (V850E_UART_RX_IRQ (port->line), port);
|
||||
return err;
|
||||
}
|
||||
|
||||
v850e_uart_start_rx (port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void v850e_uart_shutdown (struct uart_port *port)
|
||||
{
|
||||
/* Disable port interrupts. */
|
||||
free_irq (V850E_UART_TX_IRQ (port->line), port);
|
||||
free_irq (V850E_UART_RX_IRQ (port->line), port);
|
||||
|
||||
/* Turn off xmit/recv enable bits. */
|
||||
V850E_UART_CONFIG (port->line)
|
||||
&= ~(V850E_UART_CONFIG_TX_ENABLE
|
||||
| V850E_UART_CONFIG_RX_ENABLE);
|
||||
/* Then reset the channel. */
|
||||
V850E_UART_CONFIG (port->line) = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
v850e_uart_set_termios (struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned cflags = termios->c_cflag;
|
||||
|
||||
/* Restrict flags to legal values. */
|
||||
if ((cflags & CSIZE) != CS7 && (cflags & CSIZE) != CS8)
|
||||
/* The new value of CSIZE is invalid, use the old value. */
|
||||
cflags = (cflags & ~CSIZE)
|
||||
| (old ? (old->c_cflag & CSIZE) : CS8);
|
||||
|
||||
termios->c_cflag = cflags;
|
||||
|
||||
v850e_uart_configure (port->line, cflags,
|
||||
uart_get_baud_rate (port, termios, old,
|
||||
v850e_uart_min_baud(),
|
||||
v850e_uart_max_baud()));
|
||||
}
|
||||
|
||||
static const char *v850e_uart_type (struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_V850E_UART ? "v850e_uart" : 0;
|
||||
}
|
||||
|
||||
static void v850e_uart_config_port (struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE)
|
||||
port->type = PORT_V850E_UART;
|
||||
}
|
||||
|
||||
static int
|
||||
v850e_uart_verify_port (struct uart_port *port, struct serial_struct *ser)
|
||||
{
|
||||
if (ser->type != PORT_UNKNOWN && ser->type != PORT_V850E_UART)
|
||||
return -EINVAL;
|
||||
if (ser->irq != V850E_UART_TX_IRQ (port->line))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct uart_ops v850e_uart_ops = {
|
||||
.tx_empty = v850e_uart_tx_empty,
|
||||
.get_mctrl = v850e_uart_get_mctrl,
|
||||
.set_mctrl = v850e_uart_set_mctrl,
|
||||
.start_tx = v850e_uart_start_tx,
|
||||
.stop_tx = v850e_uart_stop_tx,
|
||||
.stop_rx = v850e_uart_stop_rx,
|
||||
.enable_ms = v850e_uart_nop,
|
||||
.break_ctl = v850e_uart_break_ctl,
|
||||
.startup = v850e_uart_startup,
|
||||
.shutdown = v850e_uart_shutdown,
|
||||
.set_termios = v850e_uart_set_termios,
|
||||
.type = v850e_uart_type,
|
||||
.release_port = v850e_uart_nop,
|
||||
.request_port = v850e_uart_success,
|
||||
.config_port = v850e_uart_config_port,
|
||||
.verify_port = v850e_uart_verify_port,
|
||||
};
|
||||
|
||||
/* Initialization and cleanup. */
|
||||
|
||||
static struct uart_driver v850e_uart_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "v850e_uart",
|
||||
.dev_name = "ttyS",
|
||||
.major = TTY_MAJOR,
|
||||
.minor = V850E_UART_MINOR_BASE,
|
||||
.nr = V850E_UART_NUM_CHANNELS,
|
||||
.cons = V850E_UART_CONSOLE,
|
||||
};
|
||||
|
||||
|
||||
static struct uart_port v850e_uart_ports[V850E_UART_NUM_CHANNELS];
|
||||
|
||||
static int __init v850e_uart_init (void)
|
||||
{
|
||||
int rval;
|
||||
|
||||
printk (KERN_INFO "%s on-chip UART\n", V850E_UART_CHIP_NAME);
|
||||
|
||||
rval = uart_register_driver (&v850e_uart_driver);
|
||||
if (rval == 0) {
|
||||
unsigned chan;
|
||||
|
||||
for (chan = 0; chan < V850E_UART_NUM_CHANNELS; chan++) {
|
||||
struct uart_port *port = &v850e_uart_ports[chan];
|
||||
|
||||
memset (port, 0, sizeof *port);
|
||||
|
||||
port->ops = &v850e_uart_ops;
|
||||
port->line = chan;
|
||||
port->iotype = UPIO_MEM;
|
||||
port->flags = UPF_BOOT_AUTOCONF;
|
||||
|
||||
/* We actually use multiple IRQs, but the serial
|
||||
framework seems to mainly use this for
|
||||
informational purposes anyway. Here we use the TX
|
||||
irq. */
|
||||
port->irq = V850E_UART_TX_IRQ (chan);
|
||||
|
||||
/* The serial framework doesn't really use these
|
||||
membase/mapbase fields for anything useful, but
|
||||
it requires that they be something non-zero to
|
||||
consider the port `valid', and also uses them
|
||||
for informational purposes. */
|
||||
port->membase = (void *)V850E_UART_BASE_ADDR (chan);
|
||||
port->mapbase = V850E_UART_BASE_ADDR (chan);
|
||||
|
||||
/* The framework insists on knowing the uart's master
|
||||
clock freq, though it doesn't seem to do anything
|
||||
useful for us with it. We must make it at least
|
||||
higher than (the maximum baud rate * 16), otherwise
|
||||
the framework will puke during its internal
|
||||
calculations, and force the baud rate to be 9600.
|
||||
To be accurate though, just repeat the calculation
|
||||
we use when actually setting the speed. */
|
||||
port->uartclk = v850e_uart_max_clock() * 16;
|
||||
|
||||
uart_add_one_port (&v850e_uart_driver, port);
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static void __exit v850e_uart_exit (void)
|
||||
{
|
||||
unsigned chan;
|
||||
|
||||
for (chan = 0; chan < V850E_UART_NUM_CHANNELS; chan++)
|
||||
uart_remove_one_port (&v850e_uart_driver,
|
||||
&v850e_uart_ports[chan]);
|
||||
|
||||
uart_unregister_driver (&v850e_uart_driver);
|
||||
}
|
||||
|
||||
module_init (v850e_uart_init);
|
||||
module_exit (v850e_uart_exit);
|
||||
|
||||
MODULE_AUTHOR ("Miles Bader");
|
||||
MODULE_DESCRIPTION ("NEC " V850E_UART_CHIP_NAME " on-chip UART");
|
||||
MODULE_LICENSE ("GPL");
|
||||
1042
drivers/serial/vr41xx_siu.c
Normal file
1042
drivers/serial/vr41xx_siu.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user