Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

513
drivers/serial/21285.c Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

2817
drivers/serial/8250.c Normal file

File diff suppressed because it is too large Load Diff

80
drivers/serial/8250.h Normal file
View 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

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

View 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");

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

View 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");

View 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
View 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
View 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");

View 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
View 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

File diff suppressed because it is too large Load Diff

518
drivers/serial/8250_pnp.c Normal file
View 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

File diff suppressed because it is too large Load Diff

63
drivers/serial/Makefile Normal file
View 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
View 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
View 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");

View 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");

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

View 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)

View 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 */

File diff suppressed because it is too large Load Diff

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

View 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

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

View 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

File diff suppressed because it is too large Load Diff

136
drivers/serial/crisv10.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

287
drivers/serial/icom.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

281
drivers/serial/ip22zilog.h Normal file
View 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 */

View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

54
drivers/serial/m32r_sio.h Normal file
View 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

View 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

File diff suppressed because it is too large Load Diff

View 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 */

File diff suppressed because it is too large Load Diff

2091
drivers/serial/mpsc.c Normal file

File diff suppressed because it is too large Load Diff

634
drivers/serial/mux.c Normal file
View 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);

View 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
View 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

File diff suppressed because it is too large Load Diff

382
drivers/serial/pmac_zilog.h Normal file
View 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__ */

View 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
View 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

File diff suppressed because it is too large Load Diff

1679
drivers/serial/s3c6400.c Normal file

File diff suppressed because it is too large Load Diff

919
drivers/serial/sa1100.c Normal file
View 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

File diff suppressed because it is too large Load Diff

975
drivers/serial/serial_cs.c Normal file
View 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, &reg);
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, &reg);
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");

View 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

File diff suppressed because it is too large Load Diff

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
View 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

File diff suppressed because it is too large Load Diff

247
drivers/serial/suncore.c Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

322
drivers/serial/sunsab.h Normal file
View 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

File diff suppressed because it is too large Load Diff

1534
drivers/serial/sunzilog.c Normal file

File diff suppressed because it is too large Load Diff

272
drivers/serial/sunzilog.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff