Commit 33c0d1b0 authored by Russell King's avatar Russell King Committed by Linus Torvalds

[PATCH] Serial driver stuff

The serial layer is restructured to allow less code duplication (and
hence bug duplication) across various serial drivers.  Since ARM adds
six extra serial drivers, maintaining six copies of serial.c was not
my idea of fun.

Therefore, we've ended up with a core serial driver, which knows about
the interactions with the tty layer, and low-level hardware drivers,
which know all about the hardware.  The interface between the two is
described in "Documentation/serial/driver".

This patch completely removes the old serial.c driver and its associated
configuration options, as you requested at KS2002.  We keep a certain
amount of configuration compatibility with the per-architecture serial.h
file for the moment; this *will* be killed in the next round of patches.
The biggest user of this is x86, and since I don't have an x86 box to
test this stuff on, I think the changes are best kept separate.
parent ae86a80a
Low Level Serial API
--------------------
$Id: driver,v 1.9 2002/07/06 16:51:43 rmk Exp $
This document is meant as a brief overview of some aspects of the new serial
driver. It is not complete, any questions you have should be directed to
<rmk@arm.linux.org.uk>
The reference implementation is contained within serial_amba.c.
Low Level Serial Hardware Driver
--------------------------------
The low level serial hardware driver is responsible for supplying port
information (defined by uart_port) and a set of control methods (defined
by uart_ops) to the core serial driver. The low level driver is also
responsible for handling interrupts for the port, and providing any
console support.
Console Support
---------------
The serial core provides a few helper functions. This includes identifing
the correct port structure (via uart_get_console) and decoding command line
arguments (uart_parse_options).
Locking
-------
It is the responsibility of the low level hardware driver to perform the
necessary locking using port->lock. There are some exceptions (which
are described in the uart_ops listing below.)
There are three locks. A per-port spinlock, a per-port tmpbuf semaphore,
and an overall semaphore.
From the core driver perspective, the port->lock locks the following
data:
port->mctrl
port->icount
info->xmit.head (circ->head)
info->xmit.tail (circ->tail)
The low level driver is free to use this lock to provide any additional
locking.
The core driver uses the info->tmpbuf_sem lock to prevent multi-threaded
access to the info->tmpbuf bouncebuffer used for port writes.
The port_sem semaphore is used to protect against ports being added/
removed or reconfigured at inappropriate times.
uart_ops
--------
The uart_ops structure is the main interface between serial_core and the
hardware specific driver. It contains all the methods to control the
hardware.
tx_empty(port)
This function tests whether the transmitter fifo and shifter
for the port described by 'port' is empty. If it is empty,
this function should return TIOCSER_TEMT, otherwise return 0.
If the port does not support this operation, then it should
return TIOCSER_TEMT.
Locking: none.
Interrupts: caller dependent.
This call must not sleep
set_mctrl(port, mctrl)
This function sets the modem control lines for port described
by 'port' to the state described by mctrl. The relevant bits
of mctrl are:
- TIOCM_RTS RTS signal.
- TIOCM_DTR DTR signal.
- TIOCM_OUT1 OUT1 signal.
- TIOCM_OUT2 OUT2 signal.
If the appropriate bit is set, the signal should be driven
active. If the bit is clear, the signal should be driven
inactive.
Locking: port->lock taken.
Interrupts: locally disabled.
This call must not sleep
get_mctrl(port)
Returns the current state of modem control inputs. The state
of the outputs should not be returned, since the core keeps
track of their state. The state information should include:
- TIOCM_DCD state of DCD signal
- TIOCM_CTS state of CTS signal
- TIOCM_DSR state of DSR signal
- TIOCM_RI state of RI signal
The bit is set if the signal is currently driven active. If
the port does not support CTS, DCD or DSR, the driver should
indicate that the signal is permanently active. If RI is
not available, the signal should not be indicated as active.
Locking: none.
Interrupts: caller dependent.
This call must not sleep
stop_tx(port,tty_stop)
Stop transmitting characters. This might be due to the CTS
line becoming inactive or the tty layer indicating we want
to stop transmission.
tty_stop: 1 if this call is due to the TTY layer issuing a
TTY stop to the driver (equiv to rs_stop).
Locking: none.
Interrupts: caller dependent.
This call must not sleep
start_tx(port,tty_start)
start transmitting characters. (incidentally, nonempty will
always be nonzero, and shouldn't be used - it will be dropped).
tty_start: 1 if this call was due to the TTY layer issuing
a TTY start to the driver (equiv to rs_start)
Locking: port->lock taken.
Interrupts: locally disabled.
This call must not sleep
stop_rx(port)
Stop receiving characters; the port is in the process of
being closed.
Locking: none.
Interrupts: caller dependent.
This call must not sleep
enable_ms(port)
Enable the modem status interrupts.
Locking: none.
Interrupts: caller dependent.
break_ctl(port,ctl)
Control the transmission of a break signal. If ctl is
nonzero, the break signal should be transmitted. The signal
should be terminated when another call is made with a zero
ctl.
Locking: none.
Interrupts: caller dependent.
This call must not sleep
startup(port)
Grab any interrupt resources and initialise any low level driver
state. Enable the port for reception. It should not activate
RTS nor DTR; this will be done via a separate call to set_mctrl.
Locking: port_sem taken.
Interrupts: globally disabled.
shutdown(port)
Disable the port, disable any break condition that may be in
effect, and free any interrupt resources. It should not disable
RTS nor DTR; this will have already been done via a separate
call to set_mctrl.
Locking: port_sem taken.
Interrupts: caller dependent.
change_speed(port,cflag,iflag,quot)
Change the port parameters, including word length, parity, stop
bits. Update read_status_mask and ignore_status_mask to indicate
the types of events we are interested in receiving. Relevant
cflag bits are:
CSIZE - word size
CSTOPB - 2 stop bits
PARENB - parity enable
PARODD - odd parity (when PARENB is in force)
CREAD - enable reception of characters (if not set,
still receive characters from the port, but
throw them away.
CRTSCTS - if set, enable CTS status change reporting
CLOCAL - if not set, enable modem status change
reporting.
Relevant iflag bits are:
INPCK - enable frame and parity error events to be
passed to the TTY layer.
BRKINT
PARMRK - both of these enable break events to be
passed to the TTY layer.
IGNPAR - ignore parity and framing errors
IGNBRK - ignore break errors, If IGNPAR is also
set, ignore overrun errors as well.
The interaction of the iflag bits is as follows (parity error
given as an example):
Parity error INPCK IGNPAR
None n/a n/a character received
Yes n/a 0 character discarded
Yes 0 1 character received, marked as
TTY_NORMAL
Yes 1 1 character received, marked as
TTY_PARITY
Locking: none.
Interrupts: caller dependent.
This call must not sleep
pm(port,state,oldstate)
perform any power management related activities on the specified
port. state indicates the new state (defined by ACPI D0-D3),
oldstate indicates the previous state. Essentially, D0 means
fully on, D3 means powered down.
This function should not be used to grab any resources.
Locking: none.
Interrupts: caller dependent.
type(port)
Return a pointer to a string constant describing the specified
port, or return NULL, in which case the string 'unknown' is
substituted.
Locking: none.
Interrupts: caller dependent.
release_port(port)
Release any memory and IO region resources currently in use by
the port.
Locking: none.
Interrupts: caller dependent.
request_port(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.
Locking: none.
Interrupts: caller dependent.
config_port(port,type)
Perform any autoconfiguration steps required for the port. `type`
contains a bit mask of the required configuration. UART_CONFIG_TYPE
indicates that the port requires detection and identification.
port->type should be set to the type found, or PORT_UNKNOWN if
no port was detected.
UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal,
which should be probed using standard kernel autoprobing techniques.
This is not necessary on platforms where ports have interrupts
internally hard wired (eg, system on a chip implementations).
Locking: none.
Interrupts: caller dependent.
verify_port(port,serinfo)
Verify the new serial port information contained within serinfo is
suitable for this port type.
Locking: none.
Interrupts: caller dependent.
ioctl(port,cmd,arg)
Perform any port specific IOCTLs. IOCTL commands must be defined
using the standard numbering system found in <asm/ioctl.h>
Locking: none.
Interrupts: caller dependent.
Other notes
-----------
It is intended some day to drop the 'unused' entries from uart_port, and
allow low level drivers to register their own individual uart_port's with
the core. This will allow drivers to use uart_port as a pointer to a
structure containing both the uart_port entry with their own extensions,
thus:
struct my_port {
struct uart_port port;
int my_stuff;
};
Todo
----
Please see the BUGS file in CVS at
http://cvs.arm.linux.org.uk/cgi/viewcvs.cgi/serial/BUGS
...@@ -212,7 +212,7 @@ export MODLIB ...@@ -212,7 +212,7 @@ export MODLIB
CPPFLAGS := -D__KERNEL__ -I$(HPATH) CPPFLAGS := -D__KERNEL__ -I$(HPATH)
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -g \
-fomit-frame-pointer -fno-strict-aliasing -fno-common -fomit-frame-pointer -fno-strict-aliasing -fno-common
AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_ACPI) += acpi/ obj-$(CONFIG_ACPI) += acpi/
obj-$(CONFIG_PARPORT) += parport/ obj-$(CONFIG_PARPORT) += parport/
obj-y += base/ char/ block/ misc/ net/ media/ obj-y += base/ serial/ char/ block/ misc/ net/ media/
obj-$(CONFIG_NUBUS) += nubus/ obj-$(CONFIG_NUBUS) += nubus/
obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_IDE) += ide/ obj-$(CONFIG_IDE) += ide/
......
...@@ -8,25 +8,6 @@ bool 'Virtual terminal' CONFIG_VT ...@@ -8,25 +8,6 @@ bool 'Virtual terminal' CONFIG_VT
if [ "$CONFIG_VT" = "y" ]; then if [ "$CONFIG_VT" = "y" ]; then
bool ' Support for console on virtual terminal' CONFIG_VT_CONSOLE bool ' Support for console on virtual terminal' CONFIG_VT_CONSOLE
fi fi
tristate 'Standard/generic (8250/16550 and compatible UARTs) serial support' CONFIG_SERIAL
if [ "$CONFIG_SERIAL" = "y" ]; then
bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE
if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL
tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL
fi
fi
if [ "$CONFIG_ACPI" = "y" -a "$CONFIG_IA64" = "y" ]; then
bool ' Support for serial ports defined by ACPI tables' CONFIG_SERIAL_ACPI
fi
dep_mbool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED $CONFIG_SERIAL
if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS
bool ' Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ
bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_DETECT_IRQ
bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT
bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6
fi
bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
tristate ' Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate ' Computone IntelliPort Plus serial support' CONFIG_COMPUTONE
...@@ -85,15 +66,9 @@ fi ...@@ -85,15 +66,9 @@ fi
if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then
tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232 tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232
fi fi
if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
bool 'DC21285 serial port support' CONFIG_SERIAL_21285 source drivers/serial/Config.in
if [ "$CONFIG_SERIAL_21285" = "y" ]; then
if [ "$CONFIG_OBSOLETE" = "y" ]; then
bool ' Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD
fi
bool ' Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE
fi
fi
bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
......
...@@ -13,20 +13,18 @@ obj-y += mem.o tty_io.o n_tty.o tty_ioctl.o raw.o pty.o misc.o random.o ...@@ -13,20 +13,18 @@ obj-y += mem.o tty_io.o n_tty.o tty_ioctl.o raw.o pty.o misc.o random.o
# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
export-objs := busmouse.o console.o keyboard.o sysrq.o \ export-objs := busmouse.o console.o keyboard.o sysrq.o \
misc.o pty.o random.o selection.o serial.o \ misc.o pty.o random.o selection.o \
sonypi.o tty_io.o tty_ioctl.o generic_serial.o rtc.o \ sonypi.o tty_io.o tty_ioctl.o generic_serial.o rtc.o \
ip2main.o ip2main.o
KEYMAP =defkeymap.o KEYMAP =defkeymap.o
KEYBD =pc_keyb.o KEYBD =pc_keyb.o
CONSOLE =console.o CONSOLE =console.o
SERIAL =serial.o
ifeq ($(ARCH),s390) ifeq ($(ARCH),s390)
KEYMAP = KEYMAP =
KEYBD = KEYBD =
CONSOLE = CONSOLE =
SERIAL =
endif endif
ifeq ($(ARCH),mips) ifeq ($(ARCH),mips)
...@@ -39,7 +37,6 @@ ifeq ($(ARCH),s390x) ...@@ -39,7 +37,6 @@ ifeq ($(ARCH),s390x)
KEYMAP = KEYMAP =
KEYBD = KEYBD =
CONSOLE = CONSOLE =
SERIAL =
endif endif
ifeq ($(ARCH),m68k) ifeq ($(ARCH),m68k)
...@@ -48,7 +45,6 @@ ifeq ($(ARCH),m68k) ...@@ -48,7 +45,6 @@ ifeq ($(ARCH),m68k)
else else
KEYBD = KEYBD =
endif endif
SERIAL =
endif endif
ifeq ($(ARCH),arm) ifeq ($(ARCH),arm)
...@@ -96,15 +92,6 @@ endif ...@@ -96,15 +92,6 @@ endif
ifeq ($(CONFIG_BAGET_MIPS),y) ifeq ($(CONFIG_BAGET_MIPS),y)
KEYBD = KEYBD =
SERIAL =
endif
ifeq ($(CONFIG_NINO),y)
SERIAL =
endif
ifneq ($(CONFIG_SUN_SERIAL),)
SERIAL =
endif endif
ifeq ($(CONFIG_QTRONIX_KEYBOARD),y) ifeq ($(CONFIG_QTRONIX_KEYBOARD),y)
...@@ -113,11 +100,7 @@ ifeq ($(CONFIG_QTRONIX_KEYBOARD),y) ...@@ -113,11 +100,7 @@ ifeq ($(CONFIG_QTRONIX_KEYBOARD),y)
endif endif
obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o
obj-$(CONFIG_SERIAL) += $(SERIAL) #obj-$(CONFIG_SERIAL) += $(SERIAL) # Fix for decserial.o
obj-$(CONFIG_SERIAL_ACPI) += acpi_serial.o
obj-$(CONFIG_SERIAL_21285) += serial_21285.o
obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o
obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o
ifndef CONFIG_SUN_KEYBOARD ifndef CONFIG_SUN_KEYBOARD
obj-$(CONFIG_VT) += keyboard.o $(KEYMAP) $(KEYBD) obj-$(CONFIG_VT) += keyboard.o $(KEYMAP) $(KEYBD)
......
/*
* linux/drivers/char/acpi_serial.c
*
* Copyright (C) 2000 Hewlett-Packard Co.
* Copyright (C) 2000 Khalid Aziz <khalid_aziz@hp.com>
*
* Detect and initialize the headless console serial port defined in
* SPCR table and debug serial port defined in DBGP table
*
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <asm/serial.h>
#include <asm/io.h>
#include <linux/acpi_serial.h>
/*#include <asm/acpi-ext.h>*/
#undef SERIAL_DEBUG_ACPI
/*
* Query ACPI tables for a debug and a headless console serial
* port. If found, add them to rs_table[]. A pointer to either SPCR
* or DBGP table is passed as parameter. This function should be called
* before serial_console_init() is called to make sure the SPCR serial
* console will be available for use. IA-64 kernel calls this function
* from within acpi.c when it encounters SPCR or DBGP tables as it parses
* the ACPI 2.0 tables during bootup.
*
*/
void __init setup_serial_acpi(void *tablep)
{
acpi_ser_t *acpi_ser_p;
struct serial_struct serial_req;
unsigned long iobase;
int global_sys_irq;
#ifdef SERIAL_DEBUG_ACPI
printk("Entering setup_serial_acpi()\n");
#endif
/* Now get the table */
if (tablep == NULL) {
return;
}
acpi_ser_p = (acpi_ser_t *)tablep;
/*
* Perform a sanity check on the table. Table should have a
* signature of "SPCR" or "DBGP" and it should be atleast 52 bytes
* long.
*/
if ((strncmp(acpi_ser_p->signature, ACPI_SPCRT_SIGNATURE,
ACPI_SIG_LEN) != 0) &&
(strncmp(acpi_ser_p->signature, ACPI_DBGPT_SIGNATURE,
ACPI_SIG_LEN) != 0)) {
return;
}
if (acpi_ser_p->length < 52) {
return;
}
iobase = (((u64) acpi_ser_p->base_addr.addrh) << 32) | acpi_ser_p->base_addr.addrl;
global_sys_irq = (acpi_ser_p->global_int[3] << 24) |
(acpi_ser_p->global_int[2] << 16) |
(acpi_ser_p->global_int[1] << 8) |
acpi_ser_p->global_int[0];
#ifdef SERIAL_DEBUG_ACPI
printk("setup_serial_acpi(): table pointer = 0x%p\n", acpi_ser_p);
printk(" sig = '%c%c%c%c'\n",
acpi_ser_p->signature[0],
acpi_ser_p->signature[1],
acpi_ser_p->signature[2],
acpi_ser_p->signature[3]);
printk(" length = %d\n", acpi_ser_p->length);
printk(" Rev = %d\n", acpi_ser_p->rev);
printk(" Interface type = %d\n", acpi_ser_p->intfc_type);
printk(" Base address = 0x%lX\n", iobase);
printk(" IRQ = %d\n", acpi_ser_p->irq);
printk(" Global System Int = %d\n", global_sys_irq);
printk(" Baud rate = ");
switch (acpi_ser_p->baud) {
case ACPI_SERIAL_BAUD_9600:
printk("9600\n");
break;
case ACPI_SERIAL_BAUD_19200:
printk("19200\n");
break;
case ACPI_SERIAL_BAUD_57600:
printk("57600\n");
break;
case ACPI_SERIAL_BAUD_115200:
printk("115200\n");
break;
default:
printk("Huh (%d)\n", acpi_ser_p->baud);
break;
}
if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_PCICONF_SPACE) {
printk(" PCI serial port:\n");
printk(" Bus %d, Device %d, Vendor ID 0x%x, Dev ID 0x%x\n",
acpi_ser_p->pci_bus, acpi_ser_p->pci_dev,
acpi_ser_p->pci_vendor_id, acpi_ser_p->pci_dev_id);
}
#endif
/*
* Now build a serial_req structure to update the entry in
* rs_table for the headless console port.
*/
switch (acpi_ser_p->intfc_type) {
case ACPI_SERIAL_INTFC_16550:
serial_req.type = PORT_16550;
serial_req.baud_base = BASE_BAUD;
break;
case ACPI_SERIAL_INTFC_16450:
serial_req.type = PORT_16450;
serial_req.baud_base = BASE_BAUD;
break;
default:
serial_req.type = PORT_UNKNOWN;
break;
}
if (strncmp(acpi_ser_p->signature, ACPI_SPCRT_SIGNATURE,
ACPI_SIG_LEN) == 0) {
serial_req.line = ACPI_SERIAL_CONSOLE_PORT;
}
else if (strncmp(acpi_ser_p->signature, ACPI_DBGPT_SIGNATURE,
ACPI_SIG_LEN) == 0) {
serial_req.line = ACPI_SERIAL_DEBUG_PORT;
}
/*
* Check if this is an I/O mapped address or a memory mapped address
*/
if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_MEM_SPACE) {
serial_req.port = 0;
serial_req.port_high = 0;
serial_req.iomem_base = (void *)ioremap(iobase, 64);
serial_req.io_type = SERIAL_IO_MEM;
}
else if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_IO_SPACE) {
serial_req.port = (unsigned long) iobase & 0xffffffff;
serial_req.port_high = (unsigned long)(((u64)iobase) >> 32);
serial_req.iomem_base = NULL;
serial_req.io_type = SERIAL_IO_PORT;
}
else if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_PCICONF_SPACE) {
printk("WARNING: No support for PCI serial console\n");
return;
}
/*
* If the table does not have IRQ information, use 0 for IRQ.
* This will force rs_init() to probe for IRQ.
*/
if (acpi_ser_p->length < 53) {
serial_req.irq = 0;
}
else {
serial_req.flags = ASYNC_SKIP_TEST | ASYNC_BOOT_AUTOCONF |
ASYNC_AUTO_IRQ;
if (acpi_ser_p->int_type &
(ACPI_SERIAL_INT_APIC | ACPI_SERIAL_INT_SAPIC)) {
serial_req.irq = global_sys_irq;
}
else if (acpi_ser_p->int_type & ACPI_SERIAL_INT_PCAT) {
serial_req.irq = acpi_ser_p->irq;
}
else {
/*
* IRQ type not being set would mean UART will
* run in polling mode. Do not probe for IRQ in
* that case.
*/
serial_req.flags = ASYNC_SKIP_TEST|ASYNC_BOOT_AUTOCONF;
}
}
serial_req.xmit_fifo_size = serial_req.custom_divisor = 0;
serial_req.close_delay = serial_req.hub6 = serial_req.closing_wait = 0;
serial_req.iomem_reg_shift = 0;
if (early_serial_setup(&serial_req) < 0) {
printk("early_serial_setup() for ACPI serial console port failed\n");
return;
}
#ifdef SERIAL_DEBUG_ACPI
printk("Leaving setup_serial_acpi()\n");
#endif
}
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
mainmenu_option next_comment mainmenu_option next_comment
comment 'PCMCIA character devices' comment 'PCMCIA character devices'
dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS $CONFIG_PCMCIA $CONFIG_SERIAL
dep_tristate 'SyncLink PC Card support' CONFIG_SYNCLINK_CS $CONFIG_PCMCIA dep_tristate 'SyncLink PC Card support' CONFIG_SYNCLINK_CS $CONFIG_PCMCIA
endmenu endmenu
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
# Makefile for the Linux PCMCIA char device drivers. # Makefile for the Linux PCMCIA char device drivers.
# #
obj-$(CONFIG_PCMCIA_SERIAL_CS) += serial_cs.o
obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
/*======================================================================
A driver for PCMCIA serial devices
serial_cs.c 1.123 2000/08/24 18:46:38
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/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <asm/io.h>
#include <asm/system.h>
#include <pcmcia/version.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>
#ifdef PCMCIA_DEBUG
static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
"serial_cs.c 1.123 2000/08/24 18:46:38 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
/*====================================================================*/
/* Parameters that can be set with 'insmod' */
/* Bit map of interrupts to choose from */
static u_int irq_mask = 0xdeb8;
static int irq_list[4] = { -1 };
/* Enable the speaker? */
static int do_sound = 1;
MODULE_PARM(irq_mask, "i");
MODULE_PARM(irq_list, "1-4i");
MODULE_PARM(do_sound, "i");
/*====================================================================*/
/* Table of multi-port card ID's */
typedef struct {
u_short manfid;
u_short prodid;
int multi; /* 1 = multifunction, > 1 = # ports */
} multi_id_t;
static multi_id_t multi_id[] = {
{ MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 },
{ MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 },
{ MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 },
{ MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 },
{ MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 },
{ MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 },
{ MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 }
};
#define MULTI_COUNT (sizeof(multi_id)/sizeof(multi_id_t))
typedef struct serial_info_t {
dev_link_t link;
int ndev;
int multi;
int slave;
int manfid;
dev_node_t node[4];
int line[4];
} serial_info_t;
static void serial_config(dev_link_t *link);
static void serial_release(u_long arg);
static int serial_event(event_t event, int priority,
event_callback_args_t *args);
static dev_info_t dev_info = "serial_cs";
static dev_link_t *serial_attach(void);
static void serial_detach(dev_link_t *);
static dev_link_t *dev_list = NULL;
/*====================================================================*/
static void cs_error(client_handle_t handle, int func, int ret)
{
error_info_t err = { func, ret };
CardServices(ReportError, handle, &err);
}
/*======================================================================
serial_attach() creates an "instance" of the driver, allocating
local data structures for one device. The device is registered
with Card Services.
======================================================================*/
static dev_link_t *serial_attach(void)
{
serial_info_t *info;
client_reg_t client_reg;
dev_link_t *link;
int i, ret;
DEBUG(0, "serial_attach()\n");
/* Create new serial device */
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) return NULL;
memset(info, 0, sizeof(*info));
link = &info->link; link->priv = info;
link->release.function = &serial_release;
link->release.data = (u_long)link;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts1 = 8;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
if (irq_list[0] == -1)
link->irq.IRQInfo2 = irq_mask;
else
for (i = 0; i < 4; i++)
link->irq.IRQInfo2 |= 1 << irq_list[i];
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.Vcc = 50;
if (do_sound) {
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
}
link->conf.IntType = INT_MEMORY_AND_IO;
/* Register with Card Services */
link->next = dev_list;
dev_list = link;
client_reg.dev_info = &dev_info;
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
client_reg.EventMask =
CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
client_reg.event_handler = &serial_event;
client_reg.Version = 0x0210;
client_reg.event_callback_args.client_data = link;
ret = CardServices(RegisterClient, &link->handle, &client_reg);
if (ret != CS_SUCCESS) {
cs_error(link->handle, RegisterClient, ret);
serial_detach(link);
return NULL;
}
return link;
} /* serial_attach */
/*======================================================================
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(dev_link_t *link)
{
serial_info_t *info = link->priv;
dev_link_t **linkp;
int ret;
DEBUG(0, "serial_detach(0x%p)\n", link);
/* Locate device structure */
for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
if (*linkp == link) break;
if (*linkp == NULL)
return;
del_timer(&link->release);
if (link->state & DEV_CONFIG)
serial_release((u_long)link);
if (link->handle) {
ret = CardServices(DeregisterClient, link->handle);
if (ret != CS_SUCCESS)
cs_error(link->handle, DeregisterClient, ret);
}
/* Unlink device structure, free bits */
*linkp = link->next;
kfree(info);
} /* serial_detach */
/*====================================================================*/
static int setup_serial(serial_info_t *info, ioaddr_t port, int irq)
{
struct serial_struct serial;
int line;
memset(&serial, 0, sizeof(serial));
serial.port = port;
serial.irq = irq;
serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ;
line = register_serial(&serial);
if (line < 0) {
printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx,"
" irq %d failed\n", (u_long)serial.port, serial.irq);
return -1;
}
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 get_tuple(int fn, client_handle_t handle, tuple_t *tuple,
cisparse_t *parse)
{
int i;
i = CardServices(fn, handle, tuple);
if (i != CS_SUCCESS) return CS_NO_MORE_ITEMS;
i = CardServices(GetTupleData, handle, tuple);
if (i != CS_SUCCESS) return i;
return CardServices(ParseTuple, handle, tuple, parse);
}
#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
/*====================================================================*/
static int simple_config(dev_link_t *link)
{
static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
client_handle_t handle = link->handle;
serial_info_t *info = link->priv;
tuple_t tuple;
u_char buf[256];
cisparse_t parse;
cistpl_cftable_entry_t *cf = &parse.cftable_entry;
config_info_t config;
int i, j, try;
/* If the card is already configured, look up the port and irq */
i = CardServices(GetConfigurationInfo, handle, &config);
if ((i == CS_SUCCESS) &&
(config.Attributes & CONF_VALID_CLIENT)) {
ioaddr_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)
return setup_serial(info, port, config.AssignedIRQ);
}
link->conf.Vcc = config.Vcc;
/* 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 (try = 0; try < 2; try++) {
i = first_tuple(handle, &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.Vpp1 = link->conf.Vpp2 =
cf->vpp1.param[CISTPL_POWER_VNOM]/10000;
if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
(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 = CardServices(RequestIO, link->handle, &link->io);
if (i == CS_SUCCESS) goto found_port;
}
next_entry:
i = next_tuple(handle, &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(handle, &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 = CardServices(RequestIO, link->handle,
&link->io);
if (i == CS_SUCCESS) goto found_port;
}
}
i = next_tuple(handle, &tuple, &parse);
}
found_port:
if (i != CS_SUCCESS) {
printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n");
cs_error(link->handle, RequestIO, i);
return -1;
}
i = CardServices(RequestIRQ, link->handle, &link->irq);
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
if (info->multi && (info->manfid == MANFID_3COM))
link->conf.ConfigIndex &= ~(0x08);
i = CardServices(RequestConfiguration, link->handle, &link->conf);
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestConfiguration, i);
return -1;
}
return setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
}
static int multi_config(dev_link_t *link)
{
client_handle_t handle = link->handle;
serial_info_t *info = link->priv;
tuple_t tuple;
u_char buf[256];
cisparse_t parse;
cistpl_cftable_entry_t *cf = &parse.cftable_entry;
int i, base2 = 0;
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(handle, &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 = CardServices(RequestIO, link->handle, &link->io);
base2 = link->io.BasePort1 + 8;
if (i == CS_SUCCESS) break;
}
i = next_tuple(handle, &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(handle, &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 = CardServices(RequestIO, link->handle, &link->io);
base2 = link->io.BasePort2;
if (i == CS_SUCCESS) break;
}
i = next_tuple(handle, &tuple, &parse);
}
}
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestIO, i);
return -1;
}
i = CardServices(RequestIRQ, link->handle, &link->irq);
if (i != CS_SUCCESS) {
printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n");
cs_error(link->handle, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
/* Socket Dual IO: this enables irq's for second port */
if (info->multi && (info->manfid == MANFID_SOCKET)) {
link->conf.Present |= PRESENT_EXT_STATUS;
link->conf.ExtStatus = ESR_REQ_ATTN_ENA;
}
i = CardServices(RequestConfiguration, link->handle, &link->conf);
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestConfiguration, i);
return -1;
}
setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
/* The Nokia cards are not really multiport cards */
if (info->manfid == MANFID_NOKIA)
return 0;
for (i = 0; i < info->multi-1; i++)
setup_serial(info, base2+(8*i), link->irq.AssignedIRQ);
return 0;
}
/*======================================================================
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.
======================================================================*/
#define CS_CHECK(fn, args...) \
while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
void serial_config(dev_link_t *link)
{
client_handle_t handle = link->handle;
serial_info_t *info = link->priv;
tuple_t tuple;
u_short buf[128];
cisparse_t parse;
cistpl_cftable_entry_t *cf = &parse.cftable_entry;
int i, last_ret, last_fn;
DEBUG(0, "serial_config(0x%p)\n", link);
tuple.TupleData = (cisdata_t *)buf;
tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
tuple.Attributes = 0;
/* Get configuration register information */
tuple.DesiredTuple = CISTPL_CONFIG;
last_ret = first_tuple(handle, &tuple, &parse);
if (last_ret != CS_SUCCESS) {
last_fn = ParseTuple;
goto cs_failed;
}
link->conf.ConfigBase = parse.config.base;
link->conf.Present = parse.config.rmask[0];
/* Configure card */
link->state |= DEV_CONFIG;
/* Is this a compliant multifunction card? */
tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS);
/* Is this a multiport card? */
tuple.DesiredTuple = CISTPL_MANFID;
if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
info->manfid = le16_to_cpu(buf[0]);
for (i = 0; i < MULTI_COUNT; i++)
if ((info->manfid == multi_id[i].manfid) &&
(le16_to_cpu(buf[1]) == multi_id[i].prodid))
break;
if (i < MULTI_COUNT)
info->multi = multi_id[i].multi;
}
/* 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) &&
((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) ||
(parse.funcid.func == CISTPL_FUNCID_MULTI) ||
(parse.funcid.func == CISTPL_FUNCID_SERIAL))) {
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
if (first_tuple(handle, &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;
}
}
if (info->multi > 1)
multi_config(link);
else
simple_config(link);
if (info->ndev == 0)
goto failed;
if (info->manfid == MANFID_IBM) {
conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
reg.Action = CS_WRITE;
reg.Value = reg.Value | 1;
CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
}
link->dev = &info->node[0];
link->state &= ~DEV_CONFIG_PENDING;
return;
cs_failed:
cs_error(link->handle, last_fn, last_ret);
failed:
serial_release((u_long)link);
} /* serial_config */
/*======================================================================
After a card is removed, serial_release() will unregister the net
device, and release the PCMCIA configuration.
======================================================================*/
void serial_release(u_long arg)
{
dev_link_t *link = (dev_link_t *)arg;
serial_info_t *info = link->priv;
int i;
DEBUG(0, "serial_release(0x%p)\n", link);
for (i = 0; i < info->ndev; i++) {
unregister_serial(info->line[i]);
}
link->dev = NULL;
if (!info->slave) {
CardServices(ReleaseConfiguration, link->handle);
CardServices(ReleaseIO, link->handle, &link->io);
CardServices(ReleaseIRQ, link->handle, &link->irq);
}
link->state &= ~DEV_CONFIG;
} /* serial_release */
/*======================================================================
The card status event handler. Mostly, this schedules other
stuff to run after an event is received. A CARD_REMOVAL event
also sets some flags to discourage the serial drivers from
talking to the ports.
======================================================================*/
static int serial_event(event_t event, int priority,
event_callback_args_t *args)
{
dev_link_t *link = args->client_data;
serial_info_t *info = link->priv;
DEBUG(1, "serial_event(0x%06x)\n", event);
switch (event) {
case CS_EVENT_CARD_REMOVAL:
link->state &= ~DEV_PRESENT;
if (link->state & DEV_CONFIG)
mod_timer(&link->release, jiffies + HZ/20);
break;
case CS_EVENT_CARD_INSERTION:
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
serial_config(link);
break;
case CS_EVENT_PM_SUSPEND:
link->state |= DEV_SUSPEND;
/* Fall through... */
case CS_EVENT_RESET_PHYSICAL:
if ((link->state & DEV_CONFIG) && !info->slave)
CardServices(ReleaseConfiguration, link->handle);
break;
case CS_EVENT_PM_RESUME:
link->state &= ~DEV_SUSPEND;
/* Fall through... */
case CS_EVENT_CARD_RESET:
if (DEV_OK(link) && !info->slave)
CardServices(RequestConfiguration, link->handle, &link->conf);
break;
}
return 0;
} /* serial_event */
/*====================================================================*/
static int __init init_serial_cs(void)
{
servinfo_t serv;
DEBUG(0, "%s\n", version);
CardServices(GetCardServicesInfo, &serv);
if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_NOTICE "serial_cs: Card Services release "
"does not match!\n");
return -1;
}
register_pccard_driver(&dev_info, &serial_attach, &serial_detach);
return 0;
}
static void __exit exit_serial_cs(void)
{
DEBUG(0, "serial_cs: unloading\n");
unregister_pccard_driver(&dev_info);
while (dev_list != NULL)
serial_detach(dev_list);
}
module_init(init_serial_cs);
module_exit(exit_serial_cs);
MODULE_LICENSE("GPL");
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -150,8 +150,7 @@ extern void con3215_init(void); ...@@ -150,8 +150,7 @@ extern void con3215_init(void);
extern void tty3215_init(void); extern void tty3215_init(void);
extern void tub3270_con_init(void); extern void tub3270_con_init(void);
extern void tub3270_init(void); extern void tub3270_init(void);
extern void rs285_console_init(void); extern void uart_console_init(void);
extern void sa1100_rs_console_init(void);
extern void sgi_serial_console_init(void); extern void sgi_serial_console_init(void);
extern void sci_console_init(void); extern void sci_console_init(void);
extern void tx3912_console_init(void); extern void tx3912_console_init(void);
...@@ -2221,18 +2220,12 @@ void __init console_init(void) ...@@ -2221,18 +2220,12 @@ void __init console_init(void)
#ifdef CONFIG_STDIO_CONSOLE #ifdef CONFIG_STDIO_CONSOLE
stdio_console_init(); stdio_console_init();
#endif #endif
#ifdef CONFIG_SERIAL_21285_CONSOLE #ifdef CONFIG_SERIAL_CORE_CONSOLE
rs285_console_init(); uart_console_init();
#endif
#ifdef CONFIG_SERIAL_SA1100_CONSOLE
sa1100_rs_console_init();
#endif #endif
#ifdef CONFIG_ARC_CONSOLE #ifdef CONFIG_ARC_CONSOLE
arc_console_init(); arc_console_init();
#endif #endif
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
ambauart_console_init();
#endif
#ifdef CONFIG_SERIAL_TX3912_CONSOLE #ifdef CONFIG_SERIAL_TX3912_CONSOLE
tx3912_console_init(); tx3912_console_init();
#endif #endif
......
# $Id: Config.help,v 1.5 2002/07/06 17:16:24 rmk Exp $
CONFIG_SERIAL_8250
This selects whether you want to include the driver for the standard
serial ports. The standard answer is Y. People who might say N
here are those that are setting up dedicated Ethernet WWW/FTP
servers, or users that have one of the various bus mice instead of a
serial mouse and don't intend to use their machine's standard serial
port for anything. (Note that the Cyclades and Stallion multi
serial port drivers do not need this driver built in for them to
work.)
If you want to compile this driver as a module, say M here and read
<file:Documentation/modules.txt>. The module will be called
serial.o.
[WARNING: Do not compile this driver as a module if you are using
non-standard serial ports, since the configuration information will
be lost when the driver is unloaded. This limitation may be lifted
in the future.]
BTW1: If you have a mouseman serial mouse which is not recognized by
the X window system, try running gpm first.
BTW2: If you intend to use a software modem (also called Winmodem)
under Linux, forget it. These modems are crippled and require
proprietary drivers which are only available under Windows.
Most people will say Y or M here, so that they can use serial mice,
modems and similar devices connecting to the standard serial ports.
CONFIG_SERIAL_8250_CONSOLE
If you say Y here, it will be possible to use a serial port as the
system console (the system console is the device which receives all
kernel messages and warnings and which allows logins in single user
mode). This could be useful if some terminal or printer is connected
to that serial port.
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyS1". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
If you don't have a VGA card installed and you say Y here, the
kernel will automatically use the first serial line, /dev/ttyS0, as
system console.
If unsure, say N.
CONFIG_SERIAL_8250_CS
Say Y here to enable support for 16-bit PCMCIA serial devices,
including serial port cards, modems, and the modem functions of
multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are
credit-card size devices often used with laptops.)
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called serial_cs.o. If you want to compile it as
a module, say M here and read <file:Documentation/modules.txt>.
If unsure, say N.
CONFIG_SERIAL_8250_EXTENDED
If you wish to use any non-standard features of the standard "dumb"
driver, say Y here. This includes HUB6 support, shared serial
interrupts, special multiport support, support for more than the
four COM 1/2/3/4 boards, etc.
Note that the answer to this question won't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about serial driver options. If unsure, say N.
CONFIG_SERIAL_8250_MANY_PORTS
Say Y here if you have dumb serial boards other than the four
standard COM 1/2/3/4 ports. This may happen if you have an AST
FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available
from <http://www.linuxdoc.org/docs.html#howto>), or other custom
serial port hardware which acts similar to standard serial port
hardware. If you only use the standard COM 1/2/3/4 ports, you can
say N here to save some memory. You can also say Y if you have an
"intelligent" multiport card such as Cyclades, Digiboards, etc.
CONFIG_SERIAL_8250_SHARE_IRQ
Some serial boards have hardware support which allows multiple dumb
serial ports on the same board to share a single IRQ. To enable
support for this in the serial driver, say Y here.
CONFIG_SERIAL_8250_DETECT_IRQ
Say Y here if you want the kernel to try to guess which IRQ
to use for your serial port.
This is considered unsafe; it is far better to configure the IRQ in
a boot script using the setserial command.
If unsure, say N.
CONFIG_SERIAL_8250_MULTIPORT
Some multiport serial ports have special ports which are used to
signal when there are any serial ports on the board which need
servicing. Say Y here to enable the serial driver to take advantage
of those special I/O ports.
CONFIG_SERIAL_8250_RSA
::: To be written :::
CONFIG_ATOMWIDE_SERIAL
If you have an Atomwide Serial card for an Acorn system, say Y to
this option. The driver can handle 1, 2, or 3 port cards.
If unsure, say N.
CONFIG_DUALSP_SERIAL
If you have the Serial Port's dual serial card for an Acorn system,
say Y to this option. If unsure, say N.
CONFIG_SERIAL_ANAKIN
::: To be written :::
CONFIG_SERIAL_ANAKIN_CONSOLE
::: To be written :::
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyAN0". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
CONFIG_ANAKIN_DEFAULT_BAUDRATE
::: To be written :::
CONFIG_SERIAL_AMBA
This selects the ARM(R) AMBA(R) PrimeCell UART. If you have an
Integrator platform, say Y or M here.
If unsure, say N.
CONFIG_SERIAL_AMBA_CONSOLE
Say Y here if you wish to use an AMBA PrimeCell UART as the system
console (the system console is the device which receives all kernel
messages and warnings and which allows logins in single user mode).
Even if you say Y here, the currently visible framebuffer console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyAM0". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
CONFIG_SERIAL_CLPS711X
::: To be written :::
CONFIG_SERIAL_CLPS711X_CONSOLE
::: To be written :::
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyCL1". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
CONFIG_SERIAL_CLPS711X_OLD_NAME
::: To be written :::
CONFIG_SERIAL_21285
If you have a machine based on a 21285 (Footbridge) StrongARM(R)/
PCI bridge you can enable its onboard serial port by enabling this
option.
CONFIG_SERIAL_21285_OLD
Use the old /dev/ttyS name, major 4 minor 64. This is obsolete
and will be removed during later 2.5 development.
CONFIG_SERIAL_21285_CONSOLE
If you have enabled the serial port on the 21285 footbridge you can
make it the console by answering Y to this option.
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyFB". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
CONFIG_SERIAL_UART00
Say Y here if you want to use the hard logic uart on Excalibur. This
driver also supports soft logic implentations of this uart core.
CONFIG_SERIAL_UART00_CONSOLE
Say Y here if you want to support a serial console on an Excalibur
hard logic uart or uart00 IP core.
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyS1". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
CONFIG_SERIAL_SA1100
If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you
can enable its onboard serial port by enabling this option.
Please read <file:Documentation/arm/SA1100/serial_UART> for further
info.
CONFIG_SERIAL_SA1100_CONSOLE
If you have enabled the serial port on the SA1100/SA1110 StrongARM
CPU you can make it the console by answering Y to this option.
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttySA0". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
#CONFIG_SERIAL_L7200
# If you have a LinkUp Systems L7200 board you can enable its two
# onboard serial ports by enabling this option. The device numbers
# are major ID 4 with minor 64 and 65 respectively.
#
#CONFIG_SERIAL_L7200_CONSOLE
# If you have enabled the serial ports on the L7200 development board
# you can make the first serial port the console by answering Y to
# this option.
#
# Serial device configuration
#
# $Id: Config.in,v 1.15 2002/07/06 17:16:24 rmk Exp $
#
mainmenu_option next_comment
comment 'Serial drivers'
#
# The new 8250/16550 serial drivers
dep_tristate '8250/16550 and compatible serial support (EXPERIMENTAL)' CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL
dep_bool ' Console on 8250/16550 and compatible serial port (EXPERIMENTAL)' CONFIG_SERIAL_8250_CONSOLE $CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL
dep_tristate ' 8250/16550 PCMCIA device support' CONFIG_SERIAL_8250_CS $CONFIG_PCMCIA $CONFIG_SERIAL_8250
dep_mbool 'Extended 8250/16550 serial driver options' CONFIG_SERIAL_8250_EXTENDED $CONFIG_SERIAL_8250
dep_bool ' Support more than 4 serial ports' CONFIG_SERIAL_8250_MANY_PORTS $CONFIG_SERIAL_8250_EXTENDED
dep_bool ' Support for sharing serial interrupts' CONFIG_SERIAL_8250_SHARE_IRQ $CONFIG_SERIAL_8250_EXTENDED
dep_bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_8250_DETECT_IRQ $CONFIG_SERIAL_8250_EXTENDED
dep_bool ' Support special multiport boards' CONFIG_SERIAL_8250_MULTIPORT $CONFIG_SERIAL_8250_EXTENDED
dep_bool ' Support RSA serial ports' CONFIG_SERIAL_8250_RSA $CONFIG_SERIAL_8250_EXTENDED
comment 'Non-8250 serial port support'
if [ "$CONFIG_ARM" = "y" ]; then
dep_tristate 'Acorn Atomwide 16550 serial port support' CONFIG_ATOMWIDE_SERIAL $CONFIG_ARCH_ACORN $CONFIG_SERIAL_8250
dep_tristate 'Acorn Dual 16550 serial port support' CONFIG_DUALSP_SERIAL $CONFIG_ARCH_ACORN $CONFIG_SERIAL_8250
dep_bool 'Anakin serial port support' CONFIG_SERIAL_ANAKIN $CONFIG_ARCH_ANAKIN
dep_bool ' Console on Anakin serial port' CONFIG_SERIAL_ANAKIN_CONSOLE $CONFIG_SERIAL_ANAKIN
if [ "$CONFIG_SERIAL_ANAKIN" = "y" ]; then
int ' Default Anakin serial baudrate' CONFIG_ANAKIN_DEFAULT_BAUDRATE 9600
fi
dep_tristate 'ARM AMBA serial port support' CONFIG_SERIAL_AMBA $CONFIG_ARCH_INTEGRATOR
dep_bool ' Support for console on AMBA serial port' CONFIG_SERIAL_AMBA_CONSOLE $CONFIG_SERIAL_AMBA
if [ "$CONFIG_SERIAL_AMBA" = "y" ]; then
define_bool CONFIG_SERIAL_INTEGRATOR y
fi
dep_tristate 'CLPS711X serial port support' CONFIG_SERIAL_CLPS711X $CONFIG_ARCH_CLPS711X
dep_bool ' Support for console on CLPS711X serial port' CONFIG_SERIAL_CLPS711X_CONSOLE $CONFIG_SERIAL_CLPS711X
dep_bool ' Use the old 2.4 names for CLPS711X serial port' CONFIG_SERIAL_CLPS711X_OLD_NAME $CONFIG_SERIAL_CLPS711X
dep_tristate 'DC21285 serial port support' CONFIG_SERIAL_21285 $CONFIG_FOOTBRIDGE
dep_bool ' Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD $CONFIG_SERIAL_21285 $CONFIG_OBSOLETE
dep_bool ' Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE $CONFIG_SERIAL_21285
dep_bool 'Excalibur serial port (uart00) support' CONFIG_SERIAL_UART00 $CONFIG_ARCH_CAMELOT
dep_bool ' Support for console on Excalibur serial port' CONFIG_SERIAL_UART00_CONSOLE $CONFIG_SERIAL_UART00
dep_bool 'SA1100 serial port support' CONFIG_SERIAL_SA1100 $CONFIG_ARCH_SA1100
dep_bool ' Console on SA1100 serial port' CONFIG_SERIAL_SA1100_CONSOLE $CONFIG_SERIAL_SA1100
fi
if [ "$CONFIG_SERIAL_AMBA" = "y" -o "$CONFIG_SERIAL_CLPS711X" = "y" -o \
"$CONFIG_SERIAL_21285" = "y" -o "$CONFIG_SERIAL_SA1100" = "y" -o \
"$CONFIG_SERIAL_ANAKIN" = "y" -o "$CONFIG_SERIAL_UART00" = "y" -o \
"$CONFIG_SERIAL_8250" = "y" -o "$CONFIG_SERIAL_ROCKETPORT" = "y" ]; then
define_bool CONFIG_SERIAL_CORE y
else
if [ "$CONFIG_SERIAL_AMBA" = "m" -o "$CONFIG_SERIAL_CLPS711X" = "m" -o \
"$CONFIG_SERIAL_21285" = "m" -o "$CONFIG_SERIAL_SA1100" = "m" -o \
"$CONFIG_SERIAL_ANAKIN" = "m" -o "$CONFIG_SERIAL_UART00" = "m" -o \
"$CONFIG_SERIAL_8250" = "m" -o "$CONFIG_SERIAL_ROCKETPORT" = "m" ]; then
define_bool CONFIG_SERIAL_CORE m
fi
fi
if [ "$CONFIG_SERIAL_AMBA_CONSOLE" = "y" -o \
"$CONFIG_SERIAL_CLPS711X_CONSOLE" = "y" -o \
"$CONFIG_SERIAL_21285_CONSOLE" = "y" -o \
"$CONFIG_SERIAL_SA1100_CONSOLE" = "y" -o \
"$CONFIG_SERIAL_ANAKIN_CONSOLE" = "y" -o \
"$CONFIG_SERIAL_UART00_CONSOLE" = "y" -o \
"$CONFIG_SERIAL_8250_CONSOLE" = "y" ]; then
define_bool CONFIG_SERIAL_CORE_CONSOLE y
fi
endmenu
#
# Makefile for the kernel serial device drivers.
#
# $Id: Makefile,v 1.7 2002/07/06 17:16:24 rmk Exp $
#
export-objs := serial_core.o serial_8250.o
serial-8250-y :=
serial-8250-$(CONFIG_PCI) += serial_8250_pci.o
serial-8250-$(CONFIG_ISAPNP) += serial_8250_pnp.o
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
obj-$(CONFIG_SERIAL_21285) += serial_21285.o
obj-$(CONFIG_SERIAL_8250) += serial_8250.o $(serial-8250-y)
obj-$(CONFIG_SERIAL_8250_CS) += serial_8250_cs.o
obj-$(CONFIG_SERIAL_ANAKIN) += serial_anakin.o
obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o
obj-$(CONFIG_SERIAL_CLPS711X) += serial_clps711x.o
obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o
obj-$(CONFIG_SERIAL_UART00) += serial_uart00.o
include $(TOPDIR)/Rules.make
/*
* linux/drivers/char/serial_21285.c
*
* Driver for the serial port on the 21285 StrongArm-110 core logic chip.
*
* Based on drivers/char/serial.c
*
* $Id: serial_21285.c,v 1.32 2002/07/21 08:57:55 rmk Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/serial_core.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.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_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__serial21285_stop_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
static void
serial21285_start_tx(struct uart_port *port, unsigned int tty_start)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
if (!tx_enabled(port)) {
enable_irq(IRQ_CONTX);
tx_enabled(port) = 1;
}
spin_unlock_irqrestore(&port->lock, flags);
}
static void serial21285_stop_rx(struct uart_port *port)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
if (rx_enabled(port)) {
disable_irq(IRQ_CONRX);
rx_enabled(port) = 0;
}
spin_unlock_irqrestore(&port->lock, flags);
}
static void serial21285_enable_ms(struct uart_port *port)
{
}
static void serial21285_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
struct tty_struct *tty = port->info->tty;
unsigned int status, ch, rxs, max_count = 256;
status = *CSR_UARTFLG;
while (!(status & 0x10) && max_count--) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty->flip.tqueue.routine((void *)tty);
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
printk(KERN_WARNING "TTY_DONT_FLIP set\n");
return;
}
}
ch = *CSR_UARTDR;
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ;
if (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)
*tty->flip.flag_buf_ptr = TTY_PARITY;
else if (rxs & RXSTAT_FRAME)
*tty->flip.flag_buf_ptr = TTY_FRAME;
}
if ((rxs & port->ignore_status_mask) == 0) {
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
if ((rxs & RXSTAT_OVERRUN) &&
tty->flip.count < TTY_FLIPBUF_SIZE) {
/*
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* character.
*/
*tty->flip.char_buf_ptr++ = 0;
*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
tty->flip.count++;
}
status = *CSR_UARTFLG;
}
tty_flip_buffer_push(tty);
}
static void serial21285_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
{
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;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
__serial21285_stop_tx(port);
return;
}
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_event(port, EVT_WRITE_WAKEUP);
if (uart_circ_empty(xmit))
__serial21285_stop_tx(port);
}
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_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
unsigned int h_lcr;
switch (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 (cflag & CSTOPB)
h_lcr |= H_UBRLCR_STOPB;
if (cflag & PARENB) {
h_lcr |= H_UBRLCR_PARENB;
if (!(cflag & PARODD))
h_lcr |= H_UBRLCR_PAREVN;
}
if (port->fifosize)
h_lcr |= H_UBRLCR_FIFO;
port->read_status_mask = RXSTAT_OVERRUN;
if (iflag & INPCK)
port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
/*
* Characters to ignore
*/
port->ignore_status_mask = 0;
if (iflag & IGNPAR)
port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
if (iflag & IGNBRK && iflag & IGNPAR)
port->ignore_status_mask |= RXSTAT_OVERRUN;
/*
* Ignore all characters if CREAD is not set.
*/
if ((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;
}
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,
change_speed: serial21285_change_speed,
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 = {
membase: 0,
mapbase: 0x42000160,
iotype: SERIAL_IO_MEM,
irq: NO_IRQ,
uartclk: 0,
fifosize: 16,
ops: &serial21285_ops,
flags: ASYNC_BOOT_AUTOCONF,
};
static void serial21285_setup_ports(void)
{
serial21285_port.uartclk = mem_fclk_21285 / 4;
}
#ifdef CONFIG_SERIAL_21285_CONSOLE
static void
serial21285_console_write(struct console *co, const char *s,
unsigned int count)
{
int i;
for (i = 0; i < count; i++) {
while (*CSR_UARTFLG & 0x20)
barrier();
*CSR_UARTDR = s[i];
if (s[i] == '\n') {
while (*CSR_UARTFLG & 0x20)
barrier();
*CSR_UARTDR = '\r';
}
}
}
static kdev_t serial21285_console_device(struct console *c)
{
return mk_kdev(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);
}
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);
}
#ifdef CONFIG_SERIAL_21285_OLD
static struct console serial21285_old_cons =
{
name: SERIAL_21285_OLD_NAME,
write: serial21285_console_write,
device: serial21285_console_device,
setup: serial21285_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
#endif
static struct console serial21285_console =
{
name: SERIAL_21285_NAME,
write: serial21285_console_write,
device: serial21285_console_device,
setup: serial21285_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
void __init rs285_console_init(void)
{
serial21285_setup_ports();
register_console(&serial21285_console);
}
#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",
#ifdef CONFIG_DEVFS_FS
dev_name: "ttyFB%d",
#else
dev_name: "ttyFB",
#endif
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.32 $\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);
EXPORT_NO_SYMBOLS;
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver $Revision: 1.32 $");
/*
* linux/drivers/char/serial_8250.c
*
* 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: serial_8250.c,v 1.80 2002/07/21 08:57:55 rmk Exp $
*
* A note about mapbase / membase
*
* mapbase is the physical address of the IO port. Currently, we don't
* support this very well, and it may well be dropped from this driver
* in future. As such, mapbase should be NULL.
*
* membase is an 'ioremapped' cookie. This is compatible with the old
* serial.c driver, and is currently the preferred form.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/serial_reg.h>
#include <linux/serialP.h>
#include <linux/delay.h>
#include <linux/kmod.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
#include "serial_8250.h"
/*
* Configuration:
* share_irqs - whether we pass SA_SHIRQ to request_irq(). This option
* is unsafe when used on edge-triggered interrupts.
*/
unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
/*
* Debugging.
*/
#if 0
#define DEBUG_AUTOCONF(fmt...) printk(fmt)
#else
#define DEBUG_AUTOCONF(fmt...) do { } while (0)
#endif
#if 0
#define DEBUG_INTR(fmt...) printk(fmt)
#else
#define DEBUG_INTR(fmt...) do { } while (0)
#endif
#define PASS_LIMIT 256
/*
* We default to IRQ0 for the "no irq" hack. Some
* machine types want others as well - they're free
* to redefine this in their header file.
*/
#define is_real_interrupt(irq) ((irq) != 0)
/*
* This converts from our new CONFIG_ symbols to the symbols
* that asm/serial.h expects. You _NEED_ to comment out the
* linux/config.h include contained inside asm/serial.h for
* this to work.
*/
#undef CONFIG_SERIAL_MANY_PORTS
#undef CONFIG_SERIAL_DETECT_IRQ
#undef CONFIG_SERIAL_MULTIPORT
#undef CONFIG_HUB6
#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
#define CONFIG_SERIAL_DETECT_IRQ 1
#endif
#ifdef CONFIG_SERIAL_8250_MULTIPORT
#define CONFIG_SERIAL_MULTIPORT 1
#endif
/*
* HUB6 is always on. This will be removed once the header
* files have been cleaned.
*/
#define CONFIG_HUB6 1
#define CONFIG_SERIAL_MANY_PORTS 1
#include <asm/serial.h>
static struct old_serial_port old_serial_port[] = {
SERIAL_PORT_DFNS /* defined in asm/serial.h */
};
#define UART_NR ARRAY_SIZE(old_serial_port)
#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
#define PORT_RSA_MAX 4
static int probe_rsa[PORT_RSA_MAX];
static int force_rsa[PORT_RSA_MAX];
#endif /* CONFIG_SERIAL_8250_RSA */
struct uart_8250_port {
struct uart_port port;
struct timer_list timer; /* "no irq" timer */
struct list_head list; /* ports on this IRQ */
unsigned char acr;
unsigned char ier;
unsigned char rev;
unsigned char lcr;
unsigned int lsr_break_flag;
/*
* We provide a per-port pm hook.
*/
void (*pm)(struct uart_port *port,
unsigned int state, unsigned int old);
};
struct irq_info {
spinlock_t lock;
struct list_head *head;
};
static struct irq_info irq_lists[NR_IRQS];
/*
* Here we define the default xmit fifo size used for each type of UART.
*/
static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = {
{ "unknown", 1, 0 },
{ "8250", 1, 0 },
{ "16450", 1, 0 },
{ "16550", 1, 0 },
{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
{ "Cirrus", 1, 0 },
{ "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH },
{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO },
{ "Startech", 1, 0 },
{ "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO },
{ "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
{ "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
{ "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }
};
static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
{
offset <<= up->port.regshift;
switch (up->port.iotype) {
case SERIAL_IO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase);
return inb(up->port.iobase + 1);
case SERIAL_IO_MEM:
return readb(up->port.membase + offset);
default:
return inb(up->port.iobase + offset);
}
}
static _INLINE_ void
serial_out(struct uart_8250_port *up, int offset, int value)
{
offset <<= up->port.regshift;
switch (up->port.iotype) {
case SERIAL_IO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase);
outb(value, up->port.iobase + 1);
break;
case SERIAL_IO_MEM:
writeb(value, up->port.membase + offset);
break;
default:
outb(value, up->port.iobase + offset);
}
}
/*
* We used to support using pause I/O for certain machines. We
* haven't supported this for a while, but just in case it's badly
* needed for certain old 386 machines, I've left these #define's
* in....
*/
#define serial_inp(up, offset) serial_in(up, offset)
#define serial_outp(up, offset, value) serial_out(up, offset, value)
/*
* For the 16C950
*/
static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
{
serial_out(up, UART_SCR, offset);
serial_out(up, UART_ICR, value);
}
static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
{
unsigned int value;
serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
serial_out(up, UART_SCR, offset);
value = serial_in(up, UART_ICR);
serial_icr_write(up, UART_ACR, up->acr);
return value;
}
#ifdef CONFIG_SERIAL_8250_RSA
/*
* Attempts to turn on the RSA FIFO. Returns zero on failure.
* We set the port uart clock rate if we succeed.
*/
static int __enable_rsa(struct uart_8250_port *up)
{
unsigned char mode;
int result;
mode = serial_inp(up, UART_RSA_MSR);
result = mode & UART_RSA_MSR_FIFO;
if (!result) {
serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
mode = serial_inp(up, UART_RSA_MSR);
result = mode & UART_RSA_MSR_FIFO;
}
if (result)
up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;
return result;
}
static void enable_rsa(struct uart_8250_port *up)
{
if (up->port.type == PORT_RSA) {
if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {
spin_lock_irq(&up->port.lock);
__enable_rsa(up);
spin_unlock_irq(&up->port.lock);
}
if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
serial_outp(up, UART_RSA_FRR, 0);
}
}
/*
* Attempts to turn off the RSA FIFO. Returns zero on failure.
* It is unknown why interrupts were disabled in here. However,
* the caller is expected to preserve this behaviour by grabbing
* the spinlock before calling this function.
*/
static void disable_rsa(struct uart_8250_port *up)
{
unsigned char mode;
int result;
if (up->port.type == PORT_RSA &&
up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
spin_lock_irq(&up->port.lock);
mode = serial_inp(up, UART_RSA_MSR);
result = !(mode & UART_RSA_MSR_FIFO);
if (!result) {
serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
mode = serial_inp(up, UART_RSA_MSR);
result = !(mode & UART_RSA_MSR_FIFO);
}
if (result)
up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;
spin_unlock_irq(&up->port.lock);
}
}
#endif /* CONFIG_SERIAL_8250_RSA */
/*
* This is a quickie test to see how big the FIFO is.
* It doesn't work at all the time, more's the pity.
*/
static int size_fifo(struct uart_8250_port *up)
{
unsigned char old_fcr, old_mcr, old_dll, old_dlm;
int count;
old_fcr = serial_inp(up, UART_FCR);
old_mcr = serial_inp(up, UART_MCR);
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
serial_outp(up, UART_MCR, UART_MCR_LOOP);
serial_outp(up, UART_LCR, UART_LCR_DLAB);
old_dll = serial_inp(up, UART_DLL);
old_dlm = serial_inp(up, UART_DLM);
serial_outp(up, UART_DLL, 0x01);
serial_outp(up, UART_DLM, 0x00);
serial_outp(up, UART_LCR, 0x03);
for (count = 0; count < 256; count++)
serial_outp(up, UART_TX, count);
mdelay(20);/* FIXME - schedule_timeout */
for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) &&
(count < 256); count++)
serial_inp(up, UART_RX);
serial_outp(up, UART_FCR, old_fcr);
serial_outp(up, UART_MCR, old_mcr);
serial_outp(up, UART_LCR, UART_LCR_DLAB);
serial_outp(up, UART_DLL, old_dll);
serial_outp(up, UART_DLM, old_dlm);
return count;
}
/*
* This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's.
* When this function is called we know it is at least a StarTech
* 16650 V2, but it might be one of several StarTech UARTs, or one of
* its clones. (We treat the broken original StarTech 16650 V1 as a
* 16550, and why not? Startech doesn't seem to even acknowledge its
* existence.)
*
* What evil have men's minds wrought...
*/
static void
autoconfig_startech_uarts(struct uart_8250_port *up)
{
unsigned char scratch, scratch2, scratch3, scratch4;
/*
* First we check to see if it's an Oxford Semiconductor UART.
*
* If we have to do this here because some non-National
* Semiconductor clone chips lock up if you try writing to the
* LSR register (which serial_icr_read does)
*/
if (up->port.type == PORT_16550A) {
/*
* EFR [4] must be set else this test fails
*
* This shouldn't be necessary, but Mike Hudson
* (Exoray@isys.ca) claims that it's needed for 952
* dual UART's (which are not recommended for new designs).
*/
up->acr = 0;
serial_out(up, UART_LCR, 0xBF);
serial_out(up, UART_EFR, 0x10);
serial_out(up, UART_LCR, 0x00);
/* Check for Oxford Semiconductor 16C950 */
scratch = serial_icr_read(up, UART_ID1);
scratch2 = serial_icr_read(up, UART_ID2);
scratch3 = serial_icr_read(up, UART_ID3);
if (scratch == 0x16 && scratch2 == 0xC9 &&
(scratch3 == 0x50 || scratch3 == 0x52 ||
scratch3 == 0x54)) {
up->port.type = PORT_16C950;
up->rev = serial_icr_read(up, UART_REV) |
(scratch3 << 8);
return;
}
}
/*
* We check for a XR16C850 by setting DLL and DLM to 0, and then
* reading back DLL and DLM. The chip type depends on the DLM
* value read back:
* 0x10 - XR16C850 and the DLL contains the chip revision.
* 0x12 - XR16C2850.
* 0x14 - XR16C854.
*/
/* Save the DLL and DLM */
serial_outp(up, UART_LCR, UART_LCR_DLAB);
scratch3 = serial_inp(up, UART_DLL);
scratch4 = serial_inp(up, UART_DLM);
serial_outp(up, UART_DLL, 0);
serial_outp(up, UART_DLM, 0);
scratch2 = serial_inp(up, UART_DLL);
scratch = serial_inp(up, UART_DLM);
serial_outp(up, UART_LCR, 0);
if (scratch == 0x10 || scratch == 0x12 || scratch == 0x14) {
if (scratch == 0x10)
up->rev = scratch2;
up->port.type = PORT_16850;
return;
}
/* Restore the DLL and DLM */
serial_outp(up, UART_LCR, UART_LCR_DLAB);
serial_outp(up, UART_DLL, scratch3);
serial_outp(up, UART_DLM, scratch4);
serial_outp(up, UART_LCR, 0);
/*
* We distinguish between the '654 and the '650 by counting
* how many bytes are in the FIFO. I'm using this for now,
* since that's the technique that was sent to me in the
* serial driver update, but I'm not convinced this works.
* I've had problems doing this in the past. -TYT
*/
if (size_fifo(up) == 64)
up->port.type = PORT_16654;
else
up->port.type = PORT_16650V2;
}
/*
* This routine is called by rs_init() to initialize a specific serial
* port. It determines what type of UART chip this serial port is
* using: 8250, 16450, 16550, 16550A. The important question is
* whether or not this UART is a 16550A or not, since this will
* determine whether or not we can use its FIFO features or not.
*/
static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
{
unsigned char status1, status2, scratch, scratch2, scratch3;
unsigned char save_lcr, save_mcr;
unsigned long flags;
DEBUG_AUTOCONF("Testing ttyS%d (0x%04x, 0x%08lx)...\n",
up->port.line, up->port.iobase, up->port.membase);
if (!up->port.iobase && !up->port.membase)
return;
/*
* We really do need global IRQs disabled here - we're going to
* be frobbing the chips IRQ enable register to see if it exists.
*/
spin_lock_irqsave(&up->port.lock, flags);
// save_flags(flags); cli();
if (!(up->port.flags & ASYNC_BUGGY_UART)) {
/*
* Do a simple existence test first; if we fail this,
* there's no point trying anything else.
*
* 0x80 is used as a nonsense port to prevent against
* false positives due to ISA bus float. The
* assumption is that 0x80 is a non-existent port;
* which should be safe since include/asm/io.h also
* makes this assumption.
*/
scratch = serial_inp(up, UART_IER);
serial_outp(up, UART_IER, 0);
#ifdef __i386__
outb(0xff, 0x080);
#endif
scratch2 = serial_inp(up, UART_IER);
serial_outp(up, UART_IER, 0x0F);
#ifdef __i386__
outb(0, 0x080);
#endif
scratch3 = serial_inp(up, UART_IER);
serial_outp(up, UART_IER, scratch);
if (scratch2 != 0 || scratch3 != 0x0F) {
/*
* We failed; there's nothing here
*/
DEBUG_AUTOCONF("serial: ttyS%d: simple autoconfig "
"failed (%02x, %02x)\n",
up->port.line, scratch2, scratch3);
goto out;
}
}
save_mcr = serial_in(up, UART_MCR);
save_lcr = serial_in(up, UART_LCR);
/*
* Check to see if a UART is really there. Certain broken
* internal modems based on the Rockwell chipset fail this
* test, because they apparently don't implement the loopback
* test mode. So this test is skipped on the COM 1 through
* COM 4 ports. This *should* be safe, since no board
* manufacturer would be stupid enough to design a board
* that conflicts with COM 1-4 --- we hope!
*/
if (!(up->port.flags & ASYNC_SKIP_TEST)) {
serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
status1 = serial_inp(up, UART_MSR) & 0xF0;
serial_outp(up, UART_MCR, save_mcr);
if (status1 != 0x90) {
DEBUG_AUTOCONF("serial: ttyS%d: no UART loopback "
"failed\n", up->port.line);
goto out;
}
}
serial_outp(up, UART_LCR, 0xBF); /* set up for StarTech test */
serial_outp(up, UART_EFR, 0); /* EFR is the same as FCR */
serial_outp(up, UART_LCR, 0);
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(up, UART_IIR) >> 6;
switch (scratch) {
case 0:
up->port.type = PORT_16450;
break;
case 1:
up->port.type = PORT_UNKNOWN;
break;
case 2:
up->port.type = PORT_16550;
break;
case 3:
up->port.type = PORT_16550A;
break;
}
if (up->port.type == PORT_16550A) {
/* Check for Startech UART's */
serial_outp(up, UART_LCR, UART_LCR_DLAB);
if (serial_in(up, UART_EFR) == 0) {
up->port.type = PORT_16650;
} else {
serial_outp(up, UART_LCR, 0xBF);
if (serial_in(up, UART_EFR) == 0)
autoconfig_startech_uarts(up);
}
}
if (up->port.type == PORT_16550A) {
/* Check for TI 16750 */
serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB);
serial_outp(up, UART_FCR,
UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
scratch = serial_in(up, UART_IIR) >> 5;
if (scratch == 7) {
/*
* If this is a 16750, and not a cheap UART
* clone, then it should only go into 64 byte
* mode if the UART_FCR7_64BYTE bit was set
* while UART_LCR_DLAB was latched.
*/
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
serial_outp(up, UART_LCR, 0);
serial_outp(up, UART_FCR,
UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
scratch = serial_in(up, UART_IIR) >> 5;
if (scratch == 6)
up->port.type = PORT_16750;
}
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
}
#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
/*
* Only probe for RSA ports if we got the region.
*/
if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) {
int i;
for (i = 0 ; i < PORT_RSA_MAX ; ++i) {
if (!probe_rsa[i] && !force_rsa[i])
break;
if (((probe_rsa[i] != up->port.iobase) ||
check_region(up->port.iobase + UART_RSA_BASE, 16)) &&
(force_rsa[i] != up->port.iobase))
continue;
if (__enable_rsa(up)) {
up->port.type = PORT_RSA;
break;
}
}
}
#endif
serial_outp(up, UART_LCR, save_lcr);
if (up->port.type == PORT_16450) {
scratch = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, 0xa5);
status1 = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, 0x5a);
status2 = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, scratch);
if ((status1 != 0xa5) || (status2 != 0x5a))
up->port.type = PORT_8250;
}
up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
if (up->port.type == PORT_UNKNOWN)
goto out;
/*
* Reset the UART.
*/
#ifdef CONFIG_SERIAL_8250_RSA
if (up->port.type == PORT_RSA)
serial_outp(up, UART_RSA_FRR, 0);
#endif
serial_outp(up, UART_MCR, save_mcr);
serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
serial_outp(up, UART_FCR, 0);
(void)serial_in(up, UART_RX);
serial_outp(up, UART_IER, 0);
out:
spin_unlock_irqrestore(&up->port.lock, flags);
// restore_flags(flags);
#ifdef CONFIG_SERIAL_8250_RSA
if (up->port.iobase && up->port.type == PORT_RSA) {
release_region(up->port.iobase, 8);
request_region(up->port.iobase + UART_RSA_BASE, 16,
"serial_rsa");
}
#endif
}
static void autoconfig_irq(struct uart_8250_port *up)
{
unsigned char save_mcr, save_ier;
unsigned char save_ICP = 0;
unsigned int ICP = 0;
unsigned long irqs;
int irq;
if (up->port.flags & ASYNC_FOURPORT) {
ICP = (up->port.iobase & 0xfe0) | 0x1f;
save_ICP = inb_p(ICP);
outb_p(0x80, ICP);
(void) inb_p(ICP);
}
/* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on());
save_mcr = serial_inp(up, UART_MCR);
save_ier = serial_inp(up, UART_IER);
serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
irqs = probe_irq_on();
serial_outp(up, UART_MCR, 0);
udelay (10);
if (up->port.flags & ASYNC_FOURPORT) {
serial_outp(up, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS);
} else {
serial_outp(up, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
}
serial_outp(up, UART_IER, 0x0f); /* enable all intrs */
(void)serial_inp(up, UART_LSR);
(void)serial_inp(up, UART_RX);
(void)serial_inp(up, UART_IIR);
(void)serial_inp(up, UART_MSR);
serial_outp(up, UART_TX, 0xFF);
udelay (20);
irq = probe_irq_off(irqs);
serial_outp(up, UART_MCR, save_mcr);
serial_outp(up, UART_IER, save_ier);
if (up->port.flags & ASYNC_FOURPORT)
outb_p(save_ICP, ICP);
up->port.irq = (irq > 0) ? irq : 0;
}
static void __serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
if (up->ier & UART_IER_THRI) {
up->ier &= ~UART_IER_THRI;
serial_out(up, UART_IER, up->ier);
}
if (up->port.type == PORT_16C950 && tty_stop) {
up->acr |= UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
}
static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
__serial8250_stop_tx(port, tty_stop);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
if (!(up->ier & UART_IER_THRI)) {
up->ier |= UART_IER_THRI;
serial_out(up, UART_IER, up->ier);
}
/*
* We only do this from uart_start
*/
if (tty_start && up->port.type == PORT_16C950) {
up->acr &= ~UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
}
static void serial8250_stop_rx(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
up->ier &= ~UART_IER_RLSI;
up->port.read_status_mask &= ~UART_LSR_DR;
serial_out(up, UART_IER, up->ier);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static void serial8250_enable_ms(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
up->ier |= UART_IER_MSI;
serial_out(up, UART_IER, up->ier);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static _INLINE_ void
receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
{
struct tty_struct *tty = up->port.info->tty;
unsigned char ch;
int max_count = 256;
do {
if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
tty->flip.tqueue.routine((void *)tty);
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
return; // if TTY_DONT_FLIP is set
}
ch = serial_inp(up, UART_RX);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = 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 ingored.
*/
*status &= up->port.read_status_mask;
#ifdef CONFIG_SERIAL_8250_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) {
DEBUG_INTR("handling break....");
*tty->flip.flag_buf_ptr = TTY_BREAK;
} else if (*status & UART_LSR_PE)
*tty->flip.flag_buf_ptr = TTY_PARITY;
else if (*status & UART_LSR_FE)
*tty->flip.flag_buf_ptr = TTY_FRAME;
}
if (uart_handle_sysrq_char(&up->port, ch, regs))
goto ignore_char;
if ((*status & up->port.ignore_status_mask) == 0) {
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
if ((*status & UART_LSR_OE) &&
tty->flip.count < TTY_FLIPBUF_SIZE) {
/*
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* character.
*/
*tty->flip.flag_buf_ptr = TTY_OVERRUN;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
ignore_char:
*status = serial_inp(up, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
tty_flip_buffer_push(tty);
}
static _INLINE_ void transmit_chars(struct uart_8250_port *up)
{
struct circ_buf *xmit = &up->port.info->xmit;
int count;
if (up->port.x_char) {
serial_outp(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)) {
__serial8250_stop_tx(&up->port, 0);
return;
}
count = up->port.fifosize;
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_event(&up->port, EVT_WRITE_WAKEUP);
DEBUG_INTR("THRE...");
if (uart_circ_empty(xmit))
__serial8250_stop_tx(&up->port, 0);
}
static _INLINE_ void check_modem_status(struct uart_8250_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 void
serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
{
unsigned int status = serial_inp(up, UART_LSR);
DEBUG_INTR("status = %x...", status);
if (status & UART_LSR_DR)
receive_chars(up, &status, regs);
check_modem_status(up);
if (status & UART_LSR_THRE)
transmit_chars(up);
}
/*
* This is the serial driver's interrupt routine.
*
* Arjan thinks the old way was overly complex, so it got simplified.
* Alan disagrees, saying that need the complexity to handle the weird
* nature of ISA shared interrupts. (This is a special exception.)
*
* In order to handle ISA shared interrupts properly, we need to check
* that all ports have been serviced, and therefore the ISA interrupt
* line has been de-asserted.
*
* This means we need to loop through all ports. checking that they
* don't have an interrupt pending.
*/
static void serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct irq_info *i = dev_id;
struct list_head *l, *end = NULL;
int pass_counter = 0;
DEBUG_INTR("serial8250_interrupt(%d)...", irq);
spin_lock(&i->lock);
l = i->head;
do {
struct uart_8250_port *up;
unsigned int iir;
up = list_entry(l, struct uart_8250_port, list);
iir = serial_in(up, UART_IIR);
if (!(iir & UART_IIR_NO_INT)) {
spin_lock(&up->port.lock);
serial8250_handle_port(up, regs);
spin_unlock(&up->port.lock);
end = NULL;
} else if (end == NULL)
end = l;
l = l->next;
if (l == i->head && pass_counter++ > PASS_LIMIT) {
/* If we hit this, we're dead. */
printk(KERN_ERR "serial8250: too much work for "
"irq%d\n", irq);
break;
}
} while (l != end);
spin_unlock(&i->lock);
DEBUG_INTR("end.\n");
}
/*
* To support ISA shared interrupts, we need to have one interrupt
* handler that ensures that the IRQ line has been deasserted
* before returning. Failing to do this will result in the IRQ
* line being stuck active, and, since ISA irqs are edge triggered,
* no more IRQs will be seen.
*/
static int serial_link_irq_chain(struct uart_8250_port *up)
{
struct irq_info *i = irq_lists + up->port.irq;
int ret, irq_flags = share_irqs ? SA_SHIRQ : 0;
spin_lock_irq(&i->lock);
if (i->head) {
list_add(&up->list, i->head);
spin_unlock_irq(&i->lock);
ret = 0;
} else {
INIT_LIST_HEAD(&up->list);
i->head = &up->list;
spin_unlock_irq(&i->lock);
ret = request_irq(up->port.irq, serial8250_interrupt,
irq_flags, "serial", i);
}
return ret;
}
static void serial_unlink_irq_chain(struct uart_8250_port *up)
{
struct irq_info *i = irq_lists + up->port.irq;
BUG_ON(i->head == NULL);
if (list_empty(i->head))
free_irq(up->port.irq, i);
spin_lock_irq(&i->lock);
if (!list_empty(i->head)) {
if (i->head == &up->list)
i->head = i->head->next;
list_del(&up->list);
} else {
BUG_ON(i->head != &up->list);
i->head = NULL;
}
spin_unlock_irq(&i->lock);
}
/*
* This function is used to handle ports that do not have an
* interrupt. This doesn't work very well for 16450's, but gives
* barely passable results for a 16550A. (Although at the expense
* of much CPU overhead).
*/
static void serial8250_timeout(unsigned long data)
{
struct uart_8250_port *up = (struct uart_8250_port *)data;
unsigned int timeout;
unsigned int iir;
iir = serial_in(up, UART_IIR);
if (!(iir & UART_IIR_NO_INT)) {
spin_lock(&up->port.lock);
serial8250_handle_port(up, NULL);
spin_unlock(&up->port.lock);
}
timeout = up->port.timeout;
timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
mod_timer(&up->timer, jiffies + timeout);
}
static unsigned int serial8250_tx_empty(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_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 serial8250_get_mctrl(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
unsigned char status;
unsigned int ret;
spin_lock_irqsave(&up->port.lock, flags);
status = serial_in(up, UART_MSR);
spin_unlock_irqrestore(&up->port.lock, flags);
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 serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned char mcr = ALPHA_KLUDGE_MCR;
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;
serial_out(up, UART_MCR, mcr);
}
static void serial8250_break_ctl(struct uart_port *port, int break_state)
{
struct uart_8250_port *up = (struct uart_8250_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);
}
static int serial8250_startup(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
int retval;
if (up->port.type == PORT_16C950) {
/* Wake up and initialize UART */
up->acr = 0;
serial_outp(up, UART_LCR, 0xBF);
serial_outp(up, UART_EFR, UART_EFR_ECB);
serial_outp(up, UART_IER, 0);
serial_outp(up, UART_LCR, 0);
serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
serial_outp(up, UART_LCR, 0xBF);
serial_outp(up, UART_EFR, UART_EFR_ECB);
serial_outp(up, UART_LCR, 0);
}
#ifdef CONFIG_SERIAL_8250_RSA
/*
* If this is an RSA port, see if we can kick it up to the
* higher speed clock.
*/
enable_rsa(up);
#endif
/*
* Clear the FIFO buffers and disable them.
* (they will be reeanbled in change_speed())
*/
if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) {
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
serial_outp(up, UART_FCR, 0);
}
/*
* Clear the interrupt registers.
*/
(void) serial_inp(up, UART_LSR);
(void) serial_inp(up, UART_RX);
(void) serial_inp(up, UART_IIR);
(void) serial_inp(up, UART_MSR);
/*
* At this point, there's no way the LSR could still be 0xff;
* if it is, then bail out, because there's likely no UART
* here.
*/
if (!(up->port.flags & ASYNC_BUGGY_UART) &&
(serial_inp(up, UART_LSR) == 0xff)) {
printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
return -ENODEV;
}
/*
* If the "interrupt" for this port doesn't correspond with any
* hardware interrupt, we use a timer-based system. The original
* driver used to do this with IRQ0.
*/
if (!is_real_interrupt(up->port.irq)) {
unsigned int timeout = up->port.timeout;
timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
up->timer.data = (unsigned long)up;
mod_timer(&up->timer, jiffies + timeout);
} else {
retval = serial_link_irq_chain(up);
if (retval)
return retval;
}
/*
* Now, initialize the UART
*/
serial_outp(up, UART_LCR, UART_LCR_WLEN8);
spin_lock_irqsave(&up->port.lock, flags);
if (up->port.flags & ASYNC_FOURPORT) {
if (!is_real_interrupt(up->port.irq))
up->port.mctrl |= TIOCM_OUT1;
} else
/*
* Most PC uarts need OUT2 raised to enable interrupts.
*/
if (is_real_interrupt(up->port.irq))
up->port.mctrl |= TIOCM_OUT2;
serial8250_set_mctrl(&up->port, up->port.mctrl);
spin_unlock_irqrestore(&up->port.lock, flags);
/*
* Finally, enable interrupts. Note: Modem status interrupts
* are set via change_speed(), which will be occuring imminently
* anyway, so we don't enable them here.
*/
up->ier = UART_IER_RLSI | UART_IER_RDI;
serial_outp(up, UART_IER, up->ier);
if (up->port.flags & ASYNC_FOURPORT) {
unsigned int icp;
/*
* Enable interrupts on the AST Fourport board
*/
icp = (up->port.iobase & 0xfe0) | 0x01f;
outb_p(0x80, icp);
(void) inb_p(icp);
}
/*
* And clear the interrupt registers again for luck.
*/
(void) serial_inp(up, UART_LSR);
(void) serial_inp(up, UART_RX);
(void) serial_inp(up, UART_IIR);
(void) serial_inp(up, UART_MSR);
return 0;
}
static void serial8250_shutdown(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
/*
* Disable interrupts from this port
*/
up->ier = 0;
serial_outp(up, UART_IER, 0);
spin_lock_irqsave(&up->port.lock, flags);
if (up->port.flags & ASYNC_FOURPORT) {
/* reset interrupts on the AST Fourport board */
inb((up->port.iobase & 0xfe0) | 0x1f);
up->port.mctrl |= TIOCM_OUT1;
} else
up->port.mctrl &= ~TIOCM_OUT2;
serial8250_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_inp(up, UART_LCR) & ~UART_LCR_SBC);
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT);
serial_outp(up, UART_FCR, 0);
#ifdef CONFIG_SERIAL_8250_RSA
/*
* Reset the RSA board back to 115kbps compat mode.
*/
disable_rsa(up);
#endif
/*
* Read data port to reset things, and then unlink from
* the IRQ chain.
*/
(void) serial_in(up, UART_RX);
if (!is_real_interrupt(up->port.irq))
del_timer_sync(&up->timer);
else
serial_unlink_irq_chain(up);
}
static void
serial8250_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned char cval, fcr = 0;
unsigned long flags;
printk("+++ change_speed port %p cflag %08x quot %d\n", port, cflag, quot);
switch (cflag & CSIZE) {
case CS5:
cval = 0x00;
break;
case CS6:
cval = 0x01;
break;
case CS7:
cval = 0x02;
break;
default:
case CS8:
cval = 0x03;
break;
}
if (cflag & CSTOPB)
cval |= 0x04;
if (cflag & PARENB)
cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
cval |= UART_LCR_EPAR;
#ifdef CMSPAR
if (cflag & CMSPAR)
cval |= UART_LCR_SPAR;
#endif
/*
* Work around a bug in the Oxford Semiconductor 952 rev B
* chip which causes it to seriously miscalculate baud rates
* when DLL is 0.
*/
if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 &&
up->rev == 0x5201)
quot ++;
if (uart_config[up->port.type].flags & UART_USE_FIFO) {
if ((up->port.uartclk / quot) < (2400 * 16))
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
#ifdef CONFIG_SERIAL_8250_RSA
else if (up->port.type == PORT_RSA)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
#endif
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
}
if (up->port.type == PORT_16750)
fcr |= UART_FCR7_64BYTE;
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (iflag & IGNPAR)
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
if (iflag & (BRKINT | PARMRK))
up->port.read_status_mask |= UART_LSR_BI;
/*
* Characteres to ignore
*/
up->port.ignore_status_mask = 0;
if (iflag & IGNPAR)
up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
if (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 (iflag & IGNPAR)
up->port.ignore_status_mask |= UART_LSR_OE;
}
/*
* ignore all characters if CREAD is not set
*/
if ((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, cflag))
up->ier |= UART_IER_MSI;
/*
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
spin_lock_irqsave(&up->port.lock, flags);
serial_out(up, UART_IER, up->ier);
if (uart_config[up->port.type].flags & UART_STARTECH) {
serial_outp(up, UART_LCR, 0xBF);
serial_outp(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0);
}
serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */
serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */
if (up->port.type == PORT_16750)
serial_outp(up, UART_FCR, fcr); /* set fcr */
serial_outp(up, UART_LCR, cval); /* reset DLAB */
up->lcr = cval; /* Save LCR */
if (up->port.type != PORT_16750) {
if (fcr & UART_FCR_ENABLE_FIFO) {
/* emulated UARTs (Lucent Venus 167x) need two steps */
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
}
serial_outp(up, UART_FCR, fcr); /* set fcr */
}
spin_unlock_irqrestore(&up->port.lock, flags);
}
static void
serial8250_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
if (state) {
/* sleep */
if (uart_config[up->port.type].flags & UART_STARTECH) {
/* Arrange to enter sleep mode */
serial_outp(up, UART_LCR, 0xBF);
serial_outp(up, UART_EFR, UART_EFR_ECB);
serial_outp(up, UART_LCR, 0);
serial_outp(up, UART_IER, UART_IERX_SLEEP);
serial_outp(up, UART_LCR, 0xBF);
serial_outp(up, UART_EFR, 0);
serial_outp(up, UART_LCR, 0);
}
if (up->port.type == PORT_16750) {
/* Arrange to enter sleep mode */
serial_outp(up, UART_IER, UART_IERX_SLEEP);
}
if (up->pm)
up->pm(port, state, oldstate);
} else {
/* wake */
if (uart_config[up->port.type].flags & UART_STARTECH) {
/* Wake up UART */
serial_outp(up, UART_LCR, 0xBF);
serial_outp(up, UART_EFR, UART_EFR_ECB);
/*
* Turn off LCR == 0xBF so we actually set the IER
* register on the XR16C850
*/
serial_outp(up, UART_LCR, 0);
serial_outp(up, UART_IER, 0);
/*
* Now reset LCR so we can turn off the ECB bit
*/
serial_outp(up, UART_LCR, 0xBF);
serial_outp(up, UART_EFR, 0);
/*
* For a XR16C850, we need to set the trigger levels
*/
if (up->port.type == PORT_16850) {
unsigned char fctr;
fctr = serial_inp(up, UART_FCTR) &
~(UART_FCTR_RX | UART_FCTR_TX);
serial_outp(up, UART_FCTR, fctr |
UART_FCTR_TRGD |
UART_FCTR_RX);
serial_outp(up, UART_TRG, UART_TRG_96);
serial_outp(up, UART_FCTR, fctr |
UART_FCTR_TRGD |
UART_FCTR_TX);
serial_outp(up, UART_TRG, UART_TRG_96);
}
serial_outp(up, UART_LCR, 0);
}
if (up->port.type == PORT_16750) {
/* Wake up UART */
serial_outp(up, UART_IER, 0);
}
if (up->pm)
up->pm(port, state, oldstate);
}
}
/*
* Resource handling. This is complicated by the fact that resources
* depend on the port type. Maybe we should be claiming the standard
* 8250 ports, and then trying to get other resources as necessary?
*/
static int
serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res)
{
unsigned int size = 8 << up->port.regshift;
int ret = 0;
switch (up->port.iotype) {
case SERIAL_IO_MEM:
if (up->port.mapbase) {
*res = request_mem_region(up->port.mapbase, size, "serial");
if (!*res)
ret = -EBUSY;
}
break;
case SERIAL_IO_HUB6:
case SERIAL_IO_PORT:
*res = request_region(up->port.iobase, size, "serial");
if (!*res)
ret = -EBUSY;
break;
}
return ret;
}
static int
serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res)
{
unsigned int size = 8 << up->port.regshift;
unsigned long start;
int ret = 0;
switch (up->port.iotype) {
case SERIAL_IO_MEM:
if (up->port.mapbase) {
start = up->port.mapbase;
start += UART_RSA_BASE << up->port.regshift;
*res = request_mem_region(start, size, "serial-rsa");
if (!*res)
ret = -EBUSY;
}
break;
case SERIAL_IO_HUB6:
case SERIAL_IO_PORT:
start = up->port.iobase;
start += UART_RSA_BASE << up->port.regshift;
*res = request_region(start, size, "serial-rsa");
if (!*res)
ret = -EBUSY;
break;
}
return ret;
}
static void serial8250_release_port(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long start, offset = 0, size = 0;
if (up->port.type == PORT_RSA) {
offset = UART_RSA_BASE << up->port.regshift;
size = 8;
}
size <<= up->port.regshift;
switch (up->port.iotype) {
case SERIAL_IO_MEM:
if (up->port.mapbase) {
/*
* Unmap the area.
*/
iounmap(up->port.membase);
up->port.membase = NULL;
start = up->port.mapbase;
if (size)
release_mem_region(start + offset, size);
release_mem_region(start, 8 << up->port.regshift);
}
break;
case SERIAL_IO_HUB6:
case SERIAL_IO_PORT:
start = up->port.iobase;
if (size)
release_region(start + offset, size);
release_region(start + offset, 8 << up->port.regshift);
break;
default:
break;
}
}
static int serial8250_request_port(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct resource *res = NULL, *res_rsa = NULL;
int ret = -EBUSY;
if (up->port.type == PORT_RSA) {
ret = serial8250_request_rsa_resource(up, &res_rsa);
if (ret)
return ret;
}
ret = serial8250_request_std_resource(up, &res);
/*
* If we have a mapbase, then request that as well.
*/
if (res != NULL && up->port.iotype == SERIAL_IO_MEM &&
up->port.mapbase) {
int size = res->end - res->start + 1;
up->port.membase = ioremap(up->port.mapbase, size);
if (!up->port.membase)
ret = -ENOMEM;
}
if (ret) {
if (res_rsa)
release_resource(res_rsa);
if (res)
release_resource(res);
}
return ret;
}
static void serial8250_config_port(struct uart_port *port, int flags)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct resource *res_std = NULL, *res_rsa = NULL;
int probeflags = PROBE_ANY;
int ret;
#ifdef CONFIG_MCA
/*
* Don't probe for MCA ports on non-MCA machines.
*/
if (up->port.flags & ASYNC_BOOT_ONLYMCA && !MCA_bus)
return;
#endif
/*
* Find the region that we can probe for. This in turn
* tells us whether we can probe for the type of port.
*/
ret = serial8250_request_std_resource(up, &res_std);
if (ret)
return;
ret = serial8250_request_rsa_resource(up, &res_rsa);
if (ret)
probeflags &= ~PROBE_RSA;
if (flags & UART_CONFIG_TYPE)
autoconfig(up, probeflags);
if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
autoconfig_irq(up);
/*
* If the port wasn't an RSA port, release the resource.
*/
if (up->port.type != PORT_RSA && res_rsa)
release_resource(res_rsa);
if (up->port.type == PORT_UNKNOWN)
release_resource(res_std);
}
static int
serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
{
if (ser->irq >= NR_IRQS || ser->irq < 0 ||
ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
ser->type > PORT_MAX_8250 || ser->type == PORT_CIRRUS ||
ser->type == PORT_STARTECH)
return -EINVAL;
return 0;
}
static const char *
serial8250_type(struct uart_port *port)
{
int type = port->type;
if (type >= ARRAY_SIZE(uart_config))
type = 0;
return uart_config[type].name;
}
static struct uart_ops serial8250_pops = {
tx_empty: serial8250_tx_empty,
set_mctrl: serial8250_set_mctrl,
get_mctrl: serial8250_get_mctrl,
stop_tx: serial8250_stop_tx,
start_tx: serial8250_start_tx,
stop_rx: serial8250_stop_rx,
enable_ms: serial8250_enable_ms,
break_ctl: serial8250_break_ctl,
startup: serial8250_startup,
shutdown: serial8250_shutdown,
change_speed: serial8250_change_speed,
pm: serial8250_pm,
type: serial8250_type,
release_port: serial8250_release_port,
request_port: serial8250_request_port,
config_port: serial8250_config_port,
verify_port: serial8250_verify_port,
};
static struct uart_8250_port serial8250_ports[UART_NR];
static void __init serial8250_isa_init_ports(void)
{
static int first = 1;
int i;
if (!first)
return;
first = 0;
for (i = 0; i < ARRAY_SIZE(old_serial_port); i++) {
serial8250_ports[i].port.iobase = old_serial_port[i].port;
serial8250_ports[i].port.irq = irq_cannonicalize(old_serial_port[i].irq);
serial8250_ports[i].port.uartclk = old_serial_port[i].base_baud * 16;
serial8250_ports[i].port.flags = old_serial_port[i].flags;
serial8250_ports[i].port.ops = &serial8250_pops;
}
}
static void __init serial8250_register_ports(struct uart_driver *drv)
{
int i;
serial8250_isa_init_ports();
for (i = 0; i < UART_NR; i++) {
serial8250_ports[i].port.line = i;
serial8250_ports[i].port.ops = &serial8250_pops;
init_timer(&serial8250_ports[i].timer);
serial8250_ports[i].timer.function = serial8250_timeout;
uart_add_one_port(drv, &serial8250_ports[i].port);
}
}
#ifdef CONFIG_SERIAL_8250_CONSOLE
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
/*
* Wait for transmitter & holding register to empty
*/
static inline void wait_for_xmitr(struct uart_8250_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 & ASYNC_CONS_FLOW) {
tmout = 1000000;
while (--tmout &&
((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
udelay(1);
}
}
/*
* 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
serial8250_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_8250_port *up = &serial8250_ports[co->index];
unsigned int ier;
int i;
/*
* First save the UER then disable the interrupts
*/
ier = serial_in(up, UART_IER);
serial_out(up, UART_IER, 0);
/*
* Now, do each character
*/
for (i = 0; i < count; i++, s++) {
wait_for_xmitr(up);
/*
* Send the character out.
* If a LF, also do CR...
*/
serial_out(up, UART_TX, *s);
if (*s == 10) {
wait_for_xmitr(up);
serial_out(up, UART_TX, 13);
}
}
/*
* Finally, wait for transmitter to become empty
* and restore the IER
*/
wait_for_xmitr(up);
serial_out(up, UART_IER, ier);
}
static kdev_t serial8250_console_device(struct console *co)
{
return mk_kdev(TTY_MAJOR, 64 + co->index);
}
static int __init serial8250_console_setup(struct console *co, char *options)
{
struct uart_port *port;
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 >= UART_NR)
co->index = 0;
port = &serial8250_ports[co->index].port;
printk("+++ index %d port %p iobase %x\n", co->index, port, port->iobase);
/*
* Temporary fix.
*/
spin_lock_init(&port->lock);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
printk("+++ baud %d bits %d parity %c flow %c\n", baud, parity, bits, flow);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct console serial8250_console = {
name: "ttyS",
write: serial8250_console_write,
device: serial8250_console_device,
setup: serial8250_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
void __init serial8250_console_init(void)
{
serial8250_isa_init_ports();
register_console(&serial8250_console);
}
#define SERIAL8250_CONSOLE &serial8250_console
#else
#define SERIAL8250_CONSOLE NULL
#endif
static struct uart_driver serial8250_reg = {
owner: THIS_MODULE,
driver_name: "serial",
#ifdef CONFIG_DEVFS_FS
dev_name: "tts/%d",
#else
dev_name: "ttyS",
#endif
major: TTY_MAJOR,
minor: 64,
nr: UART_NR,
cons: SERIAL8250_CONSOLE,
};
/*
* register_serial and unregister_serial allows for 16x50 serial ports to be
* configured at run-time, to support PCMCIA modems.
*/
static int __register_serial(struct serial_struct *req, int line)
{
struct uart_port port;
port.iobase = req->port;
port.membase = req->iomem_base;
port.irq = req->irq;
port.uartclk = req->baud_base * 16;
port.fifosize = req->xmit_fifo_size;
port.regshift = req->iomem_reg_shift;
port.iotype = req->io_type;
port.flags = req->flags | ASYNC_BOOT_AUTOCONF;
port.line = line;
if (HIGH_BITS_OFFSET)
port.iobase |= req->port_high << HIGH_BITS_OFFSET;
/*
* If a clock rate wasn't specified by the low level
* driver, then default to the standard clock rate.
*/
if (port.uartclk == 0)
port.uartclk = BASE_BAUD * 16;
return uart_register_port(&serial8250_reg, &port);
}
/**
* register_serial - configure a 16x50 serial port at runtime
* @req: request structure
*
* Configure the serial port specified by the request. If the
* port exists and is in use an error is returned. If the port
* is not currently in the table it is added.
*
* The port is then probed and if neccessary the IRQ is autodetected
* If this fails an error is returned.
*
* On success the port is ready to use and the line number is returned.
*/
int register_serial(struct serial_struct *req)
{
return __register_serial(req, -1);
}
int __init early_serial_setup(struct serial_struct *req)
{
__register_serial(req, req->line);
return 0;
}
/**
* unregister_serial - remove a 16x50 serial port at runtime
* @line: serial line number
*
* Remove one serial port. This may be called from interrupt
* context.
*/
void unregister_serial(int line)
{
uart_unregister_port(&serial8250_reg, line);
}
/*
* This is for ISAPNP only.
*/
void serial8250_get_irq_map(unsigned int *map)
{
int i;
for (i = 0; i < UART_NR; i++) {
if (serial8250_ports[i].port.type != PORT_UNKNOWN &&
serial8250_ports[i].port.irq < 16)
*map |= 1 << serial8250_ports[i].port.irq;
}
}
static int __init serial8250_init(void)
{
int ret, i;
printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.80 $ "
"IRQ sharing %sabled\n", share_irqs ? "en" : "dis");
for (i = 0; i < NR_IRQS; i++)
spin_lock_init(&irq_lists[i].lock);
ret = uart_register_driver(&serial8250_reg);
if (ret)
return ret;
serial8250_register_ports(&serial8250_reg);
return 0;
}
static void __exit serial8250_exit(void)
{
int i;
for (i = 0; i < UART_NR; i++)
uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port);
uart_unregister_driver(&serial8250_reg);
}
module_init(serial8250_init);
module_exit(serial8250_exit);
EXPORT_SYMBOL(register_serial);
EXPORT_SYMBOL(unregister_serial);
EXPORT_SYMBOL(serial8250_get_irq_map);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.80 $");
MODULE_PARM(share_irqs, "i");
MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
" (unsafe)");
#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA");
#endif
/*
* linux/drivers/char/serial_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: serial_8250.h,v 1.7 2002/07/06 16:24:46 rmk Exp $
*/
#include <linux/config.h>
struct serial8250_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 serial8250_register_probe(struct serial8250_probe *probe);
void serial8250_unregister_probe(struct serial8250_probe *probe);
void serial8250_get_irq_map(unsigned int *map);
struct old_serial_port {
unsigned int uart;
unsigned int base_baud;
unsigned int port;
unsigned int irq;
unsigned int flags;
};
#undef SERIAL_DEBUG_PCI
#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
#define SERIAL_INLINE
#endif
#ifdef SERIAL_INLINE
#define _INLINE_ inline
#else
#define _INLINE_
#endif
#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
/*======================================================================
A driver for PCMCIA serial devices
serial_cs.c 1.123 2000/08/24 18:46:38
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/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <linux/tqueue.h>
#include <asm/io.h>
#include <asm/system.h>
#include <pcmcia/version.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>
#ifdef PCMCIA_DEBUG
static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version = "serial_cs.c 1.123 2000/08/24 18:46:38 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
/*====================================================================*/
/* Parameters that can be set with 'insmod' */
/* Bit map of interrupts to choose from */
static u_int irq_mask = 0xdeb8;
static int irq_list[4] = { -1 };
/* Enable the speaker? */
static int do_sound = 1;
MODULE_PARM(irq_mask, "i");
MODULE_PARM(irq_list, "1-4i");
MODULE_PARM(do_sound, "i");
/*====================================================================*/
/* Table of multi-port card ID's */
struct multi_id {
u_short manfid;
u_short prodid;
int multi; /* 1 = multifunction, > 1 = # ports */
};
static struct multi_id multi_id[] = {
{ MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 },
{ MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 },
{ MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 },
{ MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 },
{ MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 },
{ MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 },
{ MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 }
};
#define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id))
typedef struct serial_info {
dev_link_t link;
int ndev;
int multi;
int slave;
int manfid;
dev_node_t node[4];
int line[4];
struct tq_struct remove;
} serial_info_t;
static void serial_config(dev_link_t * link);
static int serial_event(event_t event, int priority,
event_callback_args_t * args);
static dev_info_t dev_info = "serial_cs";
static dev_link_t *serial_attach(void);
static void serial_detach(dev_link_t *);
static dev_link_t *dev_list = NULL;
/*====================================================================*/
static void
cs_error(client_handle_t handle, int func, int ret)
{
error_info_t err = { func, ret };
CardServices(ReportError, handle, &err);
}
/*======================================================================
After a card is removed, do_serial_release() will unregister
the serial device(s), and release the PCMCIA configuration.
======================================================================*/
/*
* This always runs in process context.
*/
static void do_serial_release(void *arg)
{
struct serial_info *info = arg;
int i;
DEBUG(0, "serial_release(0x%p)\n", &info->link);
/*
* Recheck to see if the device is still configured.
*/
if (info->link.state & DEV_CONFIG) {
for (i = 0; i < info->ndev; i++)
unregister_serial(info->line[i]);
info->link.dev = NULL;
if (!info->slave) {
CardServices(ReleaseConfiguration, info->link.handle);
CardServices(ReleaseIO, info->link.handle, &info->link.io);
CardServices(ReleaseIRQ, info->link.handle, &info->link.irq);
}
info->link.state &= ~DEV_CONFIG;
}
}
/*
* This may be called from IRQ context.
*/
static void serial_remove(dev_link_t *link)
{
struct serial_info *info = link->priv;
link->state &= ~DEV_PRESENT;
/*
* FIXME: Since the card has probably been removed,
* we should call into the serial layer and hang up
* the ports on the card immediately.
*/
if (link->state & DEV_CONFIG)
schedule_task(&info->remove);
}
/*======================================================================
serial_attach() creates an "instance" of the driver, allocating
local data structures for one device. The device is registered
with Card Services.
======================================================================*/
static dev_link_t *serial_attach(void)
{
serial_info_t *info;
client_reg_t client_reg;
dev_link_t *link;
int i, ret;
DEBUG(0, "serial_attach()\n");
/* Create new serial device */
info = kmalloc(sizeof (*info), GFP_KERNEL);
if (!info)
return NULL;
memset(info, 0, sizeof (*info));
link = &info->link;
link->priv = info;
INIT_TQUEUE(&info->remove, do_serial_release, info);
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts1 = 8;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
if (irq_list[0] == -1)
link->irq.IRQInfo2 = irq_mask;
else
for (i = 0; i < 4; i++)
link->irq.IRQInfo2 |= 1 << irq_list[i];
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.Vcc = 50;
if (do_sound) {
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
}
link->conf.IntType = INT_MEMORY_AND_IO;
/* Register with Card Services */
link->next = dev_list;
dev_list = link;
client_reg.dev_info = &dev_info;
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
client_reg.EventMask =
CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
client_reg.event_handler = &serial_event;
client_reg.Version = 0x0210;
client_reg.event_callback_args.client_data = link;
ret = CardServices(RegisterClient, &link->handle, &client_reg);
if (ret != CS_SUCCESS) {
cs_error(link->handle, RegisterClient, ret);
serial_detach(link);
return NULL;
}
return 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(dev_link_t * link)
{
serial_info_t *info = link->priv;
dev_link_t **linkp;
int ret;
DEBUG(0, "serial_detach(0x%p)\n", link);
/* Locate device structure */
for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
if (*linkp == link)
break;
if (*linkp == NULL)
return;
/*
* Ensure any outstanding scheduled tasks are completed.
*/
flush_scheduled_tasks();
/*
* Ensure that the ports have been released.
*/
do_serial_release(info);
if (link->handle) {
ret = CardServices(DeregisterClient, link->handle);
if (ret != CS_SUCCESS)
cs_error(link->handle, DeregisterClient, ret);
}
/* Unlink device structure, free bits */
*linkp = link->next;
kfree(info);
}
/*====================================================================*/
static int setup_serial(serial_info_t * info, ioaddr_t port, int irq)
{
struct serial_struct serial;
int line;
memset(&serial, 0, sizeof (serial));
serial.port = port;
serial.irq = irq;
serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ;
line = register_serial(&serial);
if (line < 0) {
printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx,"
" irq %d failed\n", (u_long) serial.port, serial.irq);
return -1;
}
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
get_tuple(int fn, client_handle_t handle, tuple_t * tuple, cisparse_t * parse)
{
int i;
i = CardServices(fn, handle, tuple);
if (i != CS_SUCCESS)
return CS_NO_MORE_ITEMS;
i = CardServices(GetTupleData, handle, tuple);
if (i != CS_SUCCESS)
return i;
return CardServices(ParseTuple, handle, tuple, parse);
}
#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
/*====================================================================*/
static int simple_config(dev_link_t * link)
{
static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
client_handle_t handle = link->handle;
serial_info_t *info = link->priv;
tuple_t tuple;
u_char buf[256];
cisparse_t parse;
cistpl_cftable_entry_t *cf = &parse.cftable_entry;
config_info_t config;
int i, j, try;
/* If the card is already configured, look up the port and irq */
i = CardServices(GetConfigurationInfo, handle, &config);
if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) {
ioaddr_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)
return setup_serial(info, port, config.AssignedIRQ);
}
link->conf.Vcc = config.Vcc;
/* 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 (try = 0; try < 2; try++) {
i = first_tuple(handle, &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.Vpp1 = link->conf.Vpp2 =
cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
(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 =
CardServices(RequestIO, link->handle,
&link->io);
if (i == CS_SUCCESS)
goto found_port;
}
next_entry:
i = next_tuple(handle, &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(handle, &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 = CardServices(RequestIO, link->handle,
&link->io);
if (i == CS_SUCCESS)
goto found_port;
}
}
i = next_tuple(handle, &tuple, &parse);
}
found_port:
if (i != CS_SUCCESS) {
printk(KERN_NOTICE
"serial_cs: no usable port range found, giving up\n");
cs_error(link->handle, RequestIO, i);
return -1;
}
i = CardServices(RequestIRQ, link->handle, &link->irq);
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
if (info->multi && (info->manfid == MANFID_3COM))
link->conf.ConfigIndex &= ~(0x08);
i = CardServices(RequestConfiguration, link->handle, &link->conf);
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestConfiguration, i);
return -1;
}
return setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
}
static int multi_config(dev_link_t * link)
{
client_handle_t handle = link->handle;
serial_info_t *info = link->priv;
tuple_t tuple;
u_char buf[256];
cisparse_t parse;
cistpl_cftable_entry_t *cf = &parse.cftable_entry;
int i, base2 = 0;
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(handle, &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 = CardServices(RequestIO, link->handle, &link->io);
base2 = link->io.BasePort1 + 8;
if (i == CS_SUCCESS)
break;
}
i = next_tuple(handle, &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(handle, &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 =
CardServices(RequestIO, link->handle,
&link->io);
base2 = link->io.BasePort2;
if (i == CS_SUCCESS)
break;
}
i = next_tuple(handle, &tuple, &parse);
}
}
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestIO, i);
return -1;
}
i = CardServices(RequestIRQ, link->handle, &link->irq);
if (i != CS_SUCCESS) {
printk(KERN_NOTICE
"serial_cs: no usable port range found, giving up\n");
cs_error(link->handle, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
/* Socket Dual IO: this enables irq's for second port */
if (info->multi && (info->manfid == MANFID_SOCKET)) {
link->conf.Present |= PRESENT_EXT_STATUS;
link->conf.ExtStatus = ESR_REQ_ATTN_ENA;
}
i = CardServices(RequestConfiguration, link->handle, &link->conf);
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestConfiguration, i);
return -1;
}
setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
/* The Nokia cards are not really multiport cards */
if (info->manfid == MANFID_NOKIA)
return 0;
for (i = 0; i < info->multi - 1; i++)
setup_serial(info, base2 + (8 * i), link->irq.AssignedIRQ);
return 0;
}
/*======================================================================
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.
======================================================================*/
#define CS_CHECK(fn, args...) \
while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
void serial_config(dev_link_t * link)
{
client_handle_t handle = link->handle;
serial_info_t *info = link->priv;
tuple_t tuple;
u_short buf[128];
cisparse_t parse;
cistpl_cftable_entry_t *cf = &parse.cftable_entry;
int i, last_ret, last_fn;
DEBUG(0, "serial_config(0x%p)\n", link);
tuple.TupleData = (cisdata_t *) buf;
tuple.TupleOffset = 0;
tuple.TupleDataMax = 255;
tuple.Attributes = 0;
/* Get configuration register information */
tuple.DesiredTuple = CISTPL_CONFIG;
last_ret = first_tuple(handle, &tuple, &parse);
if (last_ret != CS_SUCCESS) {
last_fn = ParseTuple;
goto cs_failed;
}
link->conf.ConfigBase = parse.config.base;
link->conf.Present = parse.config.rmask[0];
/* Configure card */
link->state |= DEV_CONFIG;
/* Is this a compliant multifunction card? */
tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS);
/* Is this a multiport card? */
tuple.DesiredTuple = CISTPL_MANFID;
if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
info->manfid = le16_to_cpu(buf[0]);
for (i = 0; i < MULTI_COUNT; i++)
if ((info->manfid == multi_id[i].manfid) &&
(le16_to_cpu(buf[1]) == multi_id[i].prodid))
break;
if (i < MULTI_COUNT)
info->multi = multi_id[i].multi;
}
/* 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) &&
((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) ||
(parse.funcid.func == CISTPL_FUNCID_MULTI) ||
(parse.funcid.func == CISTPL_FUNCID_SERIAL))) {
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
if (first_tuple(handle, &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;
}
}
if (info->multi > 1)
multi_config(link);
else
simple_config(link);
if (info->ndev == 0)
goto failed;
if (info->manfid == MANFID_IBM) {
conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
reg.Action = CS_WRITE;
reg.Value = reg.Value | 1;
CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
}
link->dev = &info->node[0];
link->state &= ~DEV_CONFIG_PENDING;
return;
cs_failed:
cs_error(link->handle, last_fn, last_ret);
failed:
do_serial_release(info);
}
/*======================================================================
The card status event handler. Mostly, this schedules other
stuff to run after an event is received. A CARD_REMOVAL event
also sets some flags to discourage the serial drivers from
talking to the ports.
======================================================================*/
static int
serial_event(event_t event, int priority, event_callback_args_t * args)
{
dev_link_t *link = args->client_data;
serial_info_t *info = link->priv;
DEBUG(1, "serial_event(0x%06x)\n", event);
switch (event) {
case CS_EVENT_CARD_REMOVAL:
serial_remove(link);
break;
case CS_EVENT_CARD_INSERTION:
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
serial_config(link);
break;
case CS_EVENT_PM_SUSPEND:
link->state |= DEV_SUSPEND;
/* Fall through... */
case CS_EVENT_RESET_PHYSICAL:
if ((link->state & DEV_CONFIG) && !info->slave)
CardServices(ReleaseConfiguration, link->handle);
break;
case CS_EVENT_PM_RESUME:
link->state &= ~DEV_SUSPEND;
/* Fall through... */
case CS_EVENT_CARD_RESET:
if (DEV_OK(link) && !info->slave)
CardServices(RequestConfiguration, link->handle,
&link->conf);
break;
}
return 0;
}
/*====================================================================*/
static int __init init_serial_cs(void)
{
servinfo_t serv;
DEBUG(0, "%s\n", version);
CardServices(GetCardServicesInfo, &serv);
if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_NOTICE "serial_cs: Card Services release "
"does not match!\n");
return -1;
}
register_pccard_driver(&dev_info, &serial_attach, &serial_detach);
return 0;
}
static void __exit exit_serial_cs(void)
{
DEBUG(0, "serial_cs: unloading\n");
unregister_pccard_driver(&dev_info);
while (dev_list != NULL)
serial_detach(dev_list);
}
module_init(init_serial_cs);
module_exit(exit_serial_cs);
MODULE_LICENSE("GPL");
/*
* linux/drivers/char/serial_8250_pci.c
*
* Probe module for 8250/16550-type PCI serial ports.
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
* Copyright (C) 2001 Russell King, 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 of the License.
*
* $Id: serial_8250_pci.c,v 1.18 2002/03/10 22:32:08 rmk Exp $
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/serial.h>
/* 2.4.6 compatibility cruft - to be removed with the old serial.c code */
#define pci_board __pci_board
#include <linux/serialP.h>
#undef pci_board
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <asm/serial.h>
#include "serial_8250.h"
#ifndef IS_PCI_REGION_IOPORT
#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \
IORESOURCE_IO)
#endif
#ifndef IS_PCI_REGION_IOMEM
#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \
IORESOURCE_MEM)
#endif
#ifndef PCI_IRQ_RESOURCE
#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start)
#endif
#ifndef pci_get_subvendor
#define pci_get_subvendor(dev) ((dev)->subsystem_vendor)
#define pci_get_subdevice(dev) ((dev)->subsystem_device)
#endif
struct serial_private {
unsigned int nr;
struct pci_board *board;
int line[0];
};
struct pci_board {
int flags;
int num_ports;
int base_baud;
int uart_offset;
int reg_shift;
int (*init_fn)(struct pci_dev *dev, struct pci_board *board,
int enable);
int first_uart_offset;
};
static int
get_pci_port(struct pci_dev *dev, struct pci_board *board,
struct serial_struct *req, int idx)
{
unsigned long port;
int base_idx;
int max_port;
int offset;
base_idx = SPCI_FL_GET_BASE(board->flags);
if (board->flags & SPCI_FL_BASE_TABLE)
base_idx += idx;
if (board->flags & SPCI_FL_REGION_SZ_CAP) {
max_port = pci_resource_len(dev, base_idx) / 8;
if (idx >= max_port)
return 1;
}
offset = board->first_uart_offset;
/*
* Timedia/SUNIX uses a mixture of BARs and offsets
* Ugh, this is ugly as all hell --- TYT
*/
if (dev->vendor == PCI_VENDOR_ID_TIMEDIA)
switch(idx) {
case 0:
base_idx = 0;
break;
case 1:
base_idx = 0;
offset = 8;
break;
case 2:
base_idx = 1;
break;
case 3:
base_idx = 1;
offset = 8;
break;
case 4: /* BAR 2 */
case 5: /* BAR 3 */
case 6: /* BAR 4 */
case 7: /* BAR 5 */
base_idx = idx - 2;
}
/* AFAVLAB uses a different mixture of BARs and offsets */
/* Not that ugly ;) -- HW */
if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) {
base_idx = 4;
offset = (idx - 4) * 8;
}
/* Some Titan cards are also a little weird */
if (dev->vendor == PCI_VENDOR_ID_TITAN &&
(dev->device == PCI_DEVICE_ID_TITAN_400L ||
dev->device == PCI_DEVICE_ID_TITAN_800L)) {
switch (idx) {
case 0: base_idx = 1;
break;
case 1: base_idx = 2;
break;
default:
base_idx = 4;
offset = 8 * (idx - 2);
}
}
port = pci_resource_start(dev, base_idx) + offset;
if ((board->flags & SPCI_FL_BASE_TABLE) == 0)
port += idx * (board->uart_offset ? board->uart_offset : 8);
if (IS_PCI_REGION_IOPORT(dev, base_idx)) {
req->port = port;
if (HIGH_BITS_OFFSET)
req->port_high = port >> HIGH_BITS_OFFSET;
else
req->port_high = 0;
return 0;
}
req->io_type = SERIAL_IO_MEM;
req->iomem_base = ioremap(port, board->uart_offset);
if (req->iomem_base == NULL)
return -ENOMEM;
req->iomem_reg_shift = board->reg_shift;
req->port = 0;
return 0;
}
static _INLINE_ int
get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx)
{
int base_idx;
if ((board->flags & SPCI_FL_IRQRESOURCE) == 0)
return dev->irq;
base_idx = SPCI_FL_GET_IRQBASE(board->flags);
if (board->flags & SPCI_FL_IRQ_TABLE)
base_idx += idx;
return PCI_IRQ_RESOURCE(dev, base_idx);
}
/*
* Some PCI serial cards using the PLX 9050 PCI interface chip require
* that the card interrupt be explicitly enabled or disabled. This
* seems to be mainly needed on card using the PLX which also use I/O
* mapped memory.
*/
static int __devinit
pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
u8 *p, irq_config = 0;
if (enable) {
irq_config = 0x41;
if (dev->vendor == PCI_VENDOR_ID_PANACOM)
irq_config = 0x43;
if ((dev->vendor == PCI_VENDOR_ID_PLX) &&
(dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) {
/*
* As the megawolf cards have the int pins active
* high, and have 2 UART chips, both ints must be
* enabled on the 9050. Also, the UARTS are set in
* 16450 mode by default, so we have to enable the
* 16C950 'enhanced' mode so that we can use the
* deep FIFOs
*/
irq_config = 0x5b;
}
}
/*
* enable/disable interrupts
*/
p = ioremap(pci_resource_start(dev, 0), 0x80);
if (p == NULL)
return -ENOMEM;
writel(irq_config, (unsigned long)p + 0x4c);
/*
* Read the register back to ensure that it took effect.
*/
readl((unsigned long)p + 0x4c);
iounmap(p);
return 0;
}
/*
* SIIG serial cards have an PCI interface chip which also controls
* the UART clocking frequency. Each UART can be clocked independently
* (except cards equiped with 4 UARTs) and initial clocking settings
* are stored in the EEPROM chip. It can cause problems because this
* version of serial driver doesn't support differently clocked UART's
* on single PCI card. To prevent this, initialization functions set
* high frequency clocking for all UART's on given card. It is safe (I
* hope) because it doesn't touch EEPROM settings to prevent conflicts
* with other OSes (like M$ DOS).
*
* SIIG support added by Andrey Panin <pazke@mail.tp.ru>, 10/1999
*
* There is two family of SIIG serial cards with different PCI
* interface chip and different configuration methods:
* - 10x cards have control registers in IO and/or memory space;
* - 20x cards have control registers in standard PCI configuration space.
*/
#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc)
#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8)
static int __devinit
pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
u16 data, *p;
if (!enable)
return 0;
switch (dev->device & 0xfff8) {
case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */
data = 0xffdf;
break;
case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */
data = 0xf7ff;
break;
default: /* 1S1P, 4S */
data = 0xfffb;
break;
}
p = ioremap(pci_resource_start(dev, 0), 0x80);
if (p == NULL)
return -ENOMEM;
writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28);
iounmap(p);
return 0;
}
#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc)
#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc)
static int __devinit
pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
u8 data;
if (!enable)
return 0;
/* Change clock frequency for the first UART. */
pci_read_config_byte(dev, 0x6f, &data);
pci_write_config_byte(dev, 0x6f, data & 0xef);
/* If this card has 2 UART, we have to do the same with second UART. */
if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) ||
((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) {
pci_read_config_byte(dev, 0x73, &data);
pci_write_config_byte(dev, 0x73, data & 0xef);
}
return 0;
}
/* Added for EKF Intel i960 serial boards */
static int __devinit
pci_inteli960ni_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
unsigned long oldval;
if (!(pci_get_subdevice(dev) & 0x1000))
return -ENODEV;
if (!enable) /* is there something to deinit? */
return 0;
/* is firmware started? */
pci_read_config_dword(dev, 0x44, (void*) &oldval);
if (oldval == 0x00001000L) { /* RESET value */
printk(KERN_DEBUG "Local i960 firmware missing");
return -ENODEV;
}
return 0;
}
/*
* Timedia has an explosion of boards, and to avoid the PCI table from
* growing *huge*, we use this function to collapse some 70 entries
* in the PCI table into one, for sanity's and compactness's sake.
*/
static unsigned short timedia_single_port[] = {
0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0
};
static unsigned short timedia_dual_port[] = {
0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085,
0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079,
0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079,
0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079,
0xD079, 0
};
static unsigned short timedia_quad_port[] = {
0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157,
0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159,
0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056,
0xB157, 0
};
static unsigned short timedia_eight_port[] = {
0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166,
0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0
};
static struct timedia_struct {
int num;
unsigned short *ids;
} timedia_data[] = {
{ 1, timedia_single_port },
{ 2, timedia_dual_port },
{ 4, timedia_quad_port },
{ 8, timedia_eight_port },
{ 0, 0 }
};
static int __devinit
pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
int i, j;
unsigned short *ids;
if (!enable)
return 0;
for (i = 0; timedia_data[i].num; i++) {
ids = timedia_data[i].ids;
for (j = 0; ids[j]; j++) {
if (pci_get_subdevice(dev) == ids[j]) {
board->num_ports = timedia_data[i].num;
return 0;
}
}
}
return 0;
}
static int __devinit
pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
__set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10);
return 0;
}
/*
* This is the configuration table for all of the PCI serial boards
* which we support. It is directly indexed by the pci_board_num_t enum
* value, which is encoded in the pci_device_id PCI probe table's
* driver_data member.
*/
enum pci_board_num_t {
pbn_b0_1_115200,
pbn_default = 0,
pbn_b0_2_115200,
pbn_b0_4_115200,
pbn_b0_1_921600,
pbn_b0_2_921600,
pbn_b0_4_921600,
pbn_b0_bt_1_115200,
pbn_b0_bt_2_115200,
pbn_b0_bt_8_115200,
pbn_b0_bt_1_460800,
pbn_b0_bt_2_460800,
pbn_b1_1_115200,
pbn_b1_2_115200,
pbn_b1_4_115200,
pbn_b1_8_115200,
pbn_b1_2_921600,
pbn_b1_4_921600,
pbn_b1_8_921600,
pbn_b1_2_1382400,
pbn_b1_4_1382400,
pbn_b1_8_1382400,
pbn_b2_8_115200,
pbn_b2_4_460800,
pbn_b2_8_460800,
pbn_b2_16_460800,
pbn_b2_4_921600,
pbn_b2_8_921600,
pbn_b2_bt_1_115200,
pbn_b2_bt_2_115200,
pbn_b2_bt_4_115200,
pbn_b2_bt_2_921600,
pbn_panacom,
pbn_panacom2,
pbn_panacom4,
pbn_plx_romulus,
pbn_oxsemi,
pbn_timedia,
pbn_intel_i960,
pbn_sgi_ioc3,
pbn_nec_nile4,
pbn_dci_pccom4,
pbn_dci_pccom8,
pbn_xircom_combo,
pbn_siig10x_0,
pbn_siig10x_1,
pbn_siig10x_2,
pbn_siig10x_4,
pbn_siig20x_0,
pbn_siig20x_2,
pbn_siig20x_4,
pbn_computone_4,
pbn_computone_6,
pbn_computone_8,
};
static struct pci_board pci_boards[] __devinitdata = {
/*
* PCI Flags, Number of Ports, Base (Maximum) Baud Rate,
* Offset to get to next UART's registers,
* Register shift to use for memory-mapped I/O,
* Initialization function, first UART offset
*/
/* Generic serial board, pbn_b0_1_115200, pbn_default */
{ SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200,
pbn_default */
{ SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */
{ SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */
{ SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */
{ SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */
{ SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */
{ SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */
{ SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */
{ SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */
{ SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */
{ SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */
{ SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */
{ SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */
{ SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */
{ SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */
{ SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */
{ SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */
{ SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */
{ SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */
{ SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */
{ SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */
{ SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */
{ SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */
0x400, 7, pci_plx9050_fn },
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */
0x400, 7, pci_plx9050_fn },
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */
0x400, 7, pci_plx9050_fn },
{ SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */
0x20, 2, pci_plx9050_fn, 0x03 },
/* This board uses the size of PCI Base region 0 to
* signal now many ports are available */
{ SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */
{ SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */
0, 0, pci_timedia_fn },
/* EKF addition for i960 Boards form EKF with serial port */
{ SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */
8<<2, 2, pci_inteli960ni_fn, 0x10000},
{ SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */
1, 458333, 0, 0, 0, 0x20178 },
/*
* NEC Vrc-5074 (Nile 4) builtin UART.
*/
{ SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */
64, 3, NULL, 0x300 },
{ SPCI_FL_BASE3, 4, 115200, 8 }, /* pbn_dci_pccom4 */
{ SPCI_FL_BASE3, 8, 115200, 8 }, /* pbn_dci_pccom8 */
{ SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */
0, 0, pci_xircom_fn },
{ SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */
0, 0, pci_siig10x_fn },
{ SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */
0, 0, pci_siig10x_fn },
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */
0, 0, pci_siig10x_fn },
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */
0, 0, pci_siig10x_fn },
{ SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */
0, 0, pci_siig20x_fn },
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */
0, 0, pci_siig20x_fn },
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */
0, 0, pci_siig20x_fn },
{ SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */
0x40, 2, NULL, 0x200 },
{ SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */
0x40, 2, NULL, 0x200 },
{ SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */
0x40, 2, NULL, 0x200 },
};
/*
* Given a complete unknown PCI device, try to use some heuristics to
* guess what the configuration might be, based on the pitiful PCI
* serial specs. Returns 0 on success, 1 on failure.
*/
static int __devinit
serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board)
{
int num_iomem = 0, num_port = 0, first_port = -1;
int i;
/*
* If it is not a communications device or the programming
* interface is greater than 6, give up.
*
* (Should we try to make guesses for multiport serial devices
* later?)
*/
if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) &&
((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) ||
(dev->class & 0xff) > 6)
return 1;
for (i = 0; i < 6; i++) {
if (IS_PCI_REGION_IOPORT(dev, i)) {
num_port++;
if (first_port == -1)
first_port = i;
}
if (IS_PCI_REGION_IOMEM(dev, i))
num_iomem++;
}
/*
* If there is 1 or 0 iomem regions, and exactly one port, use
* it.
*/
if (num_iomem <= 1 && num_port == 1) {
board->flags = first_port;
return 0;
}
return 1;
}
/*
* return an error code to refuse.
*
* serial_struct is 60 bytes.
*/
static int __devinit pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
{
struct serial_private *priv;
struct pci_board *board, tmp;
struct serial_struct serial_req;
int base_baud, rc, k;
if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
ent->driver_data);
return -EINVAL;
}
board = &pci_boards[ent->driver_data];
rc = pci_enable_device(dev);
if (rc)
return rc;
if (ent->driver_data == pbn_default &&
serial_pci_guess_board(dev, board))
return -ENODEV;
else if (serial_pci_guess_board(dev, &tmp) == 0) {
printk(KERN_INFO "Redundant entry in serial pci_table. "
"Please send the output of\n"
"lspci -vv, this message (%d,%d,%d,%d)\n"
"and the manufacturer and name of "
"serial board or modem board\n"
"to serial-pci-info@lists.sourceforge.net.\n",
dev->vendor, dev->device,
pci_get_subvendor(dev), pci_get_subdevice(dev));
}
priv = kmalloc(sizeof(struct serial_private) +
sizeof(unsigned int) * board->num_ports,
GFP_KERNEL);
if (!priv)
return -ENOMEM;
/*
* Run the initialization function, if any
*/
if (board->init_fn) {
rc = board->init_fn(dev, board, 1);
if (rc != 0) {
kfree(priv);
return rc;
}
}
base_baud = board->base_baud;
if (!base_baud)
base_baud = BASE_BAUD;
memset(&serial_req, 0, sizeof(serial_req));
for (k = 0; k < board->num_ports; k++) {
serial_req.irq = get_pci_irq(dev, board, k);
if (get_pci_port(dev, board, &serial_req, k))
break;
#ifdef SERIAL_DEBUG_PCI
printk("Setup PCI/PNP port: port %x, irq %d, type %d\n",
serial_req.port, serial_req.irq, serial_req.io_type);
#endif
serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
serial_req.baud_base = base_baud;
priv->line[k] = register_serial(&serial_req);
if (priv->line[k] < 0)
break;
}
priv->board = board;
priv->nr = k;
pci_set_drvdata(dev, priv);
return 0;
}
static void __devexit pci_remove_one(struct pci_dev *dev)
{
struct serial_private *priv = pci_get_drvdata(dev);
int i;
pci_set_drvdata(dev, NULL);
if (priv) {
for (i = 0; i < priv->nr; i++)
unregister_serial(priv->line[i]);
priv->board->init_fn(dev, priv->board, 0);
pci_disable_device(dev);
kfree(priv);
}
}
static struct pci_device_id serial_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
pbn_b1_8_1382400 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
pbn_b1_4_1382400 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
pbn_b1_2_1382400 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
pbn_b1_8_1382400 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
pbn_b1_4_1382400 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
pbn_b1_2_1382400 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0,
pbn_b1_8_921600 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0,
pbn_b1_8_921600 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0,
pbn_b1_4_921600 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0,
pbn_b1_4_921600 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0,
pbn_b1_2_921600 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0,
pbn_b1_8_921600 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0,
pbn_b1_8_921600 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0,
pbn_b1_4_921600 },
{ PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_1_115200 },
{ PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_2_115200 },
{ PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_4_115200 },
{ PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_2_115200 },
{ PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_4_115200 },
{ PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_8_115200 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_2_115200 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_2_921600 },
/* VScom SPCOM800, from sl@s.pl */
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_8_921600 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_4_921600 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_KEYSPAN,
PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0,
pbn_panacom },
{ PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_panacom4 },
{ PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_panacom2 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_CHASE_PCIFAST,
PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0,
pbn_b2_4_460800 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_CHASE_PCIFAST,
PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0,
pbn_b2_8_460800 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_CHASE_PCIFAST,
PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0,
pbn_b2_16_460800 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_CHASE_PCIFAST,
PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0,
pbn_b2_16_460800 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_CHASE_PCIRAS,
PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0,
pbn_b2_4_460800 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_CHASE_PCIRAS,
PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0,
pbn_b2_8_460800 },
/* Megawolf Romulus PCI Serial Card, from Mike Hudson */
/* (Exoray@isys.ca) */
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
0x10b5, 0x106a, 0, 0,
pbn_plx_romulus },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_2_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_8_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_8_115200 },
{ PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_115200 },
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_115200 },
/* Digitan DS560-558, from jimd@esoft.com */
{ PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_1_115200 },
/* 3Com US Robotics 56k Voice Internal PCI model 5610 */
{ PCI_VENDOR_ID_USR, 0x1008,
PCI_ANY_ID, PCI_ANY_ID, },
/* Titan Electronic cards */
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_1_921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE1, 1, 921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 },
/* The 400L and 800L have a custom hack in get_pci_port */
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE_TABLE, 4, 921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE_TABLE, 8, 921600 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_1 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_1 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_1 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_4 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_4 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_4 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_2 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_4 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_4 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_4 },
/* Computone devices submitted by Doug McNash dmcnash@computone.com */
{ PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4,
0, 0, pbn_computone_4 },
{ PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8,
0, 0, pbn_computone_8 },
{ PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6,
0, 0, pbn_computone_6 },
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi },
{ PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_115200 },
/* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> */
{ PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_8_115200 },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_115200 },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_115200 },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_460800 },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_460800 },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_460800 },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_1_115200 },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_1_460800 },
/* RAStel 2 port modem, gerg@moreton.com.au */
{ PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_2_115200 },
/* EKF addition for i960 Boards form EKF with serial port */
{ PCI_VENDOR_ID_INTEL, 0x1960,
0xE4BF, PCI_ANY_ID, 0, 0,
pbn_intel_i960 },
/* Xircom Cardbus/Ethernet combos */
{ PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_xircom_combo },
/*
* Untested PCI modems, sent in from various folks...
*/
/* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */
{ PCI_VENDOR_ID_ROCKWELL, 0x1004,
0x1048, 0x1500, 0, 0,
pbn_b1_1_115200 },
{ PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
0xFF00, 0, 0, 0,
pbn_sgi_ioc3 },
/*
* NEC Vrc-5074 (Nile 4) builtin UART.
*/
{ PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_nec_nile4 },
{ PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_dci_pccom4 },
{ PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_dci_pccom8 },
{ PCI_ANY_ID, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_COMMUNICATION_SERIAL << 8,
0xffff00, },
{ PCI_ANY_ID, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_COMMUNICATION_MODEM << 8,
0xffff00, },
{ PCI_ANY_ID, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_COMMUNICATION_MULTISERIAL << 8,
0xffff00, },
{ 0, }
};
#ifndef __devexit_p
#if defined(MODULE) || defined(CONFIG_HOTPLUG)
#define __devexit_p(x) x
#else
#define __devexit_p(x) NULL
#endif
#endif
static struct pci_driver serial_pci_driver = {
name: "serial",
probe: pci_init_one,
remove: __devexit_p(pci_remove_one),
id_table: serial_pci_tbl,
};
static int __init serial8250_pci_init(void)
{
return pci_module_init(&serial_pci_driver);
}
static void __exit serial8250_pci_exit(void)
{
pci_unregister_driver(&serial_pci_driver);
}
module_init(serial8250_pci_init);
module_exit(serial8250_pci_exit);
EXPORT_NO_SYMBOLS;
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
MODULE_DEVICE_TABLE(pci, serial_pci_tbl);
/*
* linux/drivers/char/serial_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.
*
* 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: serial_8250_pnp.c,v 1.9 2002/02/18 19:20:29 rmk Exp $
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/isapnp.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <asm/serial.h>
#include "serial_8250.h"
struct pnpbios_device_id
{
char id[8];
unsigned long driver_data;
};
static const struct pnpbios_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 },
/* Rockwell 56K ACF II Fax+Data+Voice Modem */
{ "AKY1021", 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 },
/* 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 },
/* 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 },
/* 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 },
/* 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 33.6 Data/Fax PnP modem */
{ "SUP1760", 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 },
/* 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 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 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 },
{ "", 0 }
};
static void inline avoid_irq_share(struct pci_dev *dev)
{
unsigned int map = 0x1FF8;
struct isapnp_irq *irq;
struct isapnp_resources *res = dev->sysdata;
serial8250_get_irq_map(&map);
for ( ; res; res = res->alt)
for (irq = res->irq; irq; irq = irq->next)
irq->map = map;
}
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", 0
};
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 inline check_compatible_id(struct pci_dev *dev)
{
int i;
for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++)
if ((dev->vendor_compatible[i] ==
ISAPNP_VENDOR('P', 'N', 'P')) &&
(swab16(dev->device_compatible[i]) >= 0xc000) &&
(swab16(dev->device_compatible[i]) <= 0xdfff))
return 0;
return 1;
}
/*
* Given a complete unknown ISA 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 adress
* 0x2e8, 0x3e8, 0x2f8 or 0x3f8.
*
* Such detection looks very ugly, but can detect at least some of numerous
* ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[]
* table.
*/
static int serial_pnp_guess_board(struct pci_dev *dev, int *flags)
{
struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata;
struct isapnp_resources *resa;
if (!(check_name(dev->name) || check_name(dev->bus->name)) &&
!(check_compatible_id(dev)))
return -ENODEV;
if (!res || res->next)
return -ENODEV;
for (resa = res->alt; resa; resa = resa->alt) {
struct isapnp_port *port;
for (port = res->port; port; port = port->next)
if ((port->size == 8) &&
((port->min == 0x2f8) ||
(port->min == 0x3f8) ||
(port->min == 0x2e8) ||
(port->min == 0x3e8)))
return 0;
}
return -ENODEV;
}
static int
pnp_init_one(struct pci_dev *dev, const struct pnpbios_device_id *ent,
char *slot_name)
{
struct serial_struct serial_req;
int ret, line, flags = ent ? ent->driver_data : 0;
if (!ent) {
ret = serial_pnp_guess_board(dev, &flags);
if (ret)
return ret;
}
if (dev->prepare(dev) < 0) {
printk("serial: PNP device '%s' prepare failed\n",
slot_name);
return -ENODEV;
}
if (dev->active)
return -ENODEV;
if (flags & SPCI_FL_NO_SHIRQ)
avoid_irq_share(dev);
if (dev->activate(dev) < 0) {
printk("serial: PNP device '%s' activate failed\n",
slot_name);
return -ENODEV;
}
memset(&serial_req, 0, sizeof(serial_req));
serial_req.irq = dev->irq_resource[0].start;
serial_req.port = pci_resource_start(dev, 0);
if (HIGH_BITS_OFFSET)
serial_req.port = pci_resource_start(dev, 0) >> HIGH_BITS_OFFSET;
#ifdef SERIAL_DEBUG_PNP
printk("Setup PNP port: port %x, irq %d, type %d\n",
serial_req.port, serial_req.irq, serial_req.io_type);
#endif
serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
serial_req.baud_base = 115200;
line = register_serial(&serial_req);
if (line >= 0) {
pci_set_drvdata(dev, (void *)(line + 1));
/*
* Public health warning: remove this once the 2.5
* pnpbios_module_init() stuff is incorporated.
*/
dev->driver = (void *)pnp_dev_table;
} else
dev->deactivate(dev);
return line >= 0 ? 0 : -ENODEV;
}
static void pnp_remove_one(struct pci_dev *dev)
{
int line = (int)pci_get_drvdata(dev);
if (line) {
pci_set_drvdata(dev, NULL);
unregister_serial(line - 1);
dev->deactivate(dev);
}
}
static char hex[] = "0123456789ABCDEF";
/*
* This function should vanish when 2.5 comes around and
* we have pnpbios_module_init()
*/
static int pnp_init(void)
{
const struct pnpbios_device_id *id;
struct pci_dev *dev = NULL;
int nr = 0, rc = -ENODEV;
#ifdef SERIAL_DEBUG_PNP
printk("Entered probe_serial_pnp()\n");
#endif
isapnp_for_each_dev(dev) {
char slot_name[8];
u32 pnpid;
if (dev->active)
continue;
pnpid = dev->vendor << 16 | dev->device;
pnpid = cpu_to_le32(pnpid);
#define HEX(id,a) hex[((id)>>a) & 15]
#define CHAR(id,a) (0x40 + (((id)>>a) & 31))
slot_name[0] = CHAR(pnpid, 26);
slot_name[1] = CHAR(pnpid, 21);
slot_name[2] = CHAR(pnpid, 16);
slot_name[3] = HEX(pnpid, 12);
slot_name[4] = HEX(pnpid, 8);
slot_name[5] = HEX(pnpid, 4);
slot_name[6] = HEX(pnpid, 0);
slot_name[7] = '\0';
for (id = pnp_dev_table; id->id[0]; id++)
if (memcmp(id->id, slot_name, 7) == 0)
break;
if (id->id[0])
rc = pnp_init_one(dev, id, slot_name);
else
rc = pnp_init_one(dev, NULL, slot_name);
if (rc == 0)
nr++;
}
#ifdef SERIAL_DEBUG_PNP
printk("Leaving probe_serial_pnp() (probe finished)\n");
#endif
return nr == 0 ? rc : 0;
}
static int __init serial8250_pnp_init(void)
{
if (!isapnp_present()) {
#ifdef SERIAL_DEBUG_PNP
printk("Leaving probe_serial_pnp() (no isapnp)\n");
#endif
return -ENODEV;
}
return pnp_init();
}
static void __exit serial8250_pnp_exit(void)
{
struct pci_dev *dev = NULL;
isapnp_for_each_dev(dev) {
if (dev->driver != (void *)pnp_dev_table)
continue;
pnp_remove_one(dev);
}
}
module_init(serial8250_pnp_init);
module_exit(serial8250_pnp_exit);
EXPORT_NO_SYMBOLS;
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 PNPBIOS serial probe module");
MODULE_DEVICE_TABLE(pnpbios, pnp_dev_table);
/*
* linux/drivers/char/serial_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: serial_amba.c,v 1.35 2002/07/21 08:57:55 rmk Exp $
*
* This is a generic driver for ARM AMBA-type serial ports. They
* have a lot of 16550-like features, but are not register compatable.
* 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.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
#include <asm/hardware/serial_amba.h>
#define UART_NR 2
#define SERIAL_AMBA_MAJOR 204
#define SERIAL_AMBA_MINOR 16
#define SERIAL_AMBA_NR UART_NR
#define AMBA_ISR_PASS_LIMIT 256
/*
* Access macros for the AMBA UARTs
*/
#define UART_GET_INT_STATUS(p) readb((p)->membase + AMBA_UARTIIR)
#define UART_PUT_ICR(p, c) writel((c), (p)->membase + AMBA_UARTICR)
#define UART_GET_FR(p) readb((p)->membase + AMBA_UARTFR)
#define UART_GET_CHAR(p) readb((p)->membase + AMBA_UARTDR)
#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + AMBA_UARTDR)
#define UART_GET_RSR(p) readb((p)->membase + AMBA_UARTRSR)
#define UART_GET_CR(p) readb((p)->membase + AMBA_UARTCR)
#define UART_PUT_CR(p,c) writel((c), (p)->membase + AMBA_UARTCR)
#define UART_GET_LCRL(p) readb((p)->membase + AMBA_UARTLCR_L)
#define UART_PUT_LCRL(p,c) writel((c), (p)->membase + AMBA_UARTLCR_L)
#define UART_GET_LCRM(p) readb((p)->membase + AMBA_UARTLCR_M)
#define UART_PUT_LCRM(p,c) writel((c), (p)->membase + AMBA_UARTLCR_M)
#define UART_GET_LCRH(p) readb((p)->membase + AMBA_UARTLCR_H)
#define UART_PUT_LCRH(p,c) writel((c), (p)->membase + AMBA_UARTLCR_H)
#define UART_RX_DATA(s) (((s) & AMBA_UARTFR_RXFE) == 0)
#define UART_TX_READY(s) (((s) & AMBA_UARTFR_TXFF) == 0)
#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0)
#define UART_DUMMY_RSR_RX 256
#define UART_PORT_SIZE 64
/*
* On the Integrator platform, the port RTS and DTR are provided by
* bits in the following SC_CTRLS register bits:
* RTS DTR
* UART0 7 6
* UART1 5 4
*/
#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
/*
* We wrap our port structure around the generic uart_port.
*/
struct uart_amba_port {
struct uart_port port;
unsigned int dtr_mask;
unsigned int rts_mask;
unsigned int old_status;
};
static void __ambauart_stop_tx(struct uart_port *port)
{
unsigned int cr;
cr = UART_GET_CR(port);
cr &= ~AMBA_UARTCR_TIE;
UART_PUT_CR(port, cr);
}
static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__ambauart_stop_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
static void ambauart_start_tx(struct uart_port *port, unsigned int tty_start)
{
unsigned int cr;
cr = UART_GET_CR(port);
cr |= AMBA_UARTCR_TIE;
UART_PUT_CR(port, cr);
}
static void ambauart_stop_rx(struct uart_port *port)
{
unsigned long flags;
unsigned int cr;
spin_lock_irqsave(&port->lock, flags);
cr = UART_GET_CR(port);
cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE);
UART_PUT_CR(port, cr);
spin_unlock_irqrestore(&port->lock, flags);
}
static void ambauart_enable_ms(struct uart_port *port)
{
unsigned long flags;
unsigned int cr;
spin_lock_irqsave(&port->lock, flags);
cr = UART_GET_CR(port);
cr |= AMBA_UARTCR_MSIE;
UART_PUT_CR(port, cr);
spin_unlock_irqrestore(&port->lock, flags);
}
static void
#ifdef SUPPORT_SYSRQ
ambauart_rx_chars(struct uart_port *port, struct pt_regs *regs)
#else
ambauart_rx_chars(struct uart_port *port)
#endif
{
struct tty_struct *tty = port->info->tty;
unsigned int status, ch, rsr, max_count = 256;
status = UART_GET_FR(port);
while (UART_RX_DATA(status) && max_count--) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty->flip.tqueue.routine((void *)tty);
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
printk(KERN_WARNING "TTY_DONT_FLIP set\n");
return;
}
}
ch = UART_GET_CHAR(port);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
/*
* Note that the error handling code is
* out of the main execution path
*/
rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX;
if (rsr & AMBA_UARTRSR_ANY) {
if (rsr & AMBA_UARTRSR_BE) {
rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE);
port->icount.brk++;
if (uart_handle_break(port))
goto ignore_char;
} else if (rsr & AMBA_UARTRSR_PE)
port->icount.parity++;
else if (rsr & AMBA_UARTRSR_FE)
port->icount.frame++;
if (rsr & AMBA_UARTRSR_OE)
port->icount.overrun++;
rsr &= port->read_status_mask;
if (rsr & AMBA_UARTRSR_BE)
*tty->flip.flag_buf_ptr = TTY_BREAK;
else if (rsr & AMBA_UARTRSR_PE)
*tty->flip.flag_buf_ptr = TTY_PARITY;
else if (rsr & AMBA_UARTRSR_FE)
*tty->flip.flag_buf_ptr = TTY_FRAME;
}
if (uart_handle_sysrq_char(port, ch, regs))
goto ignore_char;
if ((rsr & port->ignore_status_mask) == 0) {
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
if ((rsr & AMBA_UARTRSR_OE) &&
tty->flip.count < TTY_FLIPBUF_SIZE) {
/*
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* character
*/
*tty->flip.char_buf_ptr++ = 0;
*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
tty->flip.count++;
}
ignore_char:
status = UART_GET_FR(port);
}
tty_flip_buffer_push(tty);
return;
}
static void ambauart_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->info->xmit;
int count;
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)) {
__ambauart_stop_tx(port);
return;
}
count = port->fifosize >> 1;
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);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_event(port, EVT_WRITE_WAKEUP);
if (uart_circ_empty(xmit))
__ambauart_stop_tx(port);
}
static void ambauart_modem_status(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int status, delta;
UART_PUT_ICR(&uap->port, 0);
status = UART_GET_FR(&uap->port) & AMBA_UARTFR_MODEM_ANY;
delta = status ^ uap->old_status;
uap->old_status = status;
if (!delta)
return;
if (delta & AMBA_UARTFR_DCD)
uart_handle_dcd_change(&uap->port, status & AMBA_UARTFR_DCD);
if (delta & AMBA_UARTFR_DSR)
uap->port.icount.dsr++;
if (delta & AMBA_UARTFR_CTS)
uart_handle_cts_change(&uap->port, status & AMBA_UARTFR_CTS);
wake_up_interruptible(&uap->port.info->delta_msr_wait);
}
static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
status = UART_GET_INT_STATUS(port);
do {
if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS))
#ifdef SUPPORT_SYSRQ
ambauart_rx_chars(port, regs);
#else
ambauart_rx_chars(port);
#endif
if (status & AMBA_UARTIIR_MIS)
ambauart_modem_status(port);
if (status & AMBA_UARTIIR_TIS)
ambauart_tx_chars(port);
if (pass_counter-- == 0)
break;
status = UART_GET_INT_STATUS(port);
} while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS |
AMBA_UARTIIR_TIS));
}
static unsigned int ambauart_tx_empty(struct uart_port *port)
{
return UART_GET_FR(port) & AMBA_UARTFR_BUSY ? 0 : TIOCSER_TEMT;
}
static unsigned int ambauart_get_mctrl(struct uart_port *port)
{
unsigned int result = 0;
unsigned int status;
status = UART_GET_FR(port);
if (status & AMBA_UARTFR_DCD)
result |= TIOCM_CAR;
if (status & AMBA_UARTFR_DSR)
result |= TIOCM_DSR;
if (status & AMBA_UARTFR_CTS)
result |= TIOCM_CTS;
return result;
}
static void ambauart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int ctrls = 0, ctrlc = 0;
if (mctrl & TIOCM_RTS)
ctrlc |= uap->rts_mask;
else
ctrls |= uap->rts_mask;
if (mctrl & TIOCM_DTR)
ctrlc |= uap->dtr_mask;
else
ctrls |= uap->dtr_mask;
__raw_writel(ctrls, SC_CTRLS);
__raw_writel(ctrlc, SC_CTRLC);
}
static void ambauart_break_ctl(struct uart_port *port, int break_state)
{
unsigned long flags;
unsigned int lcr_h;
spin_lock_irqsave(&port->lock, flags);
lcr_h = UART_GET_LCRH(port);
if (break_state == -1)
lcr_h |= AMBA_UARTLCR_H_BRK;
else
lcr_h &= ~AMBA_UARTLCR_H_BRK;
UART_PUT_LCRH(port, lcr_h);
spin_unlock_irqrestore(&port->lock, flags);
}
static int ambauart_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, ambauart_int, 0, "amba", port);
if (retval)
return retval;
/*
* initialise the old status of the modem signals
*/
uap->old_status = UART_GET_FR(port) & AMBA_UARTFR_MODEM_ANY;
/*
* Finally, enable interrupts
*/
UART_PUT_CR(port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE |
AMBA_UARTCR_RTIE);
return 0;
}
static void ambauart_shutdown(struct uart_port *port)
{
/*
* Free the interrupt
*/
free_irq(port->irq, port);
/*
* disable all interrupts, disable the port
*/
UART_PUT_CR(port, 0);
/* disable break condition and fifos */
UART_PUT_LCRH(port, UART_GET_LCRH(port) &
~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN));
}
static void
ambauart_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
unsigned int lcr_h, old_cr;
unsigned long flags;
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5:
lcr_h = AMBA_UARTLCR_H_WLEN_5;
break;
case CS6:
lcr_h = AMBA_UARTLCR_H_WLEN_6;
break;
case CS7:
lcr_h = AMBA_UARTLCR_H_WLEN_7;
break;
default: // CS8
lcr_h = AMBA_UARTLCR_H_WLEN_8;
break;
}
if (cflag & CSTOPB)
lcr_h |= AMBA_UARTLCR_H_STP2;
if (cflag & PARENB) {
lcr_h |= AMBA_UARTLCR_H_PEN;
if (!(cflag & PARODD))
lcr_h |= AMBA_UARTLCR_H_EPS;
}
if (port->fifosize > 1)
lcr_h |= AMBA_UARTLCR_H_FEN;
port->read_status_mask = AMBA_UARTRSR_OE;
if (iflag & INPCK)
port->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
if (iflag & (BRKINT | PARMRK))
port->read_status_mask |= AMBA_UARTRSR_BE;
/*
* Characters to ignore
*/
port->ignore_status_mask = 0;
if (iflag & IGNPAR)
port->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
if (iflag & IGNBRK) {
port->ignore_status_mask |= AMBA_UARTRSR_BE;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (iflag & IGNPAR)
port->ignore_status_mask |= AMBA_UARTRSR_OE;
}
/*
* Ignore all characters if CREAD is not set.
*/
if ((cflag & CREAD) == 0)
port->ignore_status_mask |= UART_DUMMY_RSR_RX;
/* first, disable everything */
spin_lock_irqsave(&port->lock, flags);
old_cr = UART_GET_CR(port) & ~AMBA_UARTCR_MSIE;
if (UART_ENABLE_MS(port, cflag))
old_cr |= AMBA_UARTCR_MSIE;
UART_PUT_CR(port, 0);
/* Set baud rate */
quot -= 1;
UART_PUT_LCRM(port, ((quot & 0xf00) >> 8));
UART_PUT_LCRL(port, (quot & 0xff));
/*
* ----------v----------v----------v----------v-----
* NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
* ----------^----------^----------^----------^-----
*/
UART_PUT_LCRH(port, lcr_h);
UART_PUT_CR(port, old_cr);
spin_unlock_irqrestore(&port->lock, flags);
}
static const char *ambauart_type(struct uart_port *port)
{
return port->type == PORT_AMBA ? "AMBA" : NULL;
}
/*
* Release the memory region(s) being used by 'port'
*/
static void ambauart_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 ambauart_request_port(struct uart_port *port)
{
return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_amba")
!= NULL ? 0 : -EBUSY;
}
/*
* Configure/autoconfigure the port.
*/
static void ambauart_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE) {
port->type = PORT_AMBA;
ambauart_request_port(port);
}
}
/*
* verify the new serial_struct (for TIOCSSERIAL).
*/
static int ambauart_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_pops = {
tx_empty: ambauart_tx_empty,
set_mctrl: ambauart_set_mctrl,
get_mctrl: ambauart_get_mctrl,
stop_tx: ambauart_stop_tx,
start_tx: ambauart_start_tx,
stop_rx: ambauart_stop_rx,
enable_ms: ambauart_enable_ms,
break_ctl: ambauart_break_ctl,
startup: ambauart_startup,
shutdown: ambauart_shutdown,
change_speed: ambauart_change_speed,
type: ambauart_type,
release_port: ambauart_release_port,
request_port: ambauart_request_port,
config_port: ambauart_config_port,
verify_port: ambauart_verify_port,
};
static struct uart_amba_port amba_ports[UART_NR] = {
{
port: {
membase: (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE),
mapbase: INTEGRATOR_UART0_BASE,
iotype: SERIAL_IO_MEM,
irq: IRQ_UARTINT0,
uartclk: 14745600,
fifosize: 16,
ops: &amba_pops,
flags: ASYNC_BOOT_AUTOCONF,
line: 0,
},
dtr_mask: 1 << 5,
rts_mask: 1 << 4,
},
{
port: {
membase: (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE),
mapbase: INTEGRATOR_UART1_BASE,
iotype: SERIAL_IO_MEM,
irq: IRQ_UARTINT1,
uartclk: 14745600,
fifosize: 16,
ops: &amba_pops,
flags: ASYNC_BOOT_AUTOCONF,
line: 1,
},
dtr_mask: 1 << 7,
rts_mask: 1 << 6,
}
};
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
static void
ambauart_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;
int i;
/*
* First save the CR then disable the interrupts
*/
old_cr = UART_GET_CR(port);
UART_PUT_CR(port, AMBA_UARTCR_UARTEN);
/*
* Now, do each character
*/
for (i = 0; i < count; i++) {
do {
status = UART_GET_FR(port);
} while (!UART_TX_READY(status));
UART_PUT_CHAR(port, s[i]);
if (s[i] == '\n') {
do {
status = UART_GET_FR(port);
} while (!UART_TX_READY(status));
UART_PUT_CHAR(port, '\r');
}
}
/*
* Finally, wait for transmitter to become empty
* and restore the TCR
*/
do {
status = UART_GET_FR(port);
} while (status & AMBA_UARTFR_BUSY);
UART_PUT_CR(port, old_cr);
}
static kdev_t ambauart_console_device(struct console *co)
{
return mk_kdev(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + co->index);
}
static void __init
ambauart_console_get_options(struct uart_port *port, int *baud,
int *parity, int *bits)
{
if (UART_GET_CR(port) & AMBA_UARTCR_UARTEN) {
unsigned int lcr_h, quot;
lcr_h = UART_GET_LCRH(port);
*parity = 'n';
if (lcr_h & AMBA_UARTLCR_H_PEN) {
if (lcr_h & AMBA_UARTLCR_H_EPS)
*parity = 'e';
else
*parity = 'o';
}
if ((lcr_h & 0x60) == AMBA_UARTLCR_H_WLEN_7)
*bits = 7;
else
*bits = 8;
quot = UART_GET_LCRL(port) | UART_GET_LCRM(port) << 8;
*baud = port->uartclk / (16 * (quot + 1));
}
}
static int __init ambauart_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;
port = &amba_ports[co->index].port;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
ambauart_console_get_options(port, &baud, &parity, &bits);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct console amba_console = {
name: "ttyAM",
write: ambauart_console_write,
device: ambauart_console_device,
setup: ambauart_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
void __init ambauart_console_init(void)
{
register_console(&amba_console);
}
#define AMBA_CONSOLE &amba_console
#else
#define AMBA_CONSOLE NULL
#endif
static struct uart_driver amba_reg = {
owner: THIS_MODULE,
driver_name: "ttyAM",
#ifdef CONFIG_DEVFS_FS
dev_name: "ttyAM%d",
#else
dev_name: "ttyAM",
#endif
major: SERIAL_AMBA_MAJOR,
minor: SERIAL_AMBA_MINOR,
nr: UART_NR,
cons: AMBA_CONSOLE,
};
static int __init ambauart_init(void)
{
int ret;
printk(KERN_INFO "Serial: AMBA driver $Revision: 1.35 $\n");
ret = uart_register_driver(&amba_reg);
if (ret == 0) {
int i;
for (i = 0; i < UART_NR; i++)
uart_add_one_port(&amba_reg, &amba_ports[i].port);
}
return ret;
}
static void __exit ambauart_exit(void)
{
int i;
for (i = 0; i < UART_NR; i++)
uart_remove_one_port(&amba_reg, &amba_ports[i].port);
uart_unregister_driver(&amba_reg);
}
module_init(ambauart_init);
module_exit(ambauart_exit);
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.35 $");
MODULE_LICENSE("GPL");
/*
* linux/drivers/char/serial_anakin.c
*
* Based on driver for AMBA serial ports, by ARM Limited,
* Deep Blue Solutions Ltd., Linus Torvalds and Theodore Ts'o.
*
* Copyright (C) 2001 Aleph One Ltd. for Acunia N.V.
*
* Copyright (C) 2001 Blue Mug, Inc. for Acunia N.V.
*
* 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.
*
* Changelog:
* 20-Apr-2001 TTC Created
* 05-May-2001 W/TTC Updated for serial_core.c
* 27-Jun-2001 jonm Minor changes; add mctrl support, switch to
* SA_INTERRUPT. Works reliably now. No longer requires
* changes to the serial_core API.
*
* $Id: serial_anakin.c,v 1.27 2002/07/20 17:10:03 rmk Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/serial_core.h>
#include <asm/arch/serial_reg.h>
#define UART_NR 5
#define SERIAL_ANAKIN_NAME "ttyAN"
#define SERIAL_ANAKIN_MAJOR 204
#define SERIAL_ANAKIN_MINOR 32
static unsigned int txenable[NR_IRQS]; /* Software interrupt register */
static inline unsigned int
anakin_in(struct uart_port *port, unsigned int offset)
{
return __raw_readl(port->base + offset);
}
static inline void
anakin_out(struct uart_port *port, unsigned int offset, unsigned int value)
{
__raw_writel(value, port->base + offset);
}
static void
anakin_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
txenable[port->irq] = 0;
}
static inline void
anakin_transmit_buffer(struct uart_port *port)
{
struct circ_buf *xmit = &port->info->xmit;
while (!(anakin_in(port, 0x10) & TXEMPTY));
anakin_out(port, 0x14, xmit->buf[xmit->tail]);
anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
port->icount.tx++;
if (uart_circ_empty(xmit))
anakin_stop_tx(port, 0);
}
static inline void
anakin_transmit_x_char(struct uart_port *port)
{
anakin_out(port, 0x14, port->x_char);
anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
port->icount.tx++;
port->x_char = 0;
}
static void
anakin_start_tx(struct uart_port *port, unsigned int tty_start)
{
unsigned int flags;
spin_lock_irqsave(&port->lock, flags);
// is it this... or below
if (!txenable[port->irq]) {
txenable[port->irq] = TXENABLE;
if ((anakin_in(port, 0x10) & TXEMPTY)) {
anakin_transmit_buffer(port);
}
}
spin_unlock_irqrestore(&port->lock, flags);
}
static void
anakin_stop_rx(struct uart_port *port)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
while (anakin_in(port, 0x10) & RXRELEASE)
anakin_in(port, 0x14);
anakin_out(port, 0x18, anakin_in(port, 0x18) | BLOCKRX);
spin_unlock_irqrestore(&port->lock, flags);
}
static void
anakin_enable_ms(struct uart_port *port)
{
}
static inline void
anakin_rx_chars(struct uart_port *port)
{
unsigned int ch;
struct tty_struct *tty = port->info->tty;
if (!(anakin_in(port, 0x10) & RXRELEASE))
return;
ch = anakin_in(port, 0x14) & 0xff;
if (tty->flip.count < TTY_FLIPBUF_SIZE) {
*tty->flip.char_buf_ptr++ = ch;
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
port->icount.rx++;
tty->flip.count++;
}
tty_flip_buffer_push(tty);
}
static inline void
anakin_overrun_chars(struct uart_port *port)
{
unsigned int ch;
ch = anakin_in(port, 0x14);
port->icount.overrun++;
}
static inline void
anakin_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->info->xmit;
if (port->x_char) {
anakin_transmit_x_char(port);
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
anakin_stop_tx(port, 0);
return;
}
anakin_transmit_buffer(port);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_event(port, EVT_WRITE_WAKEUP);
}
static void
anakin_int(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned int status;
struct uart_port *port = dev_id;
status = anakin_in(port, 0x1c);
if (status & RX)
anakin_rx_chars(port);
if (status & OVERRUN)
anakin_overrun_chars(port);
if (txenable[port->irq] && (status & TX))
anakin_tx_chars(port);
}
static unsigned int
anakin_tx_empty(struct uart_port *port)
{
return anakin_in(port, 0x10) & TXEMPTY ? TIOCSER_TEMT : 0;
}
static unsigned int
anakin_get_mctrl(struct uart_port *port)
{
unsigned int status = 0;
status |= (anakin_in(port, 0x10) & CTS ? TIOCM_CTS : 0);
status |= (anakin_in(port, 0x18) & DCD ? TIOCM_CAR : 0);
status |= (anakin_in(port, 0x18) & DTR ? TIOCM_DTR : 0);
status |= (anakin_in(port, 0x18) & RTS ? TIOCM_RTS : 0);
return status;
}
static void
anakin_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned int status;
status = anakin_in(port, 0x18);
if (mctrl & TIOCM_RTS)
status |= RTS;
else
status &= ~RTS;
if (mctrl & TIOCM_CAR)
status |= DCD;
else
status &= ~DCD;
anakin_out(port, 0x18, status);
}
static void
anakin_break_ctl(struct uart_port *port, int break_state)
{
unsigned long flags;
unsigned int status;
spin_lock_irqsave(&port->lock, flags);
status = anakin_in(port, 0x20);
if (break_state == -1)
status |= SETBREAK;
else
status &= ~SETBREAK;
anakin_out(port, 0x20, status);
spin_unlock_irqrestore(&port->lock, flags);
}
static int anakin_startup(struct uart_port *port)
{
int retval;
unsigned int read,write;
/*
* Allocate the IRQ
*/
retval = request_irq(port->irq, anakin_int, SA_INTERRUPT,
"serial_anakin", port);
if (retval)
return retval;
/*
* initialise the old status of the modem signals
*/
port->old_status = 0;
/*
* Finally, disable IRQ and softIRQs for first byte)
*/
txenable[port->irq] = 0;
read = anakin_in(port, 0x18);
write = (read & ~(RTS | DTR | BLOCKRX)) | IRQENABLE;
anakin_out(port, 0x18, write);
return 0;
}
static void anakin_shutdown(struct uart_port *port)
{
/*
* Free the interrupt
*/
free_irq(port->irq, port);
/*
* disable all interrupts, disable the port
*/
anakin_out(port, 0x18, anakin_in(port, 0x18) & ~IRQENABLE);
}
static void
anakin_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
unsigned int flags;
spin_lock_irqsave(&port->lock, flags);
while (!(anakin_in(port, 0x10) & TXEMPTY));
anakin_out(port, 0x10, (anakin_in(port, 0x10) & ~PRESCALER)
| (quot << 3));
//parity always set to none
anakin_out(port, 0x18, anakin_in(port, 0x18) & ~PARITY);
spin_unlock_irqrestore(&port->lock, flags);
}
static const char *anakin_type(struct port *port)
{
return port->type == PORT_ANAKIN ? "ANAKIN" : NULL;
}
static struct uart_ops anakin_pops = {
tx_empty: anakin_tx_empty,
set_mctrl: anakin_set_mctrl,
get_mctrl: anakin_get_mctrl,
stop_tx: anakin_stop_tx,
start_tx: anakin_start_tx,
stop_rx: anakin_stop_rx,
enable_ms: anakin_enable_ms,
break_ctl: anakin_break_ctl,
startup: anakin_startup,
shutdown: anakin_shutdown,
change_speed: anakin_change_speed,
type: anakin_type,
};
static struct uart_port anakin_ports[UART_NR] = {
{
base: IO_BASE + UART0,
irq: IRQ_UART0,
uartclk: 3686400,
fifosize: 0,
ops: &anakin_pops,
flags: ASYNC_BOOT_AUTOCONF,
line: 0,
},
{
base: IO_BASE + UART1,
irq: IRQ_UART1,
uartclk: 3686400,
fifosize: 0,
ops: &anakin_pops,
flags: ASYNC_BOOT_AUTOCONF,
line: 1,
},
{
base: IO_BASE + UART2,
irq: IRQ_UART2,
uartclk: 3686400,
fifosize: 0,
ops: &anakin_pops,
flags: ASYNC_BOOT_AUTOCONF,
line: 2,
},
{
base: IO_BASE + UART3,
irq: IRQ_UART3,
uartclk: 3686400,
fifosize: 0,
ops: &anakin_pops,
flags: ASYNC_BOOT_AUTOCONF,
line: 3,
},
{
base: IO_BASE + UART4,
irq: IRQ_UART4,
uartclk: 3686400,
fifosize: 0,
ops: &anakin_pops,
flags: ASYNC_BOOT_AUTOCONF,
line: 4,
},
};
#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE
static void
anakin_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_port *port = &anakin_ports[co->index];
unsigned int flags, status, i;
/*
* First save the status then disable the interrupts
*/
local_irq_save(flags);
status = anakin_in(port, 0x18);
anakin_out(port, 0x18, status & ~IRQENABLE);
local_irq_restore(flags);
/*
* Now, do each character
*/
for (i = 0; i < count; i++, s++) {
while (!(anakin_in(port, 0x10) & TXEMPTY));
/*
* Send the character out.
* If a LF, also do CR...
*/
anakin_out(port, 0x14, *s);
anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
if (*s == 10) {
while (!(anakin_in(port, 0x10) & TXEMPTY));
anakin_out(port, 0x14, 13);
anakin_out(port, 0x18, anakin_in(port, 0x18)
| SENDREQUEST);
}
}
/*
* Finally, wait for transmitter to become empty
* and restore the interrupts
*/
while (!(anakin_in(port, 0x10) & TXEMPTY));
if (status & IRQENABLE) {
local_irq_save(flags);
anakin_out(port, 0x18, anakin_in(port, 0x18) | IRQENABLE);
local_irq_restore(flags);
}
}
static kdev_t
anakin_console_device(struct console *co)
{
return mk_kdev(SERIAL_ANAKIN_MAJOR, SERIAL_ANAKIN_MINOR + co->index);
}
/*
* Read the current UART setup.
*/
static void __init
anakin_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
{
int paritycode;
*baud = GETBAUD (anakin_in(port, 0x10) & PRESCALER);
paritycode = GETPARITY(anakin_in(port, 0x18) & PARITY);
switch (paritycode) {
case NONEPARITY: *parity = 'n'; break;
case ODDPARITY: *parity = 'o'; break;
case EVENPARITY: *parity = 'e'; break;
}
*bits = 8;
}
static int __init
anakin_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = CONFIG_ANAKIN_DEFAULT_BAUDRATE;
int bits = 8;
int parity = '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;
port = &anakin_ports[co->index];
if (options)
uart_parse_options(options, &baud, &parity, &bits);
else
anakin_console_get_options(port, &baud, &parity, &bits);
return uart_set_options(port, co, baud, parity, bits);
}
static struct console anakin_console = {
name: SERIAL_ANAKIN_NAME,
write: anakin_console_write,
device: anakin_console_device,
setup: anakin_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
void __init
anakin_console_init(void)
{
register_console(&anakin_console);
}
#define ANAKIN_CONSOLE &anakin_console
#else
#define ANAKIN_CONSOLE NULL
#endif
static struct uart_register anakin_reg = {
driver_name: SERIAL_ANAKIN_NAME,
dev_name: SERIAL_ANAKIN_NAME,
major: SERIAL_ANAKIN_MAJOR,
minor: SERIAL_ANAKIN_MINOR,
nr: UART_NR,
cons: ANAKIN_CONSOLE,
};
static int __init
anakin_init(void)
{
int ret;
printk(KERN_INFO "Serial: Anakin driver $Revision: 1.27 $\n");
ret = uart_register_driver(&anakin_reg);
if (ret == 0) {
int i;
for (i = 0; i < UART_NR; i++)
uart_add_one_port(&anakin_reg, &anakin_ports[i]);
}
return ret;
}
__initcall(anakin_init);
MODULE_DESCRIPTION("Anakin serial driver");
MODULE_AUTHOR("Tak-Shing Chan <chan@aleph1.co.uk>");
MODULE_SUPPORTED_DEVICE("ttyAN");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
/*
* linux/drivers/char/serial_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: serial_clps711x.c,v 1.38 2002/07/21 08:57:55 rmk Exp $
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/spinlock.h>
#include <asm/bitops.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
#include <asm/hardware/clps7111.h>
#define UART_NR 2
#ifndef CONFIG_SERIAL_CLPS711X_OLD_NAME
#define SERIAL_CLPS711X_NAME "ttyCL"
#define SERIAL_CLPS711X_MAJOR 204
#define SERIAL_CLPS711X_MINOR 40
#define SERIAL_CLPS711X_NR UART_NR
#else
#warning The old names/device number for this driver if compatabity is needed
#define SERIAL_CLPS711X_NAME "ttyAM"
#define SERIAL_CLPS711X_MAJOR 204
#define SERIAL_CLPS711X_MINOR 16
#define SERIAL_CLPS711X_NR UART_NR
#endif
/*
* 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_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__clps711xuart_stop_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
static void
clps711xuart_start_tx(struct uart_port *port, unsigned int tty_start)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
if (!tx_enabled(port)) {
enable_irq(TX_IRQ(port));
tx_enabled(port) = 1;
}
spin_unlock_irqrestore(&port->lock, flags);
}
static void clps711xuart_stop_rx(struct uart_port *port)
{
disable_irq(RX_IRQ(port));
}
static void clps711xuart_enable_ms(struct uart_port *port)
{
}
static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
struct tty_struct *tty = port->info->tty;
unsigned int status, ch, flg, ignored = 0;
status = clps_readl(SYSFLG(port));
while (!(status & SYSFLG_URXFE)) {
ch = clps_readl(UARTDR(port));
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;
port->icount.rx++;
flg = TTY_NORMAL;
/*
* Note that the error handling code is
* out of the main execution path
*/
if (ch & UART_ANY_ERR)
goto handle_error;
if (uart_handle_sysrq_char(port, ch, regs))
goto ignore_char;
error_return:
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
ignore_char:
status = clps_readl(SYSFLG(port));
}
out:
tty_flip_buffer_push(tty);
return;
handle_error:
if (ch & UARTDR_PARERR)
port->icount.parity++;
else if (ch & UARTDR_FRMERR)
port->icount.frame++;
if (ch & UARTDR_OVERR)
port->icount.overrun++;
if (ch & port->ignore_status_mask) {
if (++ignored > 100)
goto out;
goto ignore_char;
}
ch &= port->read_status_mask;
if (ch & UARTDR_PARERR)
flg = TTY_PARITY;
else if (ch & UARTDR_FRMERR)
flg = TTY_FRAME;
if (ch & UARTDR_OVERR) {
/*
* CHECK: does overrun affect the current character?
* ASSUMPTION: it does not.
*/
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;
ch = 0;
flg = TTY_OVERRUN;
}
#ifdef SUPPORT_SYSRQ
port->sysrq = 0;
#endif
goto error_return;
}
static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs)
{
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;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
__clps711xuart_stop_tx(port);
return;
}
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_event(port, EVT_WRITE_WAKEUP);
if (uart_circ_empty(xmit))
__clps711xuart_stop_tx(port);
}
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_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
unsigned int ubrlcr;
unsigned long flags;
/* byte size and parity */
switch (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 (cflag & CSTOPB)
ubrlcr |= UBRLCR_XSTOP;
if (cflag & PARENB) {
ubrlcr |= UBRLCR_PRTEN;
if (!(cflag & PARODD))
ubrlcr |= UBRLCR_EVENPRT;
}
if (port->fifosize > 1)
ubrlcr |= UBRLCR_FIFOEN;
port->read_status_mask = UARTDR_OVERR;
if (iflag & INPCK)
port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
/*
* Characters to ignore
*/
port->ignore_status_mask = 0;
if (iflag & IGNPAR)
port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
if (iflag & IGNBRK) {
/*
* If we're ignoring parity and break indicators,
* ignore overruns to (for real raw support).
*/
if (iflag & IGNPAR)
port->ignore_status_mask |= UARTDR_OVERR;
}
quot -= 1;
/* first, disable everything */
spin_lock_irqsave(&port->lock, flags);
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,
change_speed: clps711xuart_change_speed,
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,
flags: ASYNC_BOOT_AUTOCONF,
},
{
iobase: SYSCON2,
irq: IRQ_UTXINT2, /* IRQ_URXINT2 */
uartclk: 3686400,
fifosize: 16,
ops: &clps711x_pops,
flags: ASYNC_BOOT_AUTOCONF,
}
};
#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
/*
* 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;
int i;
/*
* Ensure that the port is enabled.
*/
syscon = clps_readl(SYSCON(port));
clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
/*
* Now, do each character
*/
for (i = 0; i < count; i++) {
do {
status = clps_readl(SYSFLG(port));
} while (status & SYSFLG_UTXFF);
clps_writel(s[i], UARTDR(port));
if (s[i] == '\n') {
do {
status = clps_readl(SYSFLG(port));
} while (status & SYSFLG_UTXFF);
clps_writel('\r', UARTDR(port));
}
}
/*
* 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 kdev_t clps711xuart_console_device(struct console *co)
{
return mk_kdev(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR + co->index);
}
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 console clps711x_console = {
name: SERIAL_CLPS711X_NAME,
write: clps711xuart_console_write,
device: clps711xuart_console_device,
setup: clps711xuart_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
void __init clps711xuart_console_init(void)
{
register_console(&clps711x_console);
}
#define CLPS711X_CONSOLE &clps711x_console
#else
#define CLPS711X_CONSOLE NULL
#endif
static struct uart_driver clps711x_reg = {
driver_name: "ttyCL",
#ifdef CONFIG_DEVFS_FS
dev_name: SERIAL_CLPS711X_NAME,
#else
dev_name: SERIAL_CLPS711X_NAME,
#endif
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.38 $\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);
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Deep Blue Solutions Ltd");
MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.38 $");
MODULE_LICENSE("GPL");
/*
* linux/drivers/char/serial_core.c
*
* Driver core for serial ports
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
* Copyright 1999 ARM Limited
* Copyright (C) 2000-2001 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: serial_core.c,v 1.89 2002/07/20 18:07:32 rmk Exp $
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/pm.h>
#include <linux/serial_core.h>
#include <linux/smp_lock.h>
#include <linux/serial.h> /* for serial_state and serial_icounter_struct */
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#undef DEBUG
#ifdef DEBUG
#define DPRINTK(x...) printk(x)
#else
#define DPRINTK(x...) do { } while (0)
#endif
#ifndef CONFIG_PM
#define pm_access(pm) do { } while (0)
#define pm_unregister(pm) do { } while (0)
#endif
/*
* This is used to lock changes in serial line configuration.
*/
static DECLARE_MUTEX(port_sem);
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
static void uart_change_speed(struct uart_info *info, struct termios *old_termios);
static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
/*
* This routine is used by the interrupt handler to schedule processing in
* the software interrupt portion of the driver.
*/
void uart_event(struct uart_port *port, int event)
{
struct uart_info *info = port->info;
set_bit(0, &info->event);
tasklet_schedule(&info->tlet);
}
static void uart_stop(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
struct uart_port *port = info->port;
port->ops->stop_tx(port, 1);
}
static void __uart_start(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
struct uart_port *port = info->port;
if (!uart_circ_empty(&info->xmit) && info->xmit.buf &&
!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port, 1);
}
static void uart_start(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
unsigned long flags;
pm_access(info->state->pm);
spin_lock_irqsave(&info->port->lock, flags);
__uart_start(tty);
spin_unlock_irqrestore(&info->port->lock, flags);
}
static void uart_tasklet_action(unsigned long data)
{
struct uart_info *info = (struct uart_info *)data;
struct tty_struct *tty;
tty = info->tty;
if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event))
return;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
}
static inline void
uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
{
unsigned long flags;
unsigned int old;
spin_lock_irqsave(&port->lock, flags);
old = port->mctrl;
port->mctrl = (old & ~clear) | set;
if (old != port->mctrl)
port->ops->set_mctrl(port, port->mctrl);
spin_unlock_irqrestore(&port->lock, flags);
}
#define uart_set_mctrl(port,set) uart_update_mctrl(port,set,0)
#define uart_clear_mctrl(port,clear) uart_update_mctrl(port,0,clear)
static inline void uart_update_altspeed(struct uart_info *info)
{
unsigned int flags = info->port->flags & UPF_SPD_MASK;
if (flags == UPF_SPD_HI)
info->tty->alt_speed = 57600;
if (flags == UPF_SPD_VHI)
info->tty->alt_speed = 115200;
if (flags == UPF_SPD_SHI)
info->tty->alt_speed = 230400;
if (flags == UPF_SPD_WARP)
info->tty->alt_speed = 460800;
}
/*
* Startup the port. This will be called once per open. All calls
* will be serialised by the global port semaphore.
*/
static int uart_startup(struct uart_info *info, int init_hw)
{
struct uart_port *port = info->port;
unsigned long page;
int retval = 0;
if (info->flags & UIF_INITIALIZED)
return 0;
/*
* Set the TTY IO error marker - we will only clear this
* once we have successfully opened the port. Also set
* up the tty->alt_speed kludge
*/
if (info->tty) {
set_bit(TTY_IO_ERROR, &info->tty->flags);
uart_update_altspeed(info);
}
if (port->type == PORT_UNKNOWN)
return 0;
/*
* Initialise and allocate the transmit and temporary
* buffer.
*/
if (!info->xmit.buf) {
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
info->xmit.buf = (unsigned char *) page;
info->tmpbuf = info->xmit.buf + UART_XMIT_SIZE;
init_MUTEX(&info->tmpbuf_sem);
uart_circ_clear(&info->xmit);
}
port->mctrl = 0;
retval = port->ops->startup(port);
if (retval == 0) {
if (init_hw) {
/*
* Initialise the hardware port settings.
*/
uart_change_speed(info, NULL);
/*
* Setup the RTS and DTR signals once the
* port is open and ready to respond.
*/
if (info->tty->termios->c_cflag & CBAUD)
uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
}
info->flags |= UIF_INITIALIZED;
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
}
if (retval && capable(CAP_SYS_ADMIN))
retval = 0;
return retval;
}
/*
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on. Calls to
* uart_shutdown are serialised by port_sem.
*/
static void uart_shutdown(struct uart_info *info)
{
struct uart_port *port = info->port;
if (!(info->flags & UIF_INITIALIZED))
return;
/*
* Turn off DTR and RTS early.
*/
if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
uart_clear_mctrl(info->port, TIOCM_DTR | TIOCM_RTS);
/*
* clear delta_msr_wait queue to avoid mem leaks: we may free
* the irq here so the queue might never be woken up. Note
* that we won't end up waiting on delta_msr_wait again since
* any outstanding file descriptors should be pointing at
* hung_up_tty_fops now.
*/
wake_up_interruptible(&info->delta_msr_wait);
/*
* Free the IRQ and disable the port.
*/
port->ops->shutdown(port);
/*
* Ensure that the IRQ handler isn't running on another CPU.
*/
synchronize_irq(port->irq);
/*
* Free the transmit buffer page.
*/
if (info->xmit.buf) {
free_page((unsigned long)info->xmit.buf);
info->xmit.buf = NULL;
info->tmpbuf = NULL;
}
/*
* kill off our tasklet
*/
tasklet_kill(&info->tlet);
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~UIF_INITIALIZED;
}
static inline
unsigned int uart_calculate_quot(struct uart_info *info, unsigned int baud)
{
struct uart_port *port = info->port;
unsigned int quot;
/* Special case: B0 rate */
if (baud == 0)
baud = 9600;
/* Old HI/VHI/custom speed handling */
if (baud == 38400 &&
((port->flags & UPF_SPD_MASK) == UPF_SPD_CUST))
quot = info->state->custom_divisor;
else
quot = port->uartclk / (16 * baud);
return quot;
}
static void
uart_change_speed(struct uart_info *info, struct termios *old_termios)
{
struct uart_port *port = info->port;
unsigned int quot, cflag, bits, try;
/*
* If we have no tty, termios, or the port does not exist,
* then we can't set the parameters for this port.
*/
if (!info->tty || !info->tty->termios || port->type == PORT_UNKNOWN)
return;
/*
* Set flags based on termios cflag
*/
cflag = info->tty->termios->c_cflag;
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5:
bits = 7;
break;
case CS6:
bits = 8;
break;
case CS7:
bits = 9;
break;
default:
bits = 10;
break; // CS8
}
if (cflag & CSTOPB)
bits++;
if (cflag & PARENB)
bits++;
for (try = 0; try < 3; try ++) {
unsigned int baud;
/* Determine divisor based on baud rate */
baud = tty_get_baud_rate(info->tty);
quot = uart_calculate_quot(info, baud);
if (quot)
break;
/*
* Oops, the quotient was zero. Try again with
* the old baud rate if possible.
*/
info->tty->termios->c_cflag &= ~CBAUD;
if (old_termios) {
info->tty->termios->c_cflag |=
(old_termios->c_cflag & CBAUD);
old_termios = NULL;
continue;
}
/*
* As a last resort, if the quotient is zero,
* default to 9600 bps
*/
info->tty->termios->c_cflag |= B9600;
}
/*
* The total number of bits to be transmitted in the fifo.
*/
bits = bits * port->fifosize;
/*
* Figure the timeout to send the above number of bits.
* Add .02 seconds of slop
*/
port->timeout = (HZ * bits) / (port->uartclk / (16 * quot)) + HZ/50;
if (cflag & CRTSCTS)
info->flags |= UIF_CTS_FLOW;
else
info->flags &= ~UIF_CTS_FLOW;
if (cflag & CLOCAL)
info->flags &= ~UIF_CHECK_CD;
else
info->flags |= UIF_CHECK_CD;
port->ops->change_speed(port, cflag, info->tty->termios->c_iflag, quot);
}
static inline void
__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c)
{
unsigned long flags;
if (!circ->buf)
return;
spin_lock_irqsave(&port->lock, flags);
if (uart_circ_chars_free(circ) != 0) {
circ->buf[circ->head] = c;
circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
}
spin_unlock_irqrestore(&port->lock, flags);
}
static inline int
__uart_user_write(struct uart_port *port, struct circ_buf *circ,
const unsigned char *buf, int count)
{
unsigned long flags;
int c, ret = 0;
if (down_interruptible(&port->info->tmpbuf_sem))
return -EINTR;
while (1) {
int c1;
c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
c -= copy_from_user(port->info->tmpbuf, buf, c);
if (!c) {
if (!ret)
ret = -EFAULT;
break;
}
spin_lock_irqsave(&port->lock, flags);
c1 = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (c1 < c)
c = c1;
memcpy(circ->buf + circ->head, port->info->tmpbuf, c);
circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
spin_unlock_irqrestore(&port->lock, flags);
buf += c;
count -= c;
ret += c;
}
up(&port->info->tmpbuf_sem);
return ret;
}
static inline int
__uart_kern_write(struct uart_port *port, struct circ_buf *circ,
const unsigned char *buf, int count)
{
unsigned long flags;
int c, ret = 0;
spin_lock_irqsave(&port->lock, flags);
while (1) {
c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
memcpy(circ->buf + circ->head, buf, c);
circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
buf += c;
count -= c;
ret += c;
}
spin_unlock_irqrestore(&port->lock, flags);
return ret;
}
static void uart_put_char(struct tty_struct *tty, unsigned char ch)
{
struct uart_info *info = tty->driver_data;
if (tty)
__uart_put_char(info->port, &info->xmit, ch);
}
static void uart_flush_chars(struct tty_struct *tty)
{
uart_start(tty);
}
static int
uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf,
int count)
{
struct uart_info *info = tty->driver_data;
int ret;
if (!tty || !info->xmit.buf)
return 0;
if (from_user)
ret = __uart_user_write(info->port, &info->xmit, buf, count);
else
ret = __uart_kern_write(info->port, &info->xmit, buf, count);
uart_start(tty);
return ret;
}
static int uart_write_room(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
return uart_circ_chars_free(&info->xmit);
}
static int uart_chars_in_buffer(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
return uart_circ_chars_pending(&info->xmit);
}
static void uart_flush_buffer(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
unsigned long flags;
DPRINTK("uart_flush_buffer(%d) called\n",
MINOR(tty->device) - tty->driver.minor_start);
spin_lock_irqsave(&info->port->lock, flags);
uart_circ_clear(&info->xmit);
spin_unlock_irqrestore(&info->port->lock, flags);
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
}
/*
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
static void uart_send_xchar(struct tty_struct *tty, char ch)
{
struct uart_info *info = tty->driver_data;
struct uart_port *port = info->port;
if (port->ops->send_xchar)
port->ops->send_xchar(port, ch);
else {
port->x_char = ch;
if (ch)
port->ops->start_tx(port, 0);
}
}
static void uart_throttle(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
if (I_IXOFF(tty))
uart_send_xchar(tty, STOP_CHAR(tty));
if (tty->termios->c_cflag & CRTSCTS)
uart_clear_mctrl(info->port, TIOCM_RTS);
}
static void uart_unthrottle(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
struct uart_port *port = info->port;
if (I_IXOFF(tty)) {
if (port->x_char)
port->x_char = 0;
else
uart_send_xchar(tty, START_CHAR(tty));
}
if (tty->termios->c_cflag & CRTSCTS)
uart_set_mctrl(port, TIOCM_RTS);
}
static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo)
{
struct uart_state *state = info->state;
struct uart_port *port = info->port;
struct serial_struct tmp;
memset(&tmp, 0, sizeof(tmp));
tmp.type = port->type;
tmp.line = port->line;
tmp.port = port->iobase;
if (HIGH_BITS_OFFSET)
tmp.port_high = port->iobase >> HIGH_BITS_OFFSET;
tmp.irq = port->irq;
tmp.flags = port->flags | info->flags;
tmp.xmit_fifo_size = port->fifosize;
tmp.baud_base = port->uartclk / 16;
tmp.close_delay = state->close_delay;
tmp.closing_wait = state->closing_wait;
tmp.custom_divisor = state->custom_divisor;
tmp.hub6 = port->hub6;
tmp.io_type = port->iotype;
tmp.iomem_reg_shift = port->regshift;
tmp.iomem_base = (void *)port->mapbase;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
static int
uart_set_info(struct uart_info *info, struct serial_struct *newinfo)
{
struct serial_struct new_serial;
struct uart_state *state = info->state;
struct uart_port *port = info->port;
unsigned long new_port;
unsigned int change_irq, change_port, old_flags;
unsigned int old_custom_divisor;
int retval = 0;
if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
return -EFAULT;
new_port = new_serial.port;
if (HIGH_BITS_OFFSET)
new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
new_serial.irq = irq_cannonicalize(new_serial.irq);
/*
* This semaphore protects state->count. It is also
* very useful to prevent opens. Also, take the
* port configuration semaphore to make sure that a
* module insertion/removal doesn't change anything
* under us.
*/
down(&port_sem);
change_irq = new_serial.irq != port->irq;
/*
* Since changing the 'type' of the port changes its resource
* allocations, we should treat type changes the same as
* IO port changes.
*/
change_port = new_port != port->iobase ||
(unsigned long)new_serial.iomem_base != port->mapbase ||
new_serial.hub6 != port->hub6 ||
new_serial.io_type != port->iotype ||
new_serial.iomem_reg_shift != port->regshift ||
new_serial.type != port->type;
old_flags = port->flags;
old_custom_divisor = state->custom_divisor;
if (!capable(CAP_SYS_ADMIN)) {
retval = -EPERM;
if (change_irq || change_port ||
(new_serial.baud_base != port->uartclk / 16) ||
(new_serial.close_delay != state->close_delay) ||
(new_serial.closing_wait != state->closing_wait) ||
(new_serial.xmit_fifo_size != port->fifosize) ||
(((new_serial.flags ^ old_flags) & ~UPF_USR_MASK) != 0))
goto exit;
port->flags = ((port->flags & ~UPF_USR_MASK) |
(new_serial.flags & UPF_USR_MASK));
state->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}
/*
* Ask the low level driver to verify the settings.
*/
if (port->ops->verify_port)
retval = port->ops->verify_port(port, &new_serial);
if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) ||
(new_serial.baud_base < 9600))
retval = -EINVAL;
if (retval)
goto exit;
if (change_port || change_irq) {
retval = -EBUSY;
/*
* Make sure that we are the sole user of this port.
*/
if (state->count > 1 || info->blocked_open != 0)
goto exit;
/*
* We need to shutdown the serial port at the old
* port/type/irq combination.
*/
uart_shutdown(info);
}
if (change_port) {
unsigned long old_iobase, old_mapbase;
unsigned int old_type, old_iotype, old_hub6, old_shift;
old_iobase = port->iobase;
old_mapbase = port->mapbase;
old_type = port->type;
old_hub6 = port->hub6;
old_iotype = port->iotype;
old_shift = port->regshift;
/*
* Free and release old regions
*/
if (old_type != PORT_UNKNOWN)
port->ops->release_port(port);
port->iobase = new_port;
port->type = new_serial.type;
port->hub6 = new_serial.hub6;
port->iotype = new_serial.io_type;
port->regshift = new_serial.iomem_reg_shift;
port->mapbase = (unsigned long)new_serial.iomem_base;
/*
* Claim and map the new regions
*/
if (port->type != PORT_UNKNOWN)
retval = port->ops->request_port(port);
/*
* If we fail to request resources for the
* new port, try to restore the old settings.
*/
if (retval && old_type != PORT_UNKNOWN) {
port->iobase = old_iobase;
port->type = old_type;
port->hub6 = old_hub6;
port->iotype = old_iotype;
port->regshift = old_shift;
port->mapbase = old_mapbase;
retval = port->ops->request_port(port);
/*
* If we failed to restore the old settings,
* we fail like this.
*/
if (retval)
port->type = PORT_UNKNOWN;
/*
* We failed anyway.
*/
retval = -EBUSY;
}
}
port->irq = new_serial.irq;
port->uartclk = new_serial.baud_base * 16;
port->flags = new_serial.flags & UPF_FLAGS;
state->custom_divisor = new_serial.custom_divisor;
state->close_delay = new_serial.close_delay * HZ / 100;
state->closing_wait = new_serial.closing_wait * HZ / 100;
port->fifosize = new_serial.xmit_fifo_size;
info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0;
check_and_exit:
retval = 0;
if (port->type == PORT_UNKNOWN)
goto exit;
if (info->flags & UIF_INITIALIZED) {
if (((old_flags ^ port->flags) & UPF_SPD_MASK) ||
old_custom_divisor != state->custom_divisor) {
uart_update_altspeed(info);
uart_change_speed(info, NULL);
}
} else
retval = uart_startup(info, 1);
exit:
up(&port_sem);
return retval;
}
/*
* uart_get_lsr_info - get line status register info
*/
static int uart_get_lsr_info(struct uart_info *info, unsigned int *value)
{
struct uart_port *port = info->port;
unsigned int result;
result = port->ops->tx_empty(port);
/*
* If we're about to load something into the transmit
* register, we'll pretend the transmitter isn't empty to
* avoid a race condition (depending on when the transmit
* interrupt happens).
*/
if (info->port->x_char ||
((uart_circ_chars_pending(&info->xmit) > 0) &&
!info->tty->stopped && !info->tty->hw_stopped))
result &= ~TIOCSER_TEMT;
return put_user(result, value);
}
static int uart_get_modem_info(struct uart_port *port, unsigned int *value)
{
unsigned int result = port->mctrl;
result |= port->ops->get_mctrl(port);
return put_user(result, value);
}
static int
uart_set_modem_info(struct uart_port *port, unsigned int cmd,
unsigned int *value)
{
unsigned int arg, set, clear;
int ret = 0;
if (get_user(arg, value))
return -EFAULT;
set = clear = 0;
switch (cmd) {
case TIOCMBIS:
set = arg;
break;
case TIOCMBIC:
clear = arg;
break;
case TIOCMSET:
set = arg;
clear = ~arg;
break;
default:
ret = -EINVAL;
break;
}
if (ret == 0)
uart_update_mctrl(port, set, clear);
return ret;
}
static void uart_break_ctl(struct tty_struct *tty, int break_state)
{
struct uart_info *info = tty->driver_data;
struct uart_port *port = info->port;
BUG_ON(!kernel_locked());
if (port->type != PORT_UNKNOWN)
port->ops->break_ctl(port, break_state);
}
static int uart_do_autoconfig(struct uart_info *info)
{
struct uart_port *port = info->port;
int flags, ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/*
* Take the 'count' lock. This prevents count
* from incrementing, and hence any extra opens
* of the port while we're auto-configging.
*/
if (down_interruptible(&port_sem))
return -ERESTARTSYS;
ret = -EBUSY;
if (info->state->count == 1 && info->blocked_open == 0) {
uart_shutdown(info);
/*
* If we already have a port type configured,
* we must release its resources.
*/
if (port->type != PORT_UNKNOWN)
port->ops->release_port(port);
flags = UART_CONFIG_TYPE;
if (port->flags & UPF_AUTO_IRQ)
flags |= UART_CONFIG_IRQ;
/*
* This will claim the ports resources if
* a port is found.
*/
port->ops->config_port(port, flags);
ret = uart_startup(info, 1);
}
up(&port_sem);
return ret;
}
static int
uart_wait_modem_status(struct uart_info *info, unsigned long arg)
{
struct uart_port *port = info->port;
DECLARE_WAITQUEUE(wait, current);
struct uart_icount cprev, cnow;
int ret;
/*
* note the counters on entry
*/
spin_lock_irq(&port->lock);
memcpy(&cprev, &port->icount, sizeof(struct uart_icount));
spin_unlock_irq(&port->lock);
/*
* Force modem status interrupts on
*/
port->ops->enable_ms(port);
add_wait_queue(&info->delta_msr_wait, &wait);
for (;;) {
spin_lock_irq(&port->lock);
memcpy(&cnow, &port->icount, sizeof(struct uart_icount));
spin_unlock_irq(&port->lock);
set_current_state(TASK_INTERRUPTIBLE);
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
ret = 0;
break;
}
schedule();
/* see if a signal did it */
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
cprev = cnow;
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->delta_msr_wait, &wait);
return ret;
}
/*
* Called via sys_ioctl under the BKL. We can use spin_lock_irq() here.
*/
static int
uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct uart_info *info = tty->driver_data;
struct serial_icounter_struct icount;
struct uart_icount cnow;
int ret = -ENOIOCTLCMD;
BUG_ON(!kernel_locked());
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
switch (cmd) {
case TIOCMGET:
ret = uart_get_modem_info(info->port,
(unsigned int *)arg);
break;
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
ret = uart_set_modem_info(info->port, cmd,
(unsigned int *)arg);
break;
case TIOCGSERIAL:
ret = uart_get_info(info, (struct serial_struct *)arg);
break;
case TIOCSSERIAL:
ret = uart_set_info(info, (struct serial_struct *)arg);
break;
case TIOCSERCONFIG:
ret = uart_do_autoconfig(info);
break;
case TIOCSERGETLSR: /* Get line status register */
ret = uart_get_lsr_info(info, (unsigned int *)arg);
break;
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
case TIOCMIWAIT:
ret = uart_wait_modem_status(info, arg);
break;
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
* NB: both 1->0 and 0->1 transitions are counted except for
* RI where only 0->1 is counted.
*/
case TIOCGICOUNT:
spin_lock_irq(&info->port->lock);
memcpy(&cnow, &info->port->icount,
sizeof(struct uart_icount));
spin_unlock_irq(&info->port->lock);
icount.cts = cnow.cts;
icount.dsr = cnow.dsr;
icount.rng = cnow.rng;
icount.dcd = cnow.dcd;
icount.rx = cnow.rx;
icount.tx = cnow.tx;
icount.frame = cnow.frame;
icount.overrun = cnow.overrun;
icount.parity = cnow.parity;
icount.brk = cnow.brk;
icount.buf_overrun = cnow.buf_overrun;
ret = copy_to_user((void *)arg, &icount, sizeof(icount))
? -EFAULT : 0;
break;
case TIOCSERGWILD: /* obsolete */
case TIOCSERSWILD: /* obsolete */
ret = 0;
break;
default: {
struct uart_port *port = info->port;
if (port->ops->ioctl)
ret = port->ops->ioctl(port, cmd, arg);
break;
}
}
return ret;
}
static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
struct uart_info *info = tty->driver_data;
unsigned long flags;
unsigned int cflag = tty->termios->c_cflag;
BUG_ON(!kernel_locked());
/*
* These are the bits that are used to setup various
* flags in the low level driver.
*/
#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
if ((cflag ^ old_termios->c_cflag) == 0 &&
RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
return;
uart_change_speed(info, old_termios);
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
uart_clear_mctrl(info->port, TIOCM_RTS | TIOCM_DTR);
/* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
unsigned int mask = TIOCM_DTR;
if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags))
mask |= TIOCM_RTS;
uart_set_mctrl(info->port, mask);
}
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
spin_lock_irqsave(&info->port->lock, flags);
tty->hw_stopped = 0;
__uart_start(tty);
spin_unlock_irqrestore(&info->port->lock, flags);
}
#if 0
/*
* No need to wake up processes in open wait, since they
* sample the CLOCAL flag once, and don't recheck it.
* XXX It's not clear whether the current behavior is correct
* or not. Hence, this may change.....
*/
if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL))
wake_up_interruptible(&info->open_wait);
#endif
}
/*
* In 2.4.5, calls to this will be serialized via the BKL in
* linux/drivers/char/tty_io.c:tty_release()
* linux/drivers/char/tty_io.c:do_tty_handup()
*/
static void uart_close(struct tty_struct *tty, struct file *filp)
{
struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state;
struct uart_info *info = tty->driver_data;
struct uart_port *port = info->port;
struct uart_state *state;
unsigned long flags;
BUG_ON(!kernel_locked());
if (!info)
return;
state = info->state;
DPRINTK("uart_close() called\n");
/*
* This is safe, as long as the BKL exists in
* do_tty_hangup(), and we're protected by the BKL.
*/
if (tty_hung_up_p(filp))
goto done;
spin_lock_irqsave(&info->port->lock, flags);
if ((tty->count == 1) && (state->count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. state->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk("uart_close: bad serial port count; tty->count is 1, "
"state->count is %d\n", state->count);
state->count = 1;
}
if (--state->count < 0) {
printk("rs_close: bad serial port count for %s%d: %d\n",
tty->driver.name, info->port->line, state->count);
state->count = 0;
}
if (state->count) {
spin_unlock_irqrestore(&info->port->lock, flags);
goto done;
}
/*
* The UIF_CLOSING flag protects us against further opens
* of this port.
*/
info->flags |= UIF_CLOSING;
spin_unlock_irqrestore(&info->port->lock, flags);
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
if (info->state->closing_wait != USF_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, info->state->closing_wait);
/*
* At this point, we stop accepting input. To do this, we
* disable the receive line status interrupts.
*/
if (info->flags & UIF_INITIALIZED) {
port->ops->stop_rx(port);
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
uart_wait_until_sent(tty, port->timeout);
}
down(&port_sem);
uart_shutdown(info);
up(&port_sem);
uart_flush_buffer(tty);
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
info->event = 0;
info->tty = NULL;
if (info->blocked_open) {
if (info->state->close_delay) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(info->state->close_delay);
set_current_state(TASK_RUNNING);
}
} else {
#ifdef CONFIG_PM
/*
* Put device into D3 state.
*/
pm_send(info->state->pm, PM_SUSPEND, (void *)3);
#else
if (port->ops->pm)
port->ops->pm(port, 3, 0);
#endif
}
/*
* Wake up anyone trying to open this port.
*/
info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING);
wake_up_interruptible(&info->open_wait);
done:
if (drv->owner)
__MOD_DEC_USE_COUNT(drv->owner);
}
static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct uart_info *info = tty->driver_data;
struct uart_port *port = info->port;
unsigned long char_time, expire;
BUG_ON(!kernel_locked());
if (port->type == PORT_UNKNOWN || port->fifosize == 0)
return;
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
* interval should also be less than the timeout.
*
* Note: we have to use pretty tight timings here to satisfy
* the NIST-PCTS.
*/
char_time = (port->timeout - HZ/50) / port->fifosize;
char_time = char_time / 5;
if (char_time == 0)
char_time = 1;
if (timeout && timeout < char_time)
char_time = timeout;
/*
* If the transmitter hasn't cleared in twice the approximate
* amount of time to send the entire FIFO, it probably won't
* ever clear. This assumes the UART isn't doing flow
* control, which is currently the case. Hence, if it ever
* takes longer than port->timeout, this is probably due to a
* UART bug of some kind. So, we clamp the timeout parameter at
* 2*port->timeout.
*/
if (timeout == 0 || timeout > 2 * port->timeout)
timeout = 2 * port->timeout;
expire = jiffies + timeout;
DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n",
port->line, jiffies, expire);
/*
* Check whether the transmitter is empty every 'char_time'.
* 'timeout' / 'expire' give us the maximum amount of time
* we wait.
*/
while (!port->ops->tx_empty(port)) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(char_time);
if (signal_pending(current))
break;
if (time_after(jiffies, expire))
break;
}
set_current_state(TASK_RUNNING); /* might not be needed */
}
/*
* This is called with the BKL held in
* linux/drivers/char/tty_io.c:do_tty_hangup()
* We're called from the eventd thread, so we can sleep for
* a _short_ time only.
*/
static void uart_hangup(struct tty_struct *tty)
{
struct uart_info *info = tty->driver_data;
struct uart_state *state = info->state;
BUG_ON(!kernel_locked());
uart_flush_buffer(tty);
down(&port_sem);
if (info->flags & UIF_CLOSING) {
up(&port_sem);
return;
}
uart_shutdown(info);
info->event = 0;
state->count = 0;
info->flags &= ~UIF_NORMAL_ACTIVE;
info->tty = NULL;
up(&port_sem);
wake_up_interruptible(&info->open_wait);
}
/*
* Copy across the serial console cflag setting into the termios settings
* for the initial open of the port. This allows continuity between the
* kernel settings, and the settings init adopts when it opens the port
* for the first time.
*/
static void uart_update_termios(struct uart_info *info)
{
struct tty_struct *tty = info->tty;
#ifdef CONFIG_SERIAL_CORE_CONSOLE
struct console *c = info->port->cons;
if (c && c->cflag && c->index == info->port->line) {
tty->termios->c_cflag = c->cflag;
c->cflag = 0;
}
#endif
/*
* If the device failed to grab its irq resources,
* or some other error occurred, don't try to talk
* to the port hardware.
*/
if (!(tty->flags & (1 << TTY_IO_ERROR))) {
/*
* Make termios settings take effect.
*/
uart_change_speed(info, NULL);
/*
* And finally enable the RTS and DTR signals.
*/
if (tty->termios->c_cflag & CBAUD)
uart_set_mctrl(info->port, TIOCM_DTR | TIOCM_RTS);
}
}
static int
uart_block_til_ready(struct file *filp, struct uart_info *info)
{
DECLARE_WAITQUEUE(wait, current);
struct uart_state *state = info->state;
struct uart_port *port = info->port;
info->blocked_open++;
state->count--;
add_wait_queue(&info->open_wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
/*
* If we have been hung up, tell userspace/restart open.
*/
if (tty_hung_up_p(filp))
break;
/*
* If the device is in the middle of being closed, block
* until it's done. We will need to re-initialise the
* port. Hmm, is it legal to block a non-blocking open?
*/
if (info->flags & UIF_CLOSING)
goto wait;
/*
* If the port has been closed, tell userspace/restart open.
*/
if (!(info->flags & UIF_INITIALIZED))
break;
/*
* If non-blocking mode is set, or CLOCAL mode is set,
* we don't want to wait for the modem status lines to
* indicate that the port is ready.
*
* Also, if the port is not enabled/configured, we want
* to allow the open to succeed here. Note that we will
* have set TTY_IO_ERROR for a non-existant port.
*/
if ((filp->f_flags & O_NONBLOCK) ||
(info->tty->termios->c_cflag & CLOCAL) ||
(info->tty->flags & (1 << TTY_IO_ERROR))) {
break;
}
/*
* Set DTR to allow modem to know we're waiting. Do
* not set RTS here - we want to make sure we catch
* the data from the modem.
*/
if (info->tty->termios->c_cflag & CBAUD)
uart_set_mctrl(info->port, TIOCM_DTR);
/*
* and wait for the carrier to indicate that the
* modem is ready for us.
*/
if (port->ops->get_mctrl(port) & TIOCM_CAR)
break;
wait:
schedule();
if (signal_pending(current))
break;
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
state->count++;
info->blocked_open--;
if (signal_pending(current))
return -ERESTARTSYS;
if (tty_hung_up_p(filp) || !(info->flags & UIF_INITIALIZED))
return (port->flags & UPF_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS;
return 0;
}
static struct uart_info *uart_get(struct uart_driver *drv, int line)
{
struct uart_state *state = drv->state + line;
struct uart_info *info = NULL;
down(&port_sem);
if (!state->port)
goto out;
state->count++;
info = state->info;
if (!info) {
info = kmalloc(sizeof(struct uart_info), GFP_KERNEL);
if (info) {
memset(info, 0, sizeof(struct uart_info));
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->delta_msr_wait);
/*
* Link the info into the other structures.
*/
info->port = state->port;
info->state = state;
state->port->info = info;
tasklet_init(&info->tlet, uart_tasklet_action,
(unsigned long)info);
state->info = info;
} else
state->count--;
}
out:
up(&port_sem);
return info;
}
/*
* In 2.4.5, calls to uart_open are serialised by the BKL in
* linux/fs/devices.c:chrdev_open()
* Note that if this fails, then uart_close() _will_ be called.
*
* In time, we want to scrap the "opening nonpresent ports"
* behaviour and implement an alternative way for setserial
* to set base addresses/ports/types. This will allow us to
* get rid of a certain amount of extra tests.
*/
static int uart_open(struct tty_struct *tty, struct file *filp)
{
struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state;
struct uart_info *info;
int retval, line = minor(tty->device) - tty->driver.minor_start;
BUG_ON(!kernel_locked());
DPRINTK("uart_open(%d) called\n", line);
/*
* tty->driver.num won't change, so we won't fail here with
* tty->driver_data set to something non-NULL (and therefore
* we won't get caught by uart_close()).
*/
retval = -ENODEV;
if (line >= tty->driver.num)
goto fail;
/*
* If we fail to increment the module use count, we can't have
* any other users of this tty (since this implies that the module
* is about to be unloaded). Therefore, it is safe to set
* tty->driver_data to be NULL, so uart_close() doesn't bite us.
*/
if (!try_inc_mod_count(drv->owner)) {
tty->driver_data = NULL;
goto fail;
}
/*
* FIXME: This one isn't fun. We can't guarantee that the tty isn't
* already in open, nor can we guarantee the state of tty->driver_data
*/
info = uart_get(drv, line);
retval = -ENOMEM;
if (!info) {
if (tty->driver_data)
goto fail;
else
goto out;
}
/*
* Once we set tty->driver_data here, we are guaranteed that
* uart_close() will decrement the driver module use count.
* Any failures from here onwards should not touch the count.
*/
tty->driver_data = info;
info->tty = tty;
info->tty->low_latency = (info->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
/*
* If the port is in the middle of closing, bail out now.
*/
if (tty_hung_up_p(filp) || (info->flags & UIF_CLOSING)) {
wait_event_interruptible(info->open_wait,
!(info->flags & UIF_CLOSING));
retval = (info->port->flags & UPF_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS;
goto fail;
}
/*
* Make sure the device is in D0 state.
*/
if (info->state->count == 1) {
#ifdef CONFIG_PM
pm_send(info->state->pm, PM_RESUME, (void *)0);
#else
struct uart_port *port = info->port;
if (port->ops->pm)
port->ops->pm(port, 0, 3);
#endif
}
/*
* Start up the serial port. We have this semaphore here to
* prevent uart_startup or uart_shutdown being re-entered if
* we sleep while requesting an IRQ.
*/
down(&port_sem);
retval = uart_startup(info, 0);
up(&port_sem);
if (retval)
goto fail;
/*
* Wait until the port is ready.
*/
retval = uart_block_til_ready(filp, info);
/*
* If this is the first open to succeed, adjust things to suit.
*/
if (retval == 0 && !(info->flags & UIF_NORMAL_ACTIVE)) {
info->flags |= UIF_NORMAL_ACTIVE;
uart_update_termios(info);
}
return retval;
out:
if (drv->owner)
__MOD_DEC_USE_COUNT(drv->owner);
fail:
return retval;
}
#ifdef CONFIG_PROC_FS
static const char *uart_type(struct uart_port *port)
{
const char *str = NULL;
if (port->ops->type)
str = port->ops->type(port);
if (!str)
str = "unknown";
return str;
}
static int uart_line_info(char *buf, struct uart_driver *drv, int i)
{
struct uart_state *state = drv->state + i;
struct uart_port *port = state->port;
char stat_buf[32];
unsigned int status;
int ret;
if (!port)
return 0;
ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d",
port->line, uart_type(port),
port->iobase, port->irq);
if (port->type == PORT_UNKNOWN) {
strcat(buf, "\n");
return ret + 1;
}
status = port->ops->get_mctrl(port);
ret += sprintf(buf + ret, " tx:%d rx:%d",
port->icount.tx, port->icount.rx);
if (port->icount.frame)
ret += sprintf(buf + ret, " fe:%d",
port->icount.frame);
if (port->icount.parity)
ret += sprintf(buf + ret, " pe:%d",
port->icount.parity);
if (port->icount.brk)
ret += sprintf(buf + ret, " brk:%d",
port->icount.brk);
if (port->icount.overrun)
ret += sprintf(buf + ret, " oe:%d",
port->icount.overrun);
#define INFOBIT(bit,str) \
if (port->mctrl & (bit)) \
strncat(stat_buf, (str), sizeof(stat_buf) - \
strlen(stat_buf) - 2)
#define STATBIT(bit,str) \
if (status & (bit)) \
strncat(stat_buf, (str), sizeof(stat_buf) - \
strlen(stat_buf) - 2)
stat_buf[0] = '\0';
stat_buf[1] = '\0';
INFOBIT(TIOCM_RTS, "|RTS");
STATBIT(TIOCM_CTS, "|CTS");
INFOBIT(TIOCM_DTR, "|DTR");
STATBIT(TIOCM_DSR, "|DSR");
STATBIT(TIOCM_CAR, "|CD");
STATBIT(TIOCM_RNG, "|RI");
if (stat_buf[0])
stat_buf[0] = ' ';
strcat(stat_buf, "\n");
ret += sprintf(buf + ret, stat_buf);
return ret;
}
static int uart_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
struct tty_driver *ttydrv = data;
struct uart_driver *drv = ttydrv->driver_state;
int i, len = 0, l;
off_t begin = 0;
len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",
"", "", "");
for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) {
l = uart_line_info(page + len, drv, i);
len += l;
if (len + begin > off + count)
goto done;
if (len + begin < off) {
begin += len;
len = 0;
}
}
*eof = 1;
done:
if (off >= len + begin)
return 0;
*start = page + (off - begin);
return (count < begin + len - off) ? count : (begin + len - off);
}
#endif
#ifdef CONFIG_SERIAL_CORE_CONSOLE
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
struct uart_port * __init
uart_get_console(struct uart_port *ports, int nr, struct console *co)
{
int idx = co->index;
if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 &&
ports[idx].membase == NULL))
for (idx = 0; idx < nr; idx++)
if (ports[idx].iobase != 0 ||
ports[idx].membase != NULL)
break;
co->index = idx;
return ports + idx;
}
/**
* uart_parse_options - Parse serial port baud/parity/bits/flow contro.
* @options: pointer to option string
* @baud: pointer to an 'int' variable for the baud rate.
* @parity: pointer to an 'int' variable for the parity.
* @bits: pointer to an 'int' variable for the number of data bits.
* @flow: pointer to an 'int' variable for the flow control character.
*
* uart_parse_options decodes a string containing the serial console
* options. The format of the string is <baud><parity><bits><flow>,
* eg: 115200n8r
*/
void __init
uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
{
char *s = options;
*baud = simple_strtoul(s, NULL, 10);
while (*s >= '0' && *s <= '9')
s++;
if (*s)
*parity = *s++;
if (*s)
*bits = *s++ - '0';
if (*s)
*flow = *s;
}
struct baud_rates {
unsigned int rate;
unsigned int cflag;
};
static struct baud_rates baud_rates[] = {
{ 921600, B921600 },
{ 460800, B460800 },
{ 230400, B230400 },
{ 115200, B115200 },
{ 57600, B57600 },
{ 38400, B38400 },
{ 19200, B19200 },
{ 9600, B9600 },
{ 4800, B4800 },
{ 2400, B2400 },
{ 1200, B1200 },
{ 0, B38400 }
};
/**
* uart_set_options - setup the serial console parameters
* @port: pointer to the serial ports uart_port structure
* @co: console pointer
* @baud: baud rate
* @parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
* @bits: number of data bits
* @flow: flow control character - 'r' (rts)
*/
int __init
uart_set_options(struct uart_port *port, struct console *co,
int baud, int parity, int bits, int flow)
{
unsigned int cflag = CREAD | HUPCL | CLOCAL;
unsigned int quot;
int i;
/*
* Construct a cflag setting.
*/
for (i = 0; baud_rates[i].rate; i++)
if (baud_rates[i].rate <= baud)
break;
cflag |= baud_rates[i].cflag;
if (bits == 7)
cflag |= CS7;
else
cflag |= CS8;
switch (parity) {
case 'o': case 'O':
cflag |= PARODD;
/*fall through*/
case 'e': case 'E':
cflag |= PARENB;
break;
}
if (flow == 'r')
cflag |= CRTSCTS;
co->cflag = cflag;
quot = (port->uartclk / (16 * baud));
port->ops->change_speed(port, cflag, 0, quot);
return 0;
}
extern void ambauart_console_init(void);
extern void anakin_console_init(void);
extern void clps711xuart_console_init(void);
extern void rs285_console_init(void);
extern void sa1100_rs_console_init(void);
extern void serial8250_console_init(void);
extern void uart00_console_init(void);
/*
* Central "initialise all serial consoles" container. Needs to be killed.
*/
void __init uart_console_init(void)
{
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
ambauart_console_init();
#endif
#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE
anakin_console_init();
#endif
#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
clps711xuart_console_init();
#endif
#ifdef CONFIG_SERIAL_21285_CONSOLE
rs285_console_init();
#endif
#ifdef CONFIG_SERIAL_SA1100_CONSOLE
sa1100_rs_console_init();
#endif
#ifdef CONFIG_SERIAL_8250_CONSOLE
serial8250_console_init();
#endif
#ifdef CONFIG_SERIAL_UART00_CONSOLE
uart00_console_init();
#endif
}
#endif /* CONFIG_SERIAL_CORE_CONSOLE */
#ifdef CONFIG_PM
/*
* Serial port power management.
*
* This is pretty coarse at the moment - either all on or all off. We
* should probably some day do finer power management here some day.
*
* We don't actually save any state; the serial driver already has the
* state held internally to re-setup the port when we come out of D3.
*/
static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstate)
{
struct uart_port *port;
struct uart_ops *ops;
int running = state->info &&
state->info->flags & UIF_INITIALIZED;
down(&port_sem);
if (!state->port || state->port->type == PORT_UNKNOWN) {
up(&port_sem);
return 0;
}
port = state->port;
ops = port->ops;
DPRINTK("pm: %08x: %d -> %d, %srunning\n",
port->iobase, dev->state, pm_state, running ? "" : "not ");
if (pm_state == 0) {
if (ops->pm)
ops->pm(port, pm_state, oldstate);
if (running) {
/*
* The port lock isn't taken here -
* the port isn't initialised.
*/
ops->set_mctrl(port, 0);
ops->startup(port);
uart_change_speed(state->info, NULL);
spin_lock_irq(&port->lock);
ops->set_mctrl(port, port->mctrl);
ops->start_tx(port, 0);
spin_unlock_irq(&port->lock);
}
/*
* Re-enable the console device after suspending.
*/
if (port->cons && port->cons->index == port->line)
port->cons->flags |= CON_ENABLED;
} else if (pm_state == 1) {
if (ops->pm)
ops->pm(port, pm_state, oldstate);
} else {
/*
* Disable the console device before suspending.
*/
if (port->cons && port->cons->index == port->line)
port->cons->flags &= ~CON_ENABLED;
if (running) {
ops->stop_tx(port, 0);
spin_lock_irq(&port->lock);
ops->set_mctrl(port, 0);
spin_unlock_irq(&port->lock);
ops->stop_rx(port);
ops->shutdown(port);
}
if (ops->pm)
ops->pm(port, pm_state, oldstate);
}
up(&port_sem);
return 0;
}
/*
* Wakeup support.
*/
static int uart_pm_set_wakeup(struct uart_state *state, int data)
{
int err = 0;
if (state->port->ops->set_wake)
err = state->port->ops->set_wake(state->port, data);
return err;
}
static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data)
{
struct uart_state *state = dev->data;
int err = 0;
switch (rqst) {
case PM_SUSPEND:
case PM_RESUME:
err = uart_pm_set_state(state, (int)data, dev->state);
break;
case PM_SET_WAKEUP:
err = uart_pm_set_wakeup(state, (int)data);
break;
}
return err;
}
#endif
static inline void
uart_report_port(struct uart_driver *drv, struct uart_port *port)
{
printk("%s%d at ", drv->dev_name, port->line);
switch (port->iotype) {
case UPIO_PORT:
printk("I/O 0x%x", port->iobase);
break;
case UPIO_HUB6:
printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6);
break;
case UPIO_MEM:
printk("MMIO 0x%lx", port->mapbase);
break;
}
printk(" (irq = %d) is a %s\n", port->irq, uart_type(port));
}
static void
__uart_register_port(struct uart_driver *drv, struct uart_state *state,
struct uart_port *port)
{
unsigned int flags;
state->port = port;
spin_lock_init(&port->lock);
port->type = PORT_UNKNOWN;
port->cons = drv->cons;
port->info = state->info;
/*
* If there isn't a port here, don't do anything further.
*/
if (!port->iobase && !port->mapbase)
return;
/*
* Now do the auto configuration stuff. Note that config_port
* is expected to claim the resources and map the port for us.
*/
flags = UART_CONFIG_TYPE;
if (port->flags & UPF_AUTO_IRQ)
flags |= UART_CONFIG_IRQ;
if (port->flags & UPF_BOOT_AUTOCONF)
port->ops->config_port(port, flags);
/*
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this ports parameters.
*/
tty_register_devfs(drv->tty_driver, 0, drv->minor + port->line);
if (port->type != PORT_UNKNOWN) {
unsigned long flags;
uart_report_port(drv, port);
/*
* Ensure that the modem control lines are de-activated.
* We probably don't need a spinlock around this, but
*/
spin_lock_irqsave(&port->lock, flags);
port->ops->set_mctrl(port, 0);
spin_unlock_irqrestore(&port->lock, flags);
#ifdef CONFIG_PM
/*
* Power down all ports by default, except the
* console if we have one. We need to drop the
* port semaphore here.
*/
if (state->pm && (!drv->cons || port->line != drv->cons->index)) {
up(&port_sem);
pm_send(state->pm, PM_SUSPEND, (void *)3);
down(&port_sem);
}
#endif
}
}
/*
* Hangup the port. This must be done outside the port_sem
* since uart_hangup() grabs this same semaphore. Grr.
*/
static void
__uart_hangup_port(struct uart_driver *drv, struct uart_state *state)
{
struct uart_info *info = state->info;
if (info && info->tty)
tty_vhangup(info->tty);
}
/*
* This reverses the affects of __uart_register_port.
*/
static void
__uart_unregister_port(struct uart_driver *drv, struct uart_state *state)
{
struct uart_port *port = state->port;
struct uart_info *info = state->info;
state->info = NULL;
/*
* Remove the devices from devfs
*/
tty_unregister_devfs(drv->tty_driver, drv->minor + port->line);
/*
* Free the port IO and memory resources, if any.
*/
if (port->type != PORT_UNKNOWN)
port->ops->release_port(port);
/*
* Indicate that there isn't a port here anymore.
*/
port->type = PORT_UNKNOWN;
/*
* Kill the tasklet, and free resources.
*/
if (info) {
tasklet_kill(&info->tlet);
kfree(info);
}
}
/**
* uart_register_driver - register a driver with the uart core layer
* @drv: low level driver structure
*
* Register a uart driver with the core driver. We in turn register
* with the tty layer, and initialise the core driver per-port state.
*
* We have a proc file in /proc/tty/driver which is named after the
* normal driver.
*
* drv->port should be NULL, and the per-port structures should be
* registered using uart_add_one_port after this call has succeeded.
*/
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal = NULL;
struct termios **termios = NULL;
int i, retval;
BUG_ON(drv->state);
/*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle. Note that we also
* allocate space for an integer for reference counting.
*/
drv->state = kmalloc(sizeof(struct uart_state) * drv->nr +
sizeof(int), GFP_KERNEL);
retval = -ENOMEM;
if (!drv->state)
goto out;
memset(drv->state, 0, sizeof(struct uart_state) * drv->nr +
sizeof(int));
termios = kmalloc(sizeof(struct termios *) * drv->nr * 2 +
sizeof(struct tty_struct *) * drv->nr, GFP_KERNEL);
if (!termios)
goto out;
memset(termios, 0, sizeof(struct termios *) * drv->nr * 2 +
sizeof(struct tty_struct *) * drv->nr);
normal = kmalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (!normal)
goto out;
memset(normal, 0, sizeof(struct tty_driver));
drv->tty_driver = normal;
normal->magic = TTY_DRIVER_MAGIC;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->num = drv->nr;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
normal->refcount = (int *)(drv->state + drv->nr);
normal->termios = termios;
normal->termios_locked = termios + drv->nr;
normal->table = (struct tty_struct **)(termios + drv->nr * 2);
normal->driver_state = drv;
normal->open = uart_open;
normal->close = uart_close;
normal->write = uart_write;
normal->put_char = uart_put_char;
normal->flush_chars = uart_flush_chars;
normal->write_room = uart_write_room;
normal->chars_in_buffer = uart_chars_in_buffer;
normal->flush_buffer = uart_flush_buffer;
normal->ioctl = uart_ioctl;
normal->throttle = uart_throttle;
normal->unthrottle = uart_unthrottle;
normal->send_xchar = uart_send_xchar;
normal->set_termios = uart_set_termios;
normal->stop = uart_stop;
normal->start = uart_start;
normal->hangup = uart_hangup;
normal->break_ctl = uart_break_ctl;
normal->wait_until_sent = uart_wait_until_sent;
#ifdef CONFIG_PROC_FS
normal->read_proc = uart_read_proc;
#endif
/*
* Initialise the UART state(s).
*/
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
state->close_delay = 5 * HZ / 10;
state->closing_wait = 30 * HZ;
#ifdef CONFIG_PM
state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm);
if (state->pm)
state->pm->data = state;
#endif
}
retval = tty_register_driver(normal);
out:
if (retval < 0) {
#ifdef CONFIG_PM
for (i = 0; i < drv->nr; i++)
pm_unregister(drv->state[i].pm);
#endif
kfree(normal);
kfree(drv->state);
kfree(termios);
}
return retval;
}
/**
* uart_unregister_driver - remove a driver from the uart core layer
* @drv: low level driver structure
*
* Remove all references to a driver from the core driver. The low
* level driver must have removed all its ports via the
* uart_remove_one_port() if it registered them with uart_add_one_port().
* (ie, drv->port == NULL)
*/
void uart_unregister_driver(struct uart_driver *drv)
{
int i;
for (i = 0; i < drv->nr; i++)
pm_unregister(drv->state[i].pm);
tty_unregister_driver(drv->tty_driver);
kfree(drv->state);
kfree(drv->tty_driver->termios);
kfree(drv->tty_driver);
}
/**
* uart_add_one_port - attach a driver-defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @port: uart port structure to use for this port.
*
* This allows the driver to register its own uart_port structure
* with the core driver. The main purpose is to allow the low
* level uart drivers to expand uart_port, rather than having yet
* more levels of structures.
*/
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{
struct uart_state *state;
BUG_ON(in_interrupt());
if (port->line >= drv->nr)
return -EINVAL;
state = drv->state + port->line;
down(&port_sem);
__uart_register_port(drv, state, port);
up(&port_sem);
return 0;
}
/**
* uart_remove_one_port - detach a driver defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @port: uart port structure for this port
*
* This unhooks (and hangs up) the specified port structure from the
* core driver. No further calls will be made to the low-level code
* for this port.
*/
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
{
struct uart_state *state = drv->state + port->line;
BUG_ON(in_interrupt());
if (state->port != port)
printk(KERN_ALERT "Removing wrong port: %p != %p\n",
state->port, port);
__uart_hangup_port(drv, state);
down(&port_sem);
__uart_unregister_port(drv, state);
state->port = NULL;
up(&port_sem);
return 0;
}
/*
* Are the two ports equivalent?
*/
static int uart_match_port(struct uart_port *port1, struct uart_port *port2)
{
if (port1->iotype != port2->iotype)
return 0;
switch (port1->iotype) {
case UPIO_PORT:
return (port1->iobase == port2->iobase);
case UPIO_HUB6:
return (port1->iobase == port2->iobase) &&
(port1->hub6 == port2->hub6);
case UPIO_MEM:
return (port1->membase == port2->membase);
}
return 0;
}
/*
* Try to find an unused uart_state slot for a port.
*/
static struct uart_state *
uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port)
{
int i;
/*
* First, find a port entry which matches. Note: if we do
* find a matching entry, and it has a non-zero use count,
* then we can't register the port.
*/
for (i = 0; i < drv->nr; i++)
if (uart_match_port(drv->state[i].port, port))
return &drv->state[i];
/*
* We didn't find a matching entry, so look for the first
* free entry. We look for one which hasn't been previously
* used (indicated by zero iobase).
*/
for (i = 0; i < drv->nr; i++)
if (drv->state[i].port->type == PORT_UNKNOWN &&
drv->state[i].port->iobase == 0 &&
drv->state[i].count == 0)
return &drv->state[i];
/*
* That also failed. Last resort is to find any currently
* entry which doesn't have a real port associated with it.
*/
for (i = 0; i < drv->nr; i++)
if (drv->state[i].port->type == PORT_UNKNOWN &&
drv->state[i].count == 0)
return &drv->state[i];
return NULL;
}
/**
* uart_register_port: register uart settings with a port
* @drv: pointer to the uart low level driver structure for this port
* @port: uart port structure describing the port
*
* Register UART settings with the specified low level driver. Detect
* the type of the port if UPF_BOOT_AUTOCONF is set, and detect the
* IRQ if UPF_AUTO_IRQ is set.
*
* We try to pick the same port for the same IO base address, so that
* when a modem is plugged in, unplugged and plugged back in, it gets
* allocated the same port.
*
* Returns negative error, or positive line number.
*/
int uart_register_port(struct uart_driver *drv, struct uart_port *port)
{
struct uart_state *state;
int ret;
down(&port_sem);
state = uart_find_match_or_unused(drv, port);
if (state) {
/*
* Ok, we've found a line that we can use.
*
* If we find a port that matches this one, and it appears
* to be in-use (even if it doesn't have a type) we shouldn't
* alter it underneath itself - the port may be open and
* trying to do useful work.
*/
if (state->count != 0 ||
(state->info && state->info->blocked_open != 0)) {
ret = -EBUSY;
goto out;
}
state->port->iobase = port->iobase;
state->port->membase = port->membase;
state->port->irq = port->irq;
state->port->uartclk = port->uartclk;
state->port->fifosize = port->fifosize;
state->port->regshift = port->regshift;
state->port->iotype = port->iotype;
state->port->flags = port->flags;
state->port->line = drv->state - state;
__uart_register_port(drv, state, state->port);
ret = state->port->line;
} else
ret = -ENOSPC;
out:
up(&port_sem);
return ret;
}
/**
* uart_unregister_port - de-allocate a port
* @drv: pointer to the uart low level driver structure for this port
* @line: line index previously returned from uart_register_port()
*
* Hang up the specified line associated with the low level driver,
* and mark the port as unused.
*/
void uart_unregister_port(struct uart_driver *drv, int line)
{
struct uart_state *state;
if (line < 0 || line >= drv->nr) {
printk(KERN_ERR "Attempt to unregister %s%d\n",
drv->dev_name, line);
return;
}
state = drv->state + line;
__uart_hangup_port(drv, state);
down(&port_sem);
__uart_unregister_port(drv, state);
up(&port_sem);
}
EXPORT_SYMBOL(uart_event);
EXPORT_SYMBOL(uart_register_driver);
EXPORT_SYMBOL(uart_unregister_driver);
EXPORT_SYMBOL(uart_register_port);
EXPORT_SYMBOL(uart_unregister_port);
MODULE_DESCRIPTION("Serial driver core");
MODULE_LICENSE("GPL");
/*
* linux/drivers/char/serial_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: serial_sa1100.c,v 1.41 2002/07/21 08:57:55 rmk Exp $
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/hardware.h>
#include <asm/mach/serial_sa1100.h>
#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.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);
}
}
static void __sa1100_stop_tx(struct sa1100_port *sport)
{
u32 utcr3;
utcr3 = UART_GET_UTCR3(sport);
UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE);
sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS);
}
/*
* interrupts disabled on entry
*/
static void sa1100_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
struct sa1100_port *sport = (struct sa1100_port *)port;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
__sa1100_stop_tx(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
/*
* interrupts may not be disabled on entry
*/
static void sa1100_start_tx(struct uart_port *port, unsigned int tty_start)
{
struct sa1100_port *sport = (struct sa1100_port *)port;
unsigned long flags;
u32 utcr3;
spin_lock_irqsave(&sport->port.lock, flags);
utcr3 = UART_GET_UTCR3(sport);
sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS);
UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
/*
* Interrupts enabled
*/
static void sa1100_stop_rx(struct uart_port *port)
{
struct sa1100_port *sport = (struct sa1100_port *)port;
unsigned long flags;
u32 utcr3;
spin_lock_irqsave(&sport->port.lock, flags);
utcr3 = UART_GET_UTCR3(sport);
UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
/*
* 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 pt_regs *regs)
{
struct tty_struct *tty = sport->port.info->tty;
unsigned int status, ch, flg, ignored = 0;
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);
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;
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))
goto handle_error;
if (uart_handle_sysrq_char(&sport->port, ch, regs))
goto ignore_char;
error_return:
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
ignore_char:
status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
UTSR0_TO_SM(UART_GET_UTSR0(sport));
}
out:
tty_flip_buffer_push(tty);
return;
handle_error:
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++;
if (status & sport->port.ignore_status_mask) {
if (++ignored > 100)
goto out;
goto ignore_char;
}
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;
if (status & UTSR1_TO_SM(UTSR1_ROR)) {
/*
* overrun does *not* affect the character
* we read from the FIFO
*/
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;
ch = 0;
flg = TTY_OVERRUN;
}
#ifdef SUPPORT_SYSRQ
sport->port.sysrq = 0;
#endif
goto error_return;
}
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);
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_event(&sport->port, EVT_WRITE_WAKEUP);
if (uart_circ_empty(xmit))
__sa1100_stop_tx(sport);
}
static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs)
{
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, regs);
}
/* Clear the relevent 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 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,
"serial_sa1100", 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
*/
sa1100_enable_ms(&sport->port);
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_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
struct sa1100_port *sport = (struct sa1100_port *)port;
unsigned long flags;
unsigned int utcr0, old_utcr3;
/* byte size and parity */
switch (cflag & CSIZE) {
case CS7:
utcr0 = 0;
break;
default:
utcr0 = UTCR0_DSS;
break;
}
if (cflag & CSTOPB)
utcr0 |= UTCR0_SBS;
if (cflag & PARENB) {
utcr0 |= UTCR0_PE;
if (!(cflag & PARODD))
utcr0 |= UTCR0_OES;
}
sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS);
sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR);
if (iflag & INPCK)
sport->port.read_status_mask |=
UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
if (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 (iflag & IGNPAR)
sport->port.ignore_status_mask |=
UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
if (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 (iflag & IGNPAR)
sport->port.ignore_status_mask |=
UTSR1_TO_SM(UTSR1_ROR);
}
del_timer_sync(&sport->timer);
/* first, disable interrupts and drain transmitter */
spin_lock_irqsave(&sport->port.lock, flags);
old_utcr3 = UART_GET_UTCR3(sport);
UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE));
while (UART_GET_UTSR1(sport) & UTSR1_TBY);
/* 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);
spin_unlock_irqrestore(&sport->port.lock, flags);
if (UART_ENABLE_MS(&sport->port, cflag))
sa1100_enable_ms(&sport->port);
}
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,
"serial_sa1100") != 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,
change_speed: sa1100_change_speed,
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 = SERIAL_IO_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 __FUNCTION__ ": bad index number %d\n", idx);
return;
}
switch (port) {
case 1:
sa1100_ports[idx].port.membase = (void *)&Ser1UTCR0;
sa1100_ports[idx].port.mapbase = _Ser1UTCR0;
sa1100_ports[idx].port.irq = IRQ_Ser1UART;
sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF;
break;
case 2:
sa1100_ports[idx].port.membase = (void *)&Ser2UTCR0;
sa1100_ports[idx].port.mapbase = _Ser2UTCR0;
sa1100_ports[idx].port.irq = IRQ_Ser2ICP;
sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF;
break;
case 3:
sa1100_ports[idx].port.membase = (void *)&Ser3UTCR0;
sa1100_ports[idx].port.mapbase = _Ser3UTCR0;
sa1100_ports[idx].port.irq = IRQ_Ser3UART;
sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF;
break;
default:
printk(KERN_ERR __FUNCTION__ ": bad port number %d\n", port);
}
}
#ifdef CONFIG_SERIAL_SA1100_CONSOLE
/*
* 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, i;
/*
* 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);
/*
* Now, do each character
*/
for (i = 0; i < count; i++) {
do {
status = UART_GET_UTSR1(sport);
} while (!(status & UTSR1_TNF));
UART_PUT_CHAR(sport, s[i]);
if (s[i] == '\n') {
do {
status = UART_GET_UTSR1(sport);
} while (!(status & UTSR1_TNF));
UART_PUT_CHAR(sport, '\r');
}
}
/*
* 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);
}
static kdev_t sa1100_console_device(struct console *co)
{
return mk_kdev(SERIAL_SA1100_MAJOR, MINOR_START + co->index);
}
/*
* 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 console sa1100_console = {
name: "ttySA",
write: sa1100_console_write,
device: sa1100_console_device,
setup: sa1100_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
void __init sa1100_rs_console_init(void)
{
sa1100_init_ports();
register_console(&sa1100_console);
}
#define SA1100_CONSOLE &sa1100_console
#else
#define SA1100_CONSOLE NULL
#endif
static struct uart_driver sa1100_reg = {
owner: THIS_MODULE,
driver_name: "ttySA",
#ifdef CONFIG_DEVFS_FS
dev_name: "ttySA%d",
#else
dev_name: "ttySA",
#endif
major: SERIAL_SA1100_MAJOR,
minor: MINOR_START,
nr: NR_PORTS,
cons: SA1100_CONSOLE,
};
static int __init sa1100_serial_init(void)
{
int ret;
printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.41 $\n");
sa1100_init_ports();
ret = uart_register_driver(&sa1100_reg);
if (ret == 0) {
int i;
for (i = 0; i < NR_PORTS; i++)
uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port);
}
return ret;
}
static void __exit sa1100_serial_exit(void)
{
int i;
for (i = 0; i < NR_PORTS; i++)
uart_remove_one_port(&sa1100_reg, &sa1100_ports[i].port);
uart_unregister_driver(&sa1100_reg);
}
module_init(sa1100_serial_init);
module_exit(sa1100_serial_exit);
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Deep Blue Solutions Ltd");
MODULE_DESCRIPTION("SA1100 generic serial port driver $Revision: 1.41 $");
MODULE_LICENSE("GPL");
/*
* linux/drivers/char/serial_uart00.c
*
* Driver for UART00 serial ports
*
* Based on drivers/char/serial_amba.c, by ARM Limited &
* Deep Blue Solutions Ltd.
* Copyright 2001 Altera Corporation
*
* 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: serial_uart00.c,v 1.32 2002/07/20 17:10:04 rmk Exp $
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/pld/pld_hotswap.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/sizes.h>
#if defined(CONFIG_SERIAL_UART00_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
#include <asm/arch/excalibur.h>
#define UART00_TYPE (volatile unsigned int*)
#include <asm/arch/uart00.h>
#include <asm/arch/int_ctrl00.h>
#define UART_NR 2
#define SERIAL_UART00_NAME "ttyUA"
#define SERIAL_UART00_MAJOR 204
#define SERIAL_UART00_MINOR 16 /* Temporary - will change in future */
#define SERIAL_UART00_NR UART_NR
#define UART_PORT_SIZE 0x50
#define UART00_ISR_PASS_LIMIT 256
/*
* Access macros for the UART00 UARTs
*/
#define UART_GET_INT_STATUS(p) inl(UART_ISR((p)->membase))
#define UART_PUT_IES(p, c) outl(c,UART_IES((p)->membase))
#define UART_GET_IES(p) inl(UART_IES((p)->membase))
#define UART_PUT_IEC(p, c) outl(c,UART_IEC((p)->membase))
#define UART_GET_IEC(p) inl(UART_IEC((p)->membase))
#define UART_PUT_CHAR(p, c) outl(c,UART_TD((p)->membase))
#define UART_GET_CHAR(p) inl(UART_RD((p)->membase))
#define UART_GET_RSR(p) inl(UART_RSR((p)->membase))
#define UART_GET_RDS(p) inl(UART_RDS((p)->membase))
#define UART_GET_MSR(p) inl(UART_MSR((p)->membase))
#define UART_GET_MCR(p) inl(UART_MCR((p)->membase))
#define UART_PUT_MCR(p, c) outl(c,UART_MCR((p)->membase))
#define UART_GET_MC(p) inl(UART_MC((p)->membase))
#define UART_PUT_MC(p, c) outl(c,UART_MC((p)->membase))
#define UART_GET_TSR(p) inl(UART_TSR((p)->membase))
#define UART_GET_DIV_HI(p) inl(UART_DIV_HI((p)->membase))
#define UART_PUT_DIV_HI(p,c) outl(c,UART_DIV_HI((p)->membase))
#define UART_GET_DIV_LO(p) inl(UART_DIV_LO((p)->membase))
#define UART_PUT_DIV_LO(p,c) outl(c,UART_DIV_LO((p)->membase))
#define UART_RX_DATA(s) ((s) & UART_RSR_RX_LEVEL_MSK)
#define UART_TX_READY(s) (((s) & UART_TSR_TX_LEVEL_MSK) < 15)
//#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & UART00_UARTFR_TMSK) == 0)
static void uart00_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
UART_PUT_IEC(port, UART_IEC_TIE_MSK);
}
static void uart00_stop_rx(struct uart_port *port)
{
UART_PUT_IEC(port, UART_IEC_RE_MSK);
}
static void uart00_enable_ms(struct uart_port *port)
{
UART_PUT_IES(port, UART_IES_ME_MSK);
}
static void
uart00_rx_chars(struct uart_port *port, struct pt_regs *regs)
{
struct tty_struct *tty = port->info->tty;
unsigned int status, ch, rds, flg, ignored = 0;
status = UART_GET_RSR(port);
while (UART_RX_DATA(status)) {
/*
* We need to read rds before reading the
* character from the fifo
*/
rds = UART_GET_RDS(port);
ch = UART_GET_CHAR(port);
port->icount.rx++;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;
flg = TTY_NORMAL;
/*
* Note that the error handling code is
* out of the main execution path
*/
if (rds & (UART_RDS_BI_MSK |UART_RDS_FE_MSK|
UART_RDS_PE_MSK |UART_RDS_PE_MSK))
goto handle_error;
if (uart_handle_sysrq_char(port, ch, regs))
goto ignore_char;
error_return:
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
ignore_char:
status = UART_GET_RSR(port);
}
out:
tty_flip_buffer_push(tty);
return;
handle_error:
if (rds & UART_RDS_BI_MSK) {
status &= ~(UART_RDS_FE_MSK | UART_RDS_PE_MSK);
port->icount.brk++;
if (uart_handle_break(port))
goto ignore_char;
} else if (rds & UART_RDS_PE_MSK)
port->icount.parity++;
else if (rds & UART_RDS_PE_MSK)
port->icount.frame++;
if (rds & UART_RDS_OE_MSK)
port->icount.overrun++;
if (rds & port->ignore_status_mask) {
if (++ignored > 100)
goto out;
goto ignore_char;
}
rds &= port->read_status_mask;
if (rds & UART_RDS_BI_MSK)
flg = TTY_BREAK;
else if (rds & UART_RDS_PE_MSK)
flg = TTY_PARITY;
else if (rds & UART_RDS_FE_MSK)
flg = TTY_FRAME;
if (status & UART_RDS_OE_MSK) {
/*
* CHECK: does overrun affect the current character?
* ASSUMPTION: it does not.
*/
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;
ch = 0;
flg = TTY_OVERRUN;
}
#ifdef SUPPORT_SYSRQ
port->sysrq = 0;
#endif
goto error_return;
}
static void uart00_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->info->xmit;
int count;
if (port->x_char) {
while ((UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK) == 15);
UART_PUT_CHAR(port, port->x_char);
port->icount.tx++;
port->x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
uart00_stop_tx(port, 0);
return;
}
count = port->fifosize >> 1;
do {
while ((UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK) == 15);
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);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_event(port, EVT_WRITE_WAKEUP);
if (uart_circ_empty(xmit))
uart00_stop_tx(port, 0);
}
static void uart00_start_tx(struct uart_port *port, unsigned int tty_start)
{
UART_PUT_IES(port, UART_IES_TIE_MSK);
uart00_tx_chars(port);
}
static void uart00_modem_status(struct uart_port *port)
{
unsigned int status;
status = UART_GET_MSR(port);
if (!status & (UART_MSR_DCTS_MSK | UART_MSR_DDSR_MSK |
UART_MSR_TERI_MSK | UART_MSR_DDCD_MSK))
return;
if (status & UART_MSR_DDCD_MSK)
uart_handle_dcd_change(port, status & UART_MSR_DCD_MSK);
if (status & UART_MSR_DDSR_MSK)
port->icount.dsr++;
if (status & UART_MSR_DCTS_MSK)
uart_handle_cts_change(port, status & UART_MSR_CTS_MSK);
wake_up_interruptible(&port->info->delta_msr_wait);
}
static void uart00_int(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
unsigned int status, pass_counter = 0;
status = UART_GET_INT_STATUS(port);
do {
if (status & UART_ISR_RI_MSK)
uart00_rx_chars(port, regs);
if (status & UART_ISR_MI_MSK)
uart00_modem_status(port);
if (status & (UART_ISR_TI_MSK | UART_ISR_TII_MSK))
uart00_tx_chars(port);
if (pass_counter++ > UART00_ISR_PASS_LIMIT)
break;
status = UART_GET_INT_STATUS(port);
} while (status);
}
static unsigned int uart00_tx_empty(struct uart_port *port)
{
return UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK? 0 : TIOCSER_TEMT;
}
static unsigned int uart00_get_mctrl(struct uart_port *port)
{
unsigned int result = 0;
unsigned int status;
status = UART_GET_MSR(port);
if (status & UART_MSR_DCD_MSK)
result |= TIOCM_CAR;
if (status & UART_MSR_DSR_MSK)
result |= TIOCM_DSR;
if (status & UART_MSR_CTS_MSK)
result |= TIOCM_CTS;
if (status & UART_MSR_RI_MSK)
result |= TIOCM_RI;
return result;
}
static void uart00_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
{
}
static void uart00_break_ctl(struct uart_port *port, int break_state)
{
unsigned long flags;
unsigned int mcr;
spin_lock_irqsave(&port->lock, flags);
mcr = UART_GET_MCR(port);
if (break_state == -1)
mcr |= UART_MCR_BR_MSK;
else
mcr &= ~UART_MCR_BR_MSK;
UART_PUT_MCR(port, mcr);
spin_unlock_irqrestore(&port->lock, flags);
}
static void
uart00_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
unsigned int uart_mc, old_ies;
unsigned long flags;
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5:
uart_mc = UART_MC_CLS_CHARLEN_5;
break;
case CS6:
uart_mc = UART_MC_CLS_CHARLEN_6;
break;
case CS7:
uart_mc = UART_MC_CLS_CHARLEN_7;
break;
default: // CS8
uart_mc = UART_MC_CLS_CHARLEN_8;
break;
}
if (cflag & CSTOPB)
uart_mc|= UART_MC_ST_TWO;
if (cflag & PARENB) {
uart_mc |= UART_MC_PE_MSK;
if (!(cflag & PARODD))
uart_mc |= UART_MC_EP_MSK;
}
port->read_status_mask = UART_RDS_OE_MSK;
if (iflag & INPCK)
port->read_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK;
if (iflag & (BRKINT | PARMRK))
port->read_status_mask |= UART_RDS_BI_MSK;
/*
* Characters to ignore
*/
port->ignore_status_mask = 0;
if (iflag & IGNPAR)
port->ignore_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK;
if (iflag & IGNBRK) {
port->ignore_status_mask |= UART_RDS_BI_MSK;
/*
* If we're ignoring parity and break indicators,
* ignore overruns to (for real raw support).
*/
if (iflag & IGNPAR)
port->ignore_status_mask |= UART_RDS_OE_MSK;
}
/* first, disable everything */
spin_lock_irqsave(&port->lock, flags);
old_ies = UART_GET_IES(port);
if (UART_ENABLE_MS(port, cflag))
old_ies |= UART_IES_ME_MSK;
/* Set baud rate */
UART_PUT_DIV_LO(port, (quot & 0xff));
UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8));
UART_PUT_MC(port, uart_mc);
UART_PUT_IES(port, old_ies);
spin_unlock_irqrestore(&port->lock, flags);
}
static int uart00_startup(struct uart_port *port)
{
int result;
/*
* Allocate the IRQ
*/
result = request_irq(port->irq, uart00_int, 0, "uart00", port);
if (result) {
printk(KERN_ERR "Request of irq %d failed\n", port->irq);
return result;
}
/*
* Finally, enable interrupts. Use the TII interrupt to minimise
* the number of interrupts generated. If higher performance is
* needed, consider using the TI interrupt with a suitable FIFO
* threshold
*/
UART_PUT_IES(port, UART_IES_RE_MSK | UART_IES_TIE_MSK);
return 0;
}
static void uart00_shutdown(struct uart_port *port)
{
/*
* disable all interrupts, disable the port
*/
UART_PUT_IEC(port, 0xff);
/* disable break condition and fifos */
UART_PUT_MCR(port, UART_GET_MCR(port) &~UART_MCR_BR_MSK);
/*
* Free the interrupt
*/
free_irq(port->irq, port);
}
static const char *uart00_type(struct uart_port *port)
{
return port->type == PORT_UART00 ? "Altera UART00" : NULL;
}
/*
* Release the memory region(s) being used by 'port'
*/
static void uart00_release_port(struct uart_port *port)
{
release_mem_region(port->mapbase, UART_PORT_SIZE);
#ifdef CONFIG_ARCH_CAMELOT
if (port->membase != (void*)IO_ADDRESS(EXC_UART00_BASE)) {
iounmap(port->membase);
}
#endif
}
/*
* Request the memory region(s) being used by 'port'
*/
static int uart00_request_port(struct uart_port *port)
{
return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_uart00")
!= NULL ? 0 : -EBUSY;
}
/*
* Configure/autoconfigure the port.
*/
static void uart00_config_port(struct uart_port *port, int flags)
{
/*
* Map the io memory if this is a soft uart
*/
if (!port->membase)
port->membase = ioremap_nocache(port->mapbase,SZ_4K);
if (!port->membase)
printk(KERN_ERR "serial00: cannot map io memory\n");
else
port->type = PORT_UART00;
}
/*
* verify the new serial_struct (for TIOCSSERIAL).
*/
static int uart00_verify_port(struct uart_port *port, struct serial_struct *ser)
{
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00)
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 uart00_pops = {
tx_empty: uart00_tx_empty,
set_mctrl: uart00_set_mctrl_null,
get_mctrl: uart00_get_mctrl,
stop_tx: uart00_stop_tx,
start_tx: uart00_start_tx,
stop_rx: uart00_stop_rx,
enable_ms: uart00_enable_ms,
break_ctl: uart00_break_ctl,
startup: uart00_startup,
shutdown: uart00_shutdown,
change_speed: uart00_change_speed,
type: uart00_type,
release_port: uart00_release_port,
request_port: uart00_request_port,
config_port: uart00_config_port,
verify_port: uart00_verify_port,
};
#ifdef CONFIG_ARCH_CAMELOT
static struct uart_port epxa10db_port = {
membase: (void*)IO_ADDRESS(EXC_UART00_BASE),
mapbase: EXC_UART00_BASE,
iotype: SERIAL_IO_MEM,
irq: IRQ_UART,
uartclk: EXC_AHB2_CLK_FREQUENCY,
fifosize: 16,
ops: &uart00_pops,
flags: ASYNC_BOOT_AUTOCONF,
};
#endif
#ifdef CONFIG_SERIAL_UART00_CONSOLE
static void uart00_console_write(struct console *co, const char *s, unsigned count)
{
#ifdef CONFIG_ARCH_CAMELOT
struct uart_port *port = &epxa10db_port;
unsigned int status, old_ies;
int i;
/*
* First save the CR then disable the interrupts
*/
old_ies = UART_GET_IES(port);
UART_PUT_IEC(port,0xff);
/*
* Now, do each character
*/
for (i = 0; i < count; i++) {
do {
status = UART_GET_TSR(port);
} while (!UART_TX_READY(status));
UART_PUT_CHAR(port, s[i]);
if (s[i] == '\n') {
do {
status = UART_GET_TSR(port);
} while (!UART_TX_READY(status));
UART_PUT_CHAR(port, '\r');
}
}
/*
* Finally, wait for transmitter to become empty
* and restore the IES
*/
do {
status = UART_GET_TSR(port);
} while (status & UART_TSR_TX_LEVEL_MSK);
UART_PUT_IES(port, old_ies);
#endif
}
static kdev_t uart00_console_device(struct console *co)
{
return mk_kdev(SERIAL_UART00_MAJOR, SERIAL_UART00_MINOR + co->index);
}
static void __init
uart00_console_get_options(struct uart_port *port, int *baud,
int *parity, int *bits)
{
unsigned int uart_mc, quot;
uart_mc = UART_GET_MC(port);
*parity = 'n';
if (uart_mc & UART_MC_PE_MSK) {
if (uart_mc & UART_MC_EP_MSK)
*parity = 'e';
else
*parity = 'o';
}
switch (uart_mc & UART_MC_CLS_MSK) {
case UART_MC_CLS_CHARLEN_5:
*bits = 5;
break;
case UART_MC_CLS_CHARLEN_6:
*bits = 6;
break;
case UART_MC_CLS_CHARLEN_7:
*bits = 7;
break;
case UART_MC_CLS_CHARLEN_8:
*bits = 8;
break;
}
quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8);
*baud = port->uartclk / (16 *quot );
}
static int __init uart00_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 38400;
int bits = 8;
int parity = 'n';
int flow = 'n';
#ifdef CONFIG_ARCH_CAMELOT
port = &epxa10db_port; ;
#else
return -ENODEV;
#endif
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
uart00_console_get_options(port, &baud, &parity, &bits);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct console uart00_console = {
name: SERIAL_UART00_NAME,
write: uart00_console_write,
device: uart00_console_device,
setup: uart00_console_setup,
flags: CON_PRINTBUFFER,
index: 0,
};
void __init uart00_console_init(void)
{
register_console(&uart00_console);
}
#define UART00_CONSOLE &uart00_console
#else
#define UART00_CONSOLE NULL
#endif
static struct uart_driver uart00_reg = {
owner: NULL,
driver_name: SERIAL_UART00_NAME,
dev_name: SERIAL_UART00_NAME,
major: SERIAL_UART00_MAJOR,
minor: SERIAL_UART00_MINOR,
nr: UART_NR,
cons: UART00_CONSOLE,
};
struct dev_port_entry{
unsigned int base_addr;
struct uart_port *port;
};
static struct dev_port_entry dev_port_map[UART_NR];
#ifdef CONFIG_PLD_HOTSWAP
/*
* Keep a mapping of dev_info addresses -> port lines to use when
* removing ports dev==NULL indicates unused entry
*/
struct uart00_ps_data{
unsigned int clk;
unsigned int fifosize;
};
int uart00_add_device(struct pldhs_dev_info* dev_info, void* dev_ps_data)
{
struct uart00_ps_data* dev_ps=dev_ps_data;
struct uart_port * port;
int i,result;
i=0;
while(dev_port_map[i].port)
i++;
if(i==UART_NR){
printk(KERN_WARNING "uart00: Maximum number of ports reached\n");
return 0;
}
port=kmalloc(sizeof(struct uart_port),GFP_KERNEL);
if(!port)
return -ENOMEM;
printk("clk=%d fifo=%d\n",dev_ps->clk,dev_ps->fifosize);
port->membase=0;
port->mapbase=dev_info->base_addr;
port->iotype=SERIAL_IO_MEM;
port->irq=dev_info->irq;
port->uartclk=dev_ps->clk;
port->fifosize=dev_ps->fifosize;
port->ops=&uart00_pops;
port->line=i;
port->flags=ASYNC_BOOT_AUTOCONF;
result=uart_add_one_port(&uart00_reg, port);
if(result){
printk("uart_add_one_port returned %d\n",result);
return result;
}
dev_port_map[i].base_addr=dev_info->base_addr;
dev_port_map[i].port=port;
printk("uart00: added device at %x as ttyUA%d\n",dev_port_map[i].base_addr,i);
return 0;
}
int uart00_remove_devices(void)
{
int i,result;
result=0;
for(i=1;i<UART_NR;i++){
if(dev_port_map[i].base_addr){
result=uart_remove_one_port(&uart00_reg, dev_port_map[i].port);
if(result)
return result;
/* port removed sucessfully, so now tidy up */
kfree(dev_port_map[i].port);
dev_port_map[i].base_addr=0;
dev_port_map[i].port=NULL;
}
}
return 0;
}
struct pld_hotswap_ops uart00_pldhs_ops={
name: "uart00",
add_device: uart00_add_device,
remove_devices:uart00_remove_devices,
};
#endif
static int __init uart00_init(void)
{
int result;
printk(KERN_INFO "Serial: UART00 driver $Revision: 1.32 $\n");
printk(KERN_WARNING "serial_uart00:Using temporary major/minor pairs"
" - these WILL change in the future\n");
result = uart_register_driver(&uart00_reg);
if (result)
return result;
#ifdef CONFIG_ARCH_CAMELOT
result = uart_add_one_port(&uart00_reg,&epxa10db_port);
#endif
if (result)
uart_unregister_driver(&uart00_reg);
#ifdef CONFIG_PLD_HOTSWAP
pldhs_register_driver(&uart00_pldhs_ops);
#endif
return result;
}
__initcall(uart00_init);
/*
* linux/drivers/char/serial_core.h
*
* 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: serial_core.h,v 1.49 2002/07/20 18:06:32 rmk Exp $
*/
/*
* The type definitions. These are from Ted Ts'o's serial.h
*/
#define PORT_UNKNOWN 0
#define PORT_8250 1
#define PORT_16450 2
#define PORT_16550 3
#define PORT_16550A 4
#define PORT_CIRRUS 5
#define PORT_16650 6
#define PORT_16650V2 7
#define PORT_16750 8
#define PORT_STARTECH 9
#define PORT_16C950 10
#define PORT_16654 11
#define PORT_16850 12
#define PORT_RSA 13
#define PORT_MAX_8250 13 /* max port ID */
/*
* ARM specific type numbers. These are not currently guaranteed
* to be implemented, and will change in the future. These are
* separate so any additions to the old serial.c that occur before
* we are merged can be easily merged here.
*/
#define PORT_AMBA 32
#define PORT_CLPS711X 33
#define PORT_SA1100 34
#define PORT_UART00 35
#define PORT_21285 37
#ifdef __KERNEL__
#include <linux/config.h>
#include <linux/interrupt.h>
#include <linux/circ_buf.h>
#include <linux/spinlock.h>
struct uart_port;
struct uart_info;
struct serial_struct;
/*
* This structure describes all the operations that can be
* done on the physical hardware.
*/
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *, unsigned int tty_stop);
void (*start_tx)(struct uart_port *, unsigned int tty_start);
void (*send_xchar)(struct uart_port *, char ch);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*change_speed)(struct uart_port *, unsigned int cflag,
unsigned int iflag, unsigned int quot);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
int (*set_wake)(struct uart_port *, unsigned int state);
/*
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *);
/*
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void (*release_port)(struct uart_port *);
/*
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
};
#define UART_CONFIG_TYPE (1 << 0)
#define UART_CONFIG_IRQ (1 << 1)
struct uart_icount {
__u32 cts;
__u32 dsr;
__u32 rng;
__u32 dcd;
__u32 rx;
__u32 tx;
__u32 frame;
__u32 overrun;
__u32 parity;
__u32 brk;
__u32 buf_overrun;
};
struct uart_port {
spinlock_t lock; /* port lock */
unsigned int iobase; /* in/out[bwl] */
char *membase; /* read/write[bwl] */
unsigned int irq; /* irq number */
unsigned int uartclk; /* base uart clock */
unsigned char fifosize; /* tx fifo size */
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* io access style */
#define UPIO_PORT (0)
#define UPIO_HUB6 (1)
#define UPIO_MEM (2)
unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
struct uart_info *info; /* pointer to parent info */
struct uart_icount icount; /* statistics */
struct console *cons; /* struct console, if any */
#ifdef CONFIG_SERIAL_CORE_CONSOLE
unsigned long sysrq; /* sysrq timeout */
#endif
unsigned int flags;
#define UPF_HUP_NOTIFY (1 << 0)
#define UPF_SAK (1 << 2)
#define UPF_SPD_MASK (0x1030)
#define UPF_SPD_HI (0x0010)
#define UPF_SPD_VHI (0x0020)
#define UPF_SPD_CUST (0x0030)
#define UPF_SPD_SHI (0x1000)
#define UPF_SPD_WARP (0x1010)
#define UPF_SKIP_TEST (1 << 6)
#define UPF_AUTO_IRQ (1 << 7)
#define UPF_HARDPPS_CD (1 << 11)
#define UPF_LOW_LATENCY (1 << 13)
#define UPF_BUGGY_UART (1 << 14)
#define UPF_AUTOPROBE (1 << 15)
#define UPF_BOOT_AUTOCONF (1 << 28)
#define UPF_FLAGS (0x7fff)
#define UPF_USR_MASK (UPF_SPD_MASK|UPF_LOW_LATENCY)
unsigned int mctrl; /* current modem ctrl settings */
unsigned int timeout; /* character-based timeout */
unsigned int type; /* port type */
struct uart_ops *ops;
unsigned int line; /* port index */
unsigned long mapbase; /* for ioremap */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char unused[3];
};
/*
* This is the state information which is persistent across opens.
* The low level driver must not to touch any elements contained
* within.
*/
struct uart_state {
unsigned int close_delay;
unsigned int closing_wait;
#define USF_CLOSING_WAIT_INF (0)
#define USF_CLOSING_WAIT_NONE (65535)
unsigned int custom_divisor;
int count;
struct uart_info *info;
struct uart_port *port;
#ifdef CONFIG_PM
struct pm_dev *pm;
#endif
};
#define UART_XMIT_SIZE 1024
/*
* This is the state information which is only valid when the port
* is open; it may be freed by the core driver once the device has
* been closed. Either the low level driver or the core can modify
* stuff here.
*/
struct uart_info {
struct uart_port *port;
struct uart_state *state;
struct tty_struct *tty;
struct circ_buf xmit;
unsigned int flags;
/*
* These are the flags that specific to info->flags, and reflect our
* internal state. They can not be accessed via port->flags. Low
* level drivers must not change these, but may query them instead.
*/
#define UIF_CHECK_CD (1 << 25)
#define UIF_CTS_FLOW (1 << 26)
#define UIF_CLOSING (1 << 27)
#define UIF_NORMAL_ACTIVE (1 << 29)
#define UIF_INITIALIZED (1 << 31)
unsigned char *tmpbuf;
struct semaphore tmpbuf_sem;
unsigned long event;
int blocked_open;
struct tasklet_struct tlet;
wait_queue_head_t open_wait;
wait_queue_head_t delta_msr_wait;
};
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
#define EVT_WRITE_WAKEUP 0
struct module;
struct tty_driver;
struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
int nr;
struct console *cons;
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};
void uart_event(struct uart_port *port, int event);
struct uart_port *uart_get_console(struct uart_port *ports, int nr,
struct console *c);
void uart_parse_options(char *options, int *baud, int *parity, int *bits,
int *flow);
int uart_set_options(struct uart_port *port, struct console *co, int baud,
int parity, int bits, int flow);
int uart_register_driver(struct uart_driver *uart);
void uart_unregister_driver(struct uart_driver *uart);
void uart_unregister_port(struct uart_driver *reg, int line);
int uart_register_port(struct uart_driver *reg, struct uart_port *port);
int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
#define uart_circ_empty(circ) ((circ)->head == (circ)->tail)
#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0)
#define uart_circ_chars_pending(circ) \
(CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
#define uart_circ_chars_free(circ) \
(CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
#define uart_tx_stopped(port) \
((port)->info->tty->stopped || (port)->info->tty->hw_stopped)
/*
* The following are helper functions for the low level drivers.
*/
#ifdef SUPPORT_SYSRQ
static inline int
uart_handle_sysrq_char(struct uart_port *port, unsigned int ch,
struct pt_regs *regs)
{
if (port->sysrq) {
if (ch && time_before(jiffies, port->sysrq)) {
handle_sysrq(ch, regs, NULL);
port->sysrq = 0;
return 1;
}
port->sysrq = 0;
}
return 0;
}
#else
#define uart_handle_sysrq_char(port,ch,regs) (0)
#endif
/*
* We do the SysRQ and SAK checking like this...
*/
static inline int uart_handle_break(struct uart_port *port)
{
struct uart_info *info = port->info;
#ifdef SUPPORT_SYSRQ
if (port->cons && port->cons->index == port->line) {
if (!port->sysrq) {
port->sysrq = jiffies + HZ*5;
return 1;
}
port->sysrq = 0;
}
#endif
if (info->flags & UPF_SAK)
do_SAK(info->tty);
return 0;
}
/**
* uart_handle_dcd_change - handle a change of carrier detect state
* @port: uart_port structure for the open port
* @status: new carrier detect status, nonzero if active
*/
static inline void
uart_handle_dcd_change(struct uart_port *port, unsigned int status)
{
struct uart_info *info = port->info;
port->icount.dcd++;
#ifdef CONFIG_HARD_PPS
if ((port->flags & UPF_HARDPPS_CD) && status)
hardpps();
#endif
if (info->flags & UIF_CHECK_CD) {
if (status)
wake_up_interruptible(&info->open_wait);
else if (info->tty)
tty_hangup(info->tty);
}
}
/**
* uart_handle_cts_change - handle a change of clear-to-send state
* @port: uart_port structure for the open port
* @status: new clear to send status, nonzero if active
*/
static inline void
uart_handle_cts_change(struct uart_port *port, unsigned int status)
{
struct uart_info *info = port->info;
struct tty_struct *tty = info->tty;
port->icount.cts++;
if (info->flags & UIF_CTS_FLOW) {
if (tty->hw_stopped) {
if (status) {
tty->hw_stopped = 0;
port->ops->start_tx(port, 0);
uart_event(port, EVT_WRITE_WAKEUP);
}
} else {
if (!status) {
tty->hw_stopped = 1;
port->ops->stop_tx(port, 0);
}
}
}
}
/*
* UART_ENABLE_MS - determine if port should enable modem status irqs
*/
#define UART_ENABLE_MS(port,cflag) ((port)->flags & UPF_HARDPPS_CD || \
(cflag) & CRTSCTS || \
!(cflag) & CLOCAL)
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment