2005-04-17 06:20:36 +08:00
|
|
|
/* 68328serial.c: Serial port driver for 68328 microcontroller
|
|
|
|
*
|
|
|
|
* Copyright (C) 1995 David S. Miller <davem@caip.rutgers.edu>
|
|
|
|
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>
|
|
|
|
* Copyright (C) 1998, 1999 D. Jeff Dionne <jeff@uclinux.org>
|
|
|
|
* Copyright (C) 1999 Vladimir Gurevich <vgurevic@cisco.com>
|
|
|
|
* Copyright (C) 2002-2003 David McCullough <davidm@snapgear.com>
|
|
|
|
* Copyright (C) 2002 Greg Ungerer <gerg@snapgear.com>
|
|
|
|
*
|
|
|
|
* VZ Support/Fixes Evan Stawnyczy <e@lineo.ca>
|
|
|
|
* Multiple UART support Daniel Potts <danielp@cse.unsw.edu.au>
|
|
|
|
* Power management support Daniel Potts <danielp@cse.unsw.edu.au>
|
|
|
|
* VZ Second Serial Port enable Phil Wilshire
|
|
|
|
* 2.4/2.5 port David McCullough
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <asm/dbg.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/errno.h>
|
2012-04-02 19:54:35 +08:00
|
|
|
#include <linux/serial.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/signal.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/timer.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/mm.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/console.h>
|
|
|
|
#include <linux/reboot.h>
|
|
|
|
#include <linux/keyboard.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/pm.h>
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/delay.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/gfp.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <asm/delay.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
|
|
/* (es) */
|
|
|
|
/* note: perhaps we can murge these files, so that you can just
|
|
|
|
* define 1 of them, and they can sort that out for themselves
|
|
|
|
*/
|
|
|
|
#if defined(CONFIG_M68EZ328)
|
|
|
|
#include <asm/MC68EZ328.h>
|
|
|
|
#else
|
|
|
|
#if defined(CONFIG_M68VZ328)
|
|
|
|
#include <asm/MC68VZ328.h>
|
|
|
|
#else
|
|
|
|
#include <asm/MC68328.h>
|
|
|
|
#endif /* CONFIG_M68VZ328 */
|
|
|
|
#endif /* CONFIG_M68EZ328 */
|
|
|
|
|
|
|
|
/* Turn off usage of real serial interrupt code, to "support" Copilot */
|
|
|
|
#ifdef CONFIG_XCOPILOT_BUGS
|
|
|
|
#undef USE_INTS
|
|
|
|
#else
|
|
|
|
#define USE_INTS
|
|
|
|
#endif
|
|
|
|
|
2012-04-02 19:54:39 +08:00
|
|
|
/*
|
|
|
|
* I believe this is the optimal setting that reduces the number of interrupts.
|
|
|
|
* At high speeds the output might become a little "bursted" (use USTCNT_TXHE
|
|
|
|
* if that bothers you), but in most cases it will not, since we try to
|
|
|
|
* transmit characters every time rs_interrupt is called. Thus, quite often
|
|
|
|
* you'll see that a receive interrupt occures before the transmit one.
|
|
|
|
* -- Vladimir Gurevich
|
|
|
|
*/
|
|
|
|
#define USTCNT_TX_INTR_MASK (USTCNT_TXEE)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special
|
|
|
|
* "Old data interrupt" which occures whenever the data stay in the FIFO
|
|
|
|
* longer than 30 bits time. This allows us to use FIFO without compromising
|
|
|
|
* latency. '328 does not have this feature and without the real 328-based
|
|
|
|
* board I would assume that RXRE is the safest setting.
|
|
|
|
*
|
|
|
|
* For EZ328 I use RXHE (Half empty) interrupt to reduce the number of
|
|
|
|
* interrupts. RXFE (receive queue full) causes the system to lose data
|
|
|
|
* at least at 115200 baud
|
|
|
|
*
|
|
|
|
* If your board is busy doing other stuff, you might consider to use
|
|
|
|
* RXRE (data ready intrrupt) instead.
|
|
|
|
*
|
|
|
|
* The other option is to make these INTR masks run-time configurable, so
|
|
|
|
* that people can dynamically adapt them according to the current usage.
|
|
|
|
* -- Vladimir Gurevich
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* (es) */
|
|
|
|
#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328)
|
|
|
|
#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN)
|
|
|
|
#elif defined(CONFIG_M68328)
|
|
|
|
#define USTCNT_RX_INTR_MASK (USTCNT_RXRE)
|
|
|
|
#else
|
|
|
|
#error Please, define the Rx interrupt events for your CPU
|
|
|
|
#endif
|
|
|
|
/* (/es) */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is our internal structure for each serial port's state.
|
|
|
|
*/
|
|
|
|
struct m68k_serial {
|
2012-04-02 19:54:40 +08:00
|
|
|
struct tty_port tport;
|
2012-04-02 19:54:39 +08:00
|
|
|
char is_cons; /* Is this our console. */
|
|
|
|
int magic;
|
|
|
|
int baud_base;
|
|
|
|
int port;
|
|
|
|
int irq;
|
|
|
|
int type; /* UART type */
|
|
|
|
int custom_divisor;
|
|
|
|
int x_char; /* xon/xoff character */
|
|
|
|
int line;
|
|
|
|
unsigned char *xmit_buf;
|
|
|
|
int xmit_head;
|
|
|
|
int xmit_tail;
|
|
|
|
int xmit_cnt;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define SERIAL_MAGIC 0x5301
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Define the number of ports supported and their irqs.
|
|
|
|
*/
|
|
|
|
#define NR_PORTS 1
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct m68k_serial m68k_soft[NR_PORTS];
|
|
|
|
|
2012-04-02 19:54:39 +08:00
|
|
|
static unsigned int uart_irqs[NR_PORTS] = { UART_IRQ_NUM };
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* multiple ports are contiguous in memory */
|
|
|
|
m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR;
|
|
|
|
|
|
|
|
struct tty_driver *serial_driver;
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
static void change_speed(struct m68k_serial *info, struct tty_struct *tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup for console. Argument comes from the boot command line.
|
|
|
|
*/
|
|
|
|
|
2010-07-21 06:26:54 +08:00
|
|
|
/* note: this is messy, but it works, again, perhaps defined somewhere else?*/
|
|
|
|
#ifdef CONFIG_M68VZ328
|
|
|
|
#define CONSOLE_BAUD_RATE 19200
|
|
|
|
#define DEFAULT_CBAUD B19200
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
|
2010-07-21 06:26:54 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifndef CONSOLE_BAUD_RATE
|
|
|
|
#define CONSOLE_BAUD_RATE 9600
|
|
|
|
#define DEFAULT_CBAUD B9600
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static int m68328_console_initted = 0;
|
|
|
|
static int m68328_console_baud = CONSOLE_BAUD_RATE;
|
|
|
|
static int m68328_console_cbaud = DEFAULT_CBAUD;
|
|
|
|
|
|
|
|
|
|
|
|
static inline int serial_paranoia_check(struct m68k_serial *info,
|
|
|
|
char *name, const char *routine)
|
|
|
|
{
|
|
|
|
#ifdef SERIAL_PARANOIA_CHECK
|
|
|
|
static const char *badmagic =
|
|
|
|
"Warning: bad magic number for serial struct %s in %s\n";
|
|
|
|
static const char *badinfo =
|
|
|
|
"Warning: null m68k_serial for %s in %s\n";
|
|
|
|
|
|
|
|
if (!info) {
|
|
|
|
printk(badinfo, name, routine);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (info->magic != SERIAL_MAGIC) {
|
|
|
|
printk(badmagic, name, routine);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is used to figure out the divisor speeds and the timeouts
|
|
|
|
*/
|
|
|
|
static int baud_table[] = {
|
|
|
|
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
|
|
|
|
9600, 19200, 38400, 57600, 115200, 0 };
|
|
|
|
|
|
|
|
/* Utility routines */
|
|
|
|
static inline int get_baud(struct m68k_serial *ss)
|
|
|
|
{
|
|
|
|
unsigned long result = 115200;
|
|
|
|
unsigned short int baud = uart_addr[ss->line].ubaud;
|
|
|
|
if (GET_FIELD(baud, UBAUD_PRESCALER) == 0x38) result = 38400;
|
|
|
|
result >>= GET_FIELD(baud, UBAUD_DIVIDE);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
* rs_stop() and rs_start()
|
|
|
|
*
|
|
|
|
* This routines are called before setting or resetting tty->stopped.
|
|
|
|
* They enable or disable transmitter interrupts, as necessary.
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
static void rs_stop(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_stop"))
|
|
|
|
return;
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
uart->ustcnt &= ~USTCNT_TXEN;
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-04-30 15:54:01 +08:00
|
|
|
static int rs_put_char(char ch)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-04-02 19:54:38 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int loops = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
while (!(UTX & UTX_TX_AVAIL) && (loops < 1000)) {
|
|
|
|
loops++;
|
|
|
|
udelay(5);
|
|
|
|
}
|
|
|
|
|
|
|
|
UTX_TXDATA = ch;
|
|
|
|
udelay(5);
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2008-04-30 15:54:01 +08:00
|
|
|
return 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rs_start(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_start"))
|
|
|
|
return;
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (info->xmit_cnt && info->xmit_buf && !(uart->ustcnt & USTCNT_TXEN)) {
|
|
|
|
#ifdef USE_INTS
|
|
|
|
uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK;
|
|
|
|
#else
|
|
|
|
uart->ustcnt |= USTCNT_TXEN;
|
|
|
|
#endif
|
|
|
|
}
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
static void receive_chars(struct m68k_serial *info, struct tty_struct *tty,
|
|
|
|
unsigned short rx)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 12:54:13 +08:00
|
|
|
unsigned char ch, flag;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This do { } while() loop will get ALL chars out of Rx FIFO
|
|
|
|
*/
|
|
|
|
#ifndef CONFIG_XCOPILOT_BUGS
|
|
|
|
do {
|
|
|
|
#endif
|
|
|
|
ch = GET_FIELD(rx, URX_RXDATA);
|
|
|
|
|
|
|
|
if(info->is_cons) {
|
|
|
|
if(URX_BREAK & rx) { /* whee, break received */
|
|
|
|
return;
|
|
|
|
#ifdef CONFIG_MAGIC_SYSRQ
|
|
|
|
} else if (ch == 0x10) { /* ^P */
|
|
|
|
show_state();
|
2011-05-25 08:11:16 +08:00
|
|
|
show_free_areas(0);
|
2005-04-17 06:20:36 +08:00
|
|
|
show_buffers();
|
|
|
|
/* show_net_buffers(); */
|
|
|
|
return;
|
|
|
|
} else if (ch == 0x12) { /* ^R */
|
2005-07-27 01:59:54 +08:00
|
|
|
emergency_restart();
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
#endif /* CONFIG_MAGIC_SYSRQ */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!tty)
|
|
|
|
goto clear_and_exit;
|
|
|
|
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 12:54:13 +08:00
|
|
|
flag = TTY_NORMAL;
|
|
|
|
|
2012-04-02 19:54:36 +08:00
|
|
|
if (rx & URX_PARITY_ERROR)
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 12:54:13 +08:00
|
|
|
flag = TTY_PARITY;
|
2012-04-02 19:54:36 +08:00
|
|
|
else if (rx & URX_OVRUN)
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 12:54:13 +08:00
|
|
|
flag = TTY_OVERRUN;
|
2012-04-02 19:54:36 +08:00
|
|
|
else if (rx & URX_FRAME_ERROR)
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 12:54:13 +08:00
|
|
|
flag = TTY_FRAME;
|
2012-04-02 19:54:36 +08:00
|
|
|
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 12:54:13 +08:00
|
|
|
tty_insert_flip_char(tty, ch, flag);
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifndef CONFIG_XCOPILOT_BUGS
|
|
|
|
} while((rx = uart->urx.w) & URX_DATA_READY);
|
|
|
|
#endif
|
|
|
|
|
2006-02-08 07:19:17 +08:00
|
|
|
tty_schedule_flip(tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
clear_and_exit:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
static void transmit_chars(struct m68k_serial *info, struct tty_struct *tty)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
|
|
|
|
if (info->x_char) {
|
|
|
|
/* Send next char */
|
|
|
|
uart->utx.b.txdata = info->x_char;
|
|
|
|
info->x_char = 0;
|
|
|
|
goto clear_and_return;
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
if ((info->xmit_cnt <= 0) || !tty || tty->stopped) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* That's peculiar... TX ints off */
|
|
|
|
uart->ustcnt &= ~USTCNT_TX_INTR_MASK;
|
|
|
|
goto clear_and_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send char */
|
|
|
|
uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++];
|
|
|
|
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
|
|
|
|
info->xmit_cnt--;
|
|
|
|
|
|
|
|
if(info->xmit_cnt <= 0) {
|
|
|
|
/* All done for now... TX ints off */
|
|
|
|
uart->ustcnt &= ~USTCNT_TX_INTR_MASK;
|
|
|
|
goto clear_and_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clear_and_return:
|
|
|
|
/* Clear interrupt (should be auto)*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the serial driver's generic interrupt routine
|
|
|
|
*/
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
irqreturn_t rs_interrupt(int irq, void *dev_id)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-08-20 11:49:40 +08:00
|
|
|
struct m68k_serial *info = dev_id;
|
2012-04-02 19:54:45 +08:00
|
|
|
struct tty_struct *tty = tty_port_tty_get(&info->tport);
|
2005-04-17 06:20:36 +08:00
|
|
|
m68328_uart *uart;
|
|
|
|
unsigned short rx;
|
|
|
|
unsigned short tx;
|
|
|
|
|
|
|
|
uart = &uart_addr[info->line];
|
|
|
|
rx = uart->urx.w;
|
|
|
|
|
|
|
|
#ifdef USE_INTS
|
|
|
|
tx = uart->utx.w;
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
if (rx & URX_DATA_READY)
|
|
|
|
receive_chars(info, tty, rx);
|
|
|
|
if (tx & UTX_TX_AVAIL)
|
|
|
|
transmit_chars(info, tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
#else
|
2012-04-02 19:54:44 +08:00
|
|
|
receive_chars(info, tty, rx);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2012-04-02 19:54:45 +08:00
|
|
|
tty_kref_put(tty);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
static int startup(struct m68k_serial *info, struct tty_struct *tty)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned long flags;
|
|
|
|
|
2012-04-02 19:54:43 +08:00
|
|
|
if (info->tport.flags & ASYNC_INITIALIZED)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!info->xmit_buf) {
|
|
|
|
info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL);
|
|
|
|
if (!info->xmit_buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the FIFO buffers and disable them
|
|
|
|
* (they will be reenabled in change_speed())
|
|
|
|
*/
|
|
|
|
|
|
|
|
uart->ustcnt = USTCNT_UEN;
|
|
|
|
uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN;
|
|
|
|
(void)uart->urx.w;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, enable sequencing and interrupts
|
|
|
|
*/
|
|
|
|
#ifdef USE_INTS
|
|
|
|
uart->ustcnt = USTCNT_UEN | USTCNT_RXEN |
|
|
|
|
USTCNT_RX_INTR_MASK | USTCNT_TX_INTR_MASK;
|
|
|
|
#else
|
|
|
|
uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK;
|
|
|
|
#endif
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
if (tty)
|
|
|
|
clear_bit(TTY_IO_ERROR, &tty->flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* and set the speed of the serial port
|
|
|
|
*/
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
change_speed(info, tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-04-02 19:54:43 +08:00
|
|
|
info->tport.flags |= ASYNC_INITIALIZED;
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine will shutdown a serial port; interrupts are disabled, and
|
|
|
|
* DTR is dropped if the hangup on close termio flag is on.
|
|
|
|
*/
|
2012-04-02 19:54:44 +08:00
|
|
|
static void shutdown(struct m68k_serial *info, struct tty_struct *tty)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
uart->ustcnt = 0; /* All off! */
|
2012-04-02 19:54:43 +08:00
|
|
|
if (!(info->tport.flags & ASYNC_INITIALIZED))
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (info->xmit_buf) {
|
|
|
|
free_page((unsigned long) info->xmit_buf);
|
|
|
|
info->xmit_buf = 0;
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
if (tty)
|
|
|
|
set_bit(TTY_IO_ERROR, &tty->flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-04-02 19:54:43 +08:00
|
|
|
info->tport.flags &= ~ASYNC_INITIALIZED;
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct {
|
|
|
|
int divisor, prescale;
|
|
|
|
}
|
|
|
|
#ifndef CONFIG_M68VZ328
|
|
|
|
hw_baud_table[18] = {
|
|
|
|
{0,0}, /* 0 */
|
|
|
|
{0,0}, /* 50 */
|
|
|
|
{0,0}, /* 75 */
|
|
|
|
{0,0}, /* 110 */
|
|
|
|
{0,0}, /* 134 */
|
|
|
|
{0,0}, /* 150 */
|
|
|
|
{0,0}, /* 200 */
|
|
|
|
{7,0x26}, /* 300 */
|
|
|
|
{6,0x26}, /* 600 */
|
|
|
|
{5,0x26}, /* 1200 */
|
|
|
|
{0,0}, /* 1800 */
|
|
|
|
{4,0x26}, /* 2400 */
|
|
|
|
{3,0x26}, /* 4800 */
|
|
|
|
{2,0x26}, /* 9600 */
|
|
|
|
{1,0x26}, /* 19200 */
|
|
|
|
{0,0x26}, /* 38400 */
|
|
|
|
{1,0x38}, /* 57600 */
|
|
|
|
{0,0x38}, /* 115200 */
|
|
|
|
};
|
|
|
|
#else
|
|
|
|
hw_baud_table[18] = {
|
|
|
|
{0,0}, /* 0 */
|
|
|
|
{0,0}, /* 50 */
|
|
|
|
{0,0}, /* 75 */
|
|
|
|
{0,0}, /* 110 */
|
|
|
|
{0,0}, /* 134 */
|
|
|
|
{0,0}, /* 150 */
|
|
|
|
{0,0}, /* 200 */
|
|
|
|
{0,0}, /* 300 */
|
|
|
|
{7,0x26}, /* 600 */
|
|
|
|
{6,0x26}, /* 1200 */
|
|
|
|
{0,0}, /* 1800 */
|
|
|
|
{5,0x26}, /* 2400 */
|
|
|
|
{4,0x26}, /* 4800 */
|
|
|
|
{3,0x26}, /* 9600 */
|
|
|
|
{2,0x26}, /* 19200 */
|
|
|
|
{1,0x26}, /* 38400 */
|
|
|
|
{0,0x26}, /* 57600 */
|
|
|
|
{1,0x38}, /* 115200 */
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
/* rate = 1036800 / ((65 - prescale) * (1<<divider)) */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine is called to set the UART divisor registers to match
|
|
|
|
* the specified baud rate for a serial port.
|
|
|
|
*/
|
2012-04-02 19:54:44 +08:00
|
|
|
static void change_speed(struct m68k_serial *info, struct tty_struct *tty)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned short port;
|
|
|
|
unsigned short ustcnt;
|
|
|
|
unsigned cflag;
|
|
|
|
int i;
|
|
|
|
|
2012-08-08 03:47:27 +08:00
|
|
|
cflag = tty->termios.c_cflag;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!(port = info->port))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ustcnt = uart->ustcnt;
|
|
|
|
uart->ustcnt = ustcnt & ~USTCNT_TXEN;
|
|
|
|
|
|
|
|
i = cflag & CBAUD;
|
|
|
|
if (i & CBAUDEX) {
|
|
|
|
i = (i & ~CBAUDEX) + B38400;
|
|
|
|
}
|
|
|
|
|
|
|
|
uart->ubaud = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) |
|
|
|
|
PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale);
|
|
|
|
|
|
|
|
ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7);
|
|
|
|
|
|
|
|
if ((cflag & CSIZE) == CS8)
|
|
|
|
ustcnt |= USTCNT_8_7;
|
|
|
|
|
|
|
|
if (cflag & CSTOPB)
|
|
|
|
ustcnt |= USTCNT_STOP;
|
|
|
|
|
|
|
|
if (cflag & PARENB)
|
|
|
|
ustcnt |= USTCNT_PARITYEN;
|
|
|
|
if (cflag & PARODD)
|
|
|
|
ustcnt |= USTCNT_ODD_EVEN;
|
|
|
|
|
|
|
|
#ifdef CONFIG_SERIAL_68328_RTS_CTS
|
|
|
|
if (cflag & CRTSCTS) {
|
|
|
|
uart->utx.w &= ~ UTX_NOCTS;
|
|
|
|
} else {
|
|
|
|
uart->utx.w |= UTX_NOCTS;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ustcnt |= USTCNT_TXEN;
|
|
|
|
|
|
|
|
uart->ustcnt = ustcnt;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fair output driver allows a process to speak.
|
|
|
|
*/
|
|
|
|
static void rs_fair_output(void)
|
|
|
|
{
|
|
|
|
int left; /* Output no more than that */
|
|
|
|
unsigned long flags;
|
|
|
|
struct m68k_serial *info = &m68k_soft[0];
|
|
|
|
char c;
|
|
|
|
|
|
|
|
if (info == 0) return;
|
|
|
|
if (info->xmit_buf == 0) return;
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
left = info->xmit_cnt;
|
|
|
|
while (left != 0) {
|
|
|
|
c = info->xmit_buf[info->xmit_tail];
|
|
|
|
info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
|
|
|
|
info->xmit_cnt--;
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
rs_put_char(c);
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
left = min(info->xmit_cnt, left-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Last character is being transmitted now (hopefully). */
|
|
|
|
udelay(5);
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* m68k_console_print is registered for printk.
|
|
|
|
*/
|
|
|
|
void console_print_68328(const char *p)
|
|
|
|
{
|
|
|
|
char c;
|
|
|
|
|
|
|
|
while((c=*(p++)) != 0) {
|
|
|
|
if(c == '\n')
|
|
|
|
rs_put_char('\r');
|
|
|
|
rs_put_char(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Comment this if you want to have a strict interrupt-driven output */
|
|
|
|
rs_fair_output();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rs_set_ldisc(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_set_ldisc"))
|
|
|
|
return;
|
|
|
|
|
2012-08-08 03:47:27 +08:00
|
|
|
info->is_cons = (tty->termios.c_line == N_TTY);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rs_flush_chars(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
|
|
|
|
return;
|
|
|
|
#ifndef USE_INTS
|
|
|
|
for(;;) {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Enable transmitter */
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
|
|
|
|
!info->xmit_buf) {
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_INTS
|
|
|
|
uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK;
|
|
|
|
#else
|
|
|
|
uart->ustcnt |= USTCNT_TXEN;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_INTS
|
|
|
|
if (uart->utx.w & UTX_TX_AVAIL) {
|
|
|
|
#else
|
|
|
|
if (1) {
|
|
|
|
#endif
|
|
|
|
/* Send char */
|
|
|
|
uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++];
|
|
|
|
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
|
|
|
|
info->xmit_cnt--;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef USE_INTS
|
|
|
|
while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5);
|
|
|
|
}
|
|
|
|
#endif
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
extern void console_printn(const char * b, int count);
|
|
|
|
|
|
|
|
static int rs_write(struct tty_struct * tty,
|
|
|
|
const unsigned char *buf, int count)
|
|
|
|
{
|
|
|
|
int c, total = 0;
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_write"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!tty || !info->xmit_buf)
|
|
|
|
return 0;
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_save_flags(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
while (1) {
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
|
|
|
|
SERIAL_XMIT_SIZE - info->xmit_head));
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (c <= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy(info->xmit_buf + info->xmit_head, buf, c);
|
2006-06-28 13:54:22 +08:00
|
|
|
|
|
|
|
local_irq_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
|
|
|
|
info->xmit_cnt += c;
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
buf += c;
|
|
|
|
count -= c;
|
|
|
|
total += c;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
|
|
|
|
/* Enable transmitter */
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifndef USE_INTS
|
|
|
|
while(info->xmit_cnt) {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uart->ustcnt |= USTCNT_TXEN;
|
|
|
|
#ifdef USE_INTS
|
|
|
|
uart->ustcnt |= USTCNT_TX_INTR_MASK;
|
|
|
|
#else
|
|
|
|
while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5);
|
|
|
|
#endif
|
|
|
|
if (uart->utx.w & UTX_TX_AVAIL) {
|
|
|
|
uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++];
|
|
|
|
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
|
|
|
|
info->xmit_cnt--;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef USE_INTS
|
|
|
|
}
|
|
|
|
#endif
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2006-06-28 13:54:22 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rs_write_room(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_write_room"))
|
|
|
|
return 0;
|
|
|
|
ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
|
|
|
|
if (ret < 0)
|
|
|
|
ret = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rs_chars_in_buffer(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
|
|
|
|
return 0;
|
|
|
|
return info->xmit_cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rs_flush_buffer(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
2006-06-28 13:54:22 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
|
|
|
|
return;
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
tty_wakeup(tty);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
* rs_throttle()
|
|
|
|
*
|
|
|
|
* This routine is called by the upper-layer tty layer to signal that
|
|
|
|
* incoming characters should be throttled.
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
static void rs_throttle(struct tty_struct * tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_throttle"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (I_IXOFF(tty))
|
|
|
|
info->x_char = STOP_CHAR(tty);
|
|
|
|
|
|
|
|
/* Turn off RTS line (do this atomic) */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rs_unthrottle(struct tty_struct * tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (I_IXOFF(tty)) {
|
|
|
|
if (info->x_char)
|
|
|
|
info->x_char = 0;
|
|
|
|
else
|
|
|
|
info->x_char = START_CHAR(tty);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assert RTS line (do this atomic) */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
* rs_ioctl() and friends
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int get_serial_info(struct m68k_serial * info,
|
|
|
|
struct serial_struct * retinfo)
|
|
|
|
{
|
|
|
|
struct serial_struct tmp;
|
|
|
|
|
|
|
|
if (!retinfo)
|
|
|
|
return -EFAULT;
|
|
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
|
|
tmp.type = info->type;
|
|
|
|
tmp.line = info->line;
|
|
|
|
tmp.port = info->port;
|
|
|
|
tmp.irq = info->irq;
|
2012-04-02 19:54:43 +08:00
|
|
|
tmp.flags = info->tport.flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
tmp.baud_base = info->baud_base;
|
2012-04-02 19:54:42 +08:00
|
|
|
tmp.close_delay = info->tport.close_delay;
|
|
|
|
tmp.closing_wait = info->tport.closing_wait;
|
2005-04-17 06:20:36 +08:00
|
|
|
tmp.custom_divisor = info->custom_divisor;
|
2010-08-01 14:29:06 +08:00
|
|
|
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
static int set_serial_info(struct m68k_serial *info, struct tty_struct *tty,
|
2005-04-17 06:20:36 +08:00
|
|
|
struct serial_struct * new_info)
|
|
|
|
{
|
2012-04-02 19:54:42 +08:00
|
|
|
struct tty_port *port = &info->tport;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct serial_struct new_serial;
|
|
|
|
struct m68k_serial old_info;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
if (!new_info)
|
|
|
|
return -EFAULT;
|
2010-08-01 14:29:06 +08:00
|
|
|
if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
|
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
old_info = *info;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN)) {
|
|
|
|
if ((new_serial.baud_base != info->baud_base) ||
|
|
|
|
(new_serial.type != info->type) ||
|
2012-04-02 19:54:42 +08:00
|
|
|
(new_serial.close_delay != port->close_delay) ||
|
2012-04-02 19:54:35 +08:00
|
|
|
((new_serial.flags & ~ASYNC_USR_MASK) !=
|
2012-04-02 19:54:43 +08:00
|
|
|
(port->flags & ~ASYNC_USR_MASK)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EPERM;
|
2012-04-02 19:54:43 +08:00
|
|
|
port->flags = ((port->flags & ~ASYNC_USR_MASK) |
|
2012-04-02 19:54:35 +08:00
|
|
|
(new_serial.flags & ASYNC_USR_MASK));
|
2005-04-17 06:20:36 +08:00
|
|
|
info->custom_divisor = new_serial.custom_divisor;
|
|
|
|
goto check_and_exit;
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:54:42 +08:00
|
|
|
if (port->count > 1)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, past this point, all the error checking has been done.
|
|
|
|
* At this point, we start making changes.....
|
|
|
|
*/
|
|
|
|
|
|
|
|
info->baud_base = new_serial.baud_base;
|
2012-04-02 19:54:43 +08:00
|
|
|
port->flags = ((port->flags & ~ASYNC_FLAGS) |
|
2012-04-02 19:54:35 +08:00
|
|
|
(new_serial.flags & ASYNC_FLAGS));
|
2005-04-17 06:20:36 +08:00
|
|
|
info->type = new_serial.type;
|
2012-04-02 19:54:42 +08:00
|
|
|
port->close_delay = new_serial.close_delay;
|
|
|
|
port->closing_wait = new_serial.closing_wait;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
check_and_exit:
|
2012-04-02 19:54:44 +08:00
|
|
|
retval = startup(info, tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get_lsr_info - get line status register info
|
|
|
|
*
|
|
|
|
* Purpose: Let user call ioctl() to get info when the UART physically
|
|
|
|
* is emptied. On bus types like RS485, the transmitter must
|
|
|
|
* release the bus after transmitting. This must be done when
|
|
|
|
* the transmit shift register is empty, not be done when the
|
|
|
|
* transmit holding register is empty. This functionality
|
|
|
|
* allows an RS485 driver to be written in user space.
|
|
|
|
*/
|
|
|
|
static int get_lsr_info(struct m68k_serial * info, unsigned int *value)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_SERIAL_68328_RTS_CTS
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
#endif
|
|
|
|
unsigned char status;
|
2006-06-28 13:54:22 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_SERIAL_68328_RTS_CTS
|
|
|
|
status = (uart->utx.w & UTX_CTS_STAT) ? 1 : 0;
|
|
|
|
#else
|
|
|
|
status = 0;
|
|
|
|
#endif
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2010-08-01 14:29:06 +08:00
|
|
|
return put_user(status, value);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine sends a break character out the serial port.
|
|
|
|
*/
|
2005-06-26 05:59:33 +08:00
|
|
|
static void send_break(struct m68k_serial * info, unsigned int duration)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned long flags;
|
|
|
|
if (!info->port)
|
|
|
|
return;
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef USE_INTS
|
|
|
|
uart->utx.w |= UTX_SEND_BREAK;
|
2005-06-26 05:59:33 +08:00
|
|
|
msleep_interruptible(duration);
|
2005-04-17 06:20:36 +08:00
|
|
|
uart->utx.w &= ~UTX_SEND_BREAK;
|
|
|
|
#endif
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-02-15 00:27:22 +08:00
|
|
|
static int rs_ioctl(struct tty_struct *tty,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
|
|
|
|
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
|
|
|
|
(cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
|
|
|
|
if (tty->flags & (1 << TTY_IO_ERROR))
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case TCSBRK: /* SVID version: non-zero arg --> no break */
|
|
|
|
retval = tty_check_change(tty);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
tty_wait_until_sent(tty, 0);
|
|
|
|
if (!arg)
|
2005-06-26 05:59:33 +08:00
|
|
|
send_break(info, 250); /* 1/4 second */
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
case TCSBRKP: /* support for POSIX tcsendbreak() */
|
|
|
|
retval = tty_check_change(tty);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
tty_wait_until_sent(tty, 0);
|
2005-06-26 05:59:33 +08:00
|
|
|
send_break(info, arg ? arg*(100) : 250);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
case TIOCGSERIAL:
|
2010-08-01 14:29:06 +08:00
|
|
|
return get_serial_info(info,
|
|
|
|
(struct serial_struct *) arg);
|
2005-04-17 06:20:36 +08:00
|
|
|
case TIOCSSERIAL:
|
2012-04-02 19:54:44 +08:00
|
|
|
return set_serial_info(info, tty,
|
2005-04-17 06:20:36 +08:00
|
|
|
(struct serial_struct *) arg);
|
|
|
|
case TIOCSERGETLSR: /* Get line status register */
|
2010-08-01 14:29:06 +08:00
|
|
|
return get_lsr_info(info, (unsigned int *) arg);
|
2005-04-17 06:20:36 +08:00
|
|
|
case TIOCSERGSTRUCT:
|
2010-08-01 14:29:06 +08:00
|
|
|
if (copy_to_user((struct m68k_serial *) arg,
|
|
|
|
info, sizeof(struct m68k_serial)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-12-08 18:38:45 +08:00
|
|
|
static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
change_speed(info, tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if ((old_termios->c_cflag & CRTSCTS) &&
|
2012-08-08 03:47:27 +08:00
|
|
|
!(tty->termios.c_cflag & CRTSCTS)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
tty->hw_stopped = 0;
|
|
|
|
rs_start(tty);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
* rs_close()
|
|
|
|
*
|
|
|
|
* This routine is called when the serial port gets closed. First, we
|
|
|
|
* wait for the last remaining data to be sent. Then, we unlink its
|
|
|
|
* S structure from the interrupt chain if necessary, and we free
|
|
|
|
* that IRQ if nothing is left in the chain.
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
static void rs_close(struct tty_struct *tty, struct file * filp)
|
|
|
|
{
|
|
|
|
struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
|
2012-04-02 19:54:40 +08:00
|
|
|
struct tty_port *port = &info->tport;
|
2005-04-17 06:20:36 +08:00
|
|
|
m68328_uart *uart = &uart_addr[info->line];
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
|
|
|
|
return;
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (tty_hung_up_p(filp)) {
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:54:40 +08:00
|
|
|
if ((tty->count == 1) && (port->count != 1)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Uh, oh. tty->count is 1, which means that the tty
|
|
|
|
* structure will be freed. Info->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("rs_close: bad serial port count; tty->count is 1, "
|
2012-04-02 19:54:40 +08:00
|
|
|
"port->count is %d\n", port->count);
|
|
|
|
port->count = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-04-02 19:54:40 +08:00
|
|
|
if (--port->count < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk("rs_close: bad serial port count for ttyS%d: %d\n",
|
2012-04-02 19:54:40 +08:00
|
|
|
info->line, port->count);
|
|
|
|
port->count = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-04-02 19:54:40 +08:00
|
|
|
if (port->count) {
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
2012-04-02 19:54:43 +08:00
|
|
|
port->flags |= ASYNC_CLOSING;
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Now we wait for the transmit buffer to clear; and we notify
|
|
|
|
* the line discipline to only process XON/XOFF characters.
|
|
|
|
*/
|
|
|
|
tty->closing = 1;
|
2012-04-02 19:54:42 +08:00
|
|
|
if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
|
|
|
|
tty_wait_until_sent(tty, port->closing_wait);
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* At this point we stop accepting input. To do this, we
|
|
|
|
* disable the receive line status interrupts, and tell the
|
|
|
|
* interrupt driver to stop checking the data ready bit in the
|
|
|
|
* line status register.
|
|
|
|
*/
|
|
|
|
|
|
|
|
uart->ustcnt &= ~USTCNT_RXEN;
|
|
|
|
uart->ustcnt &= ~(USTCNT_RXEN | USTCNT_RX_INTR_MASK);
|
|
|
|
|
2012-04-02 19:54:44 +08:00
|
|
|
shutdown(info, tty);
|
2008-04-30 15:53:59 +08:00
|
|
|
rs_flush_buffer(tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
tty_ldisc_flush(tty);
|
|
|
|
tty->closing = 0;
|
2012-04-02 19:54:45 +08:00
|
|
|
tty_port_tty_set(&info->tport, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
#warning "This is not and has never been valid so fix it"
|
|
|
|
#if 0
|
|
|
|
if (tty->ldisc.num != ldiscs[N_TTY].num) {
|
|
|
|
if (tty->ldisc.close)
|
|
|
|
(tty->ldisc.close)(tty);
|
|
|
|
tty->ldisc = ldiscs[N_TTY];
|
2012-08-08 03:47:27 +08:00
|
|
|
tty->termios.c_line = N_TTY;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (tty->ldisc.open)
|
|
|
|
(tty->ldisc.open)(tty);
|
|
|
|
}
|
|
|
|
#endif
|
2012-04-02 19:54:40 +08:00
|
|
|
if (port->blocked_open) {
|
2012-04-02 19:54:42 +08:00
|
|
|
if (port->close_delay)
|
|
|
|
msleep_interruptible(jiffies_to_msecs(port->close_delay));
|
2012-04-02 19:54:41 +08:00
|
|
|
wake_up_interruptible(&port->open_wait);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-04-02 19:54:43 +08:00
|
|
|
port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
|
2012-04-02 19:54:41 +08:00
|
|
|
wake_up_interruptible(&port->close_wait);
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rs_hangup() --- called by tty_hangup() when a hangup is signaled.
|
|
|
|
*/
|
|
|
|
void rs_hangup(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_hangup"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
rs_flush_buffer(tty);
|
2012-04-02 19:54:44 +08:00
|
|
|
shutdown(info, tty);
|
2012-04-02 19:54:40 +08:00
|
|
|
info->tport.count = 0;
|
2012-04-02 19:54:43 +08:00
|
|
|
info->tport.flags &= ~ASYNC_NORMAL_ACTIVE;
|
2012-04-02 19:54:45 +08:00
|
|
|
tty_port_tty_set(&info->tport, NULL);
|
2012-04-02 19:54:41 +08:00
|
|
|
wake_up_interruptible(&info->tport.open_wait);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine is called whenever a serial port is opened. It
|
|
|
|
* enables interrupts for a serial port, linking in its S structure into
|
|
|
|
* the IRQ chain. It also performs the serial-specific
|
|
|
|
* initialization for the tty structure.
|
|
|
|
*/
|
|
|
|
int rs_open(struct tty_struct *tty, struct file * filp)
|
|
|
|
{
|
|
|
|
struct m68k_serial *info;
|
2012-03-05 21:52:01 +08:00
|
|
|
int retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-03-05 21:52:01 +08:00
|
|
|
info = &m68k_soft[tty->index];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (serial_paranoia_check(info, tty->name, "rs_open"))
|
|
|
|
return -ENODEV;
|
|
|
|
|
2012-04-02 19:54:40 +08:00
|
|
|
info->tport.count++;
|
2005-04-17 06:20:36 +08:00
|
|
|
tty->driver_data = info;
|
2012-04-02 19:54:45 +08:00
|
|
|
tty_port_tty_set(&info->tport, tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Start up serial port
|
|
|
|
*/
|
2012-04-02 19:54:44 +08:00
|
|
|
retval = startup(info, tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2012-04-02 19:54:46 +08:00
|
|
|
return tty_port_block_til_ready(&info->tport, tty, filp);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Finally, routines used to initialize the serial driver. */
|
|
|
|
|
|
|
|
static void show_serial_version(void)
|
|
|
|
{
|
|
|
|
printk("MC68328 serial driver version 1.00\n");
|
|
|
|
}
|
|
|
|
|
2006-10-02 17:17:18 +08:00
|
|
|
static const struct tty_operations rs_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = rs_open,
|
|
|
|
.close = rs_close,
|
|
|
|
.write = rs_write,
|
|
|
|
.flush_chars = rs_flush_chars,
|
|
|
|
.write_room = rs_write_room,
|
|
|
|
.chars_in_buffer = rs_chars_in_buffer,
|
|
|
|
.flush_buffer = rs_flush_buffer,
|
|
|
|
.ioctl = rs_ioctl,
|
|
|
|
.throttle = rs_throttle,
|
|
|
|
.unthrottle = rs_unthrottle,
|
|
|
|
.set_termios = rs_set_termios,
|
|
|
|
.stop = rs_stop,
|
|
|
|
.start = rs_start,
|
|
|
|
.hangup = rs_hangup,
|
|
|
|
.set_ldisc = rs_set_ldisc,
|
|
|
|
};
|
|
|
|
|
2012-04-02 19:54:46 +08:00
|
|
|
static const struct tty_port_operations rs_port_ops = {
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* rs_init inits the driver */
|
|
|
|
static int __init
|
|
|
|
rs68328_init(void)
|
|
|
|
{
|
2012-04-02 19:54:38 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int i;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct m68k_serial *info;
|
|
|
|
|
|
|
|
serial_driver = alloc_tty_driver(NR_PORTS);
|
|
|
|
if (!serial_driver)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
show_serial_version();
|
|
|
|
|
|
|
|
/* Initialize the tty_driver structure */
|
|
|
|
/* SPARC: Not all of this is exactly right for us. */
|
|
|
|
|
|
|
|
serial_driver->name = "ttyS";
|
|
|
|
serial_driver->major = TTY_MAJOR;
|
|
|
|
serial_driver->minor_start = 64;
|
|
|
|
serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
|
|
|
serial_driver->subtype = SERIAL_TYPE_NORMAL;
|
|
|
|
serial_driver->init_termios = tty_std_termios;
|
|
|
|
serial_driver->init_termios.c_cflag =
|
|
|
|
m68328_console_cbaud | CS8 | CREAD | HUPCL | CLOCAL;
|
|
|
|
serial_driver->flags = TTY_DRIVER_REAL_RAW;
|
|
|
|
tty_set_operations(serial_driver, &rs_ops);
|
|
|
|
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_save(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
for(i=0;i<NR_PORTS;i++) {
|
|
|
|
|
|
|
|
info = &m68k_soft[i];
|
2012-04-02 19:54:40 +08:00
|
|
|
tty_port_init(&info->tport);
|
2012-04-02 19:54:46 +08:00
|
|
|
info->tport.ops = &rs_port_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
info->magic = SERIAL_MAGIC;
|
|
|
|
info->port = (int) &uart_addr[i];
|
|
|
|
info->irq = uart_irqs[i];
|
|
|
|
info->custom_divisor = 16;
|
|
|
|
info->x_char = 0;
|
|
|
|
info->line = i;
|
|
|
|
info->is_cons = 1; /* Means shortcuts work */
|
|
|
|
|
|
|
|
printk("%s%d at 0x%08x (irq = %d)", serial_driver->name, info->line,
|
|
|
|
info->port, info->irq);
|
|
|
|
printk(" is a builtin MC68328 UART\n");
|
|
|
|
|
|
|
|
#ifdef CONFIG_M68VZ328
|
|
|
|
if (i > 0 )
|
|
|
|
PJSEL &= 0xCF; /* PSW enable second port output */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (request_irq(uart_irqs[i],
|
|
|
|
rs_interrupt,
|
2011-09-22 16:59:15 +08:00
|
|
|
0,
|
2008-08-20 11:49:40 +08:00
|
|
|
"M68328_UART", info))
|
2005-04-17 06:20:36 +08:00
|
|
|
panic("Unable to attach 68328 serial interrupt\n");
|
2012-08-08 03:47:51 +08:00
|
|
|
|
|
|
|
tty_port_link_device(&info->tport, serial_driver, i);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2006-06-28 13:54:22 +08:00
|
|
|
local_irq_restore(flags);
|
2012-08-08 03:47:51 +08:00
|
|
|
|
|
|
|
if (tty_register_driver(serial_driver)) {
|
|
|
|
put_tty_driver(serial_driver);
|
2012-11-15 16:49:56 +08:00
|
|
|
for (i = 0; i < NR_PORTS; i++)
|
|
|
|
tty_port_destroy(&m68k_soft[i].tport);
|
2012-08-08 03:47:51 +08:00
|
|
|
printk(KERN_ERR "Couldn't register serial driver\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(rs68328_init);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void m68328_set_baud(void)
|
|
|
|
{
|
|
|
|
unsigned short ustcnt;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ustcnt = USTCNT;
|
|
|
|
USTCNT = ustcnt & ~USTCNT_TXEN;
|
|
|
|
|
|
|
|
again:
|
2009-12-10 04:31:30 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(baud_table); i++)
|
2005-04-17 06:20:36 +08:00
|
|
|
if (baud_table[i] == m68328_console_baud)
|
|
|
|
break;
|
2009-12-10 04:31:30 +08:00
|
|
|
if (i >= ARRAY_SIZE(baud_table)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
m68328_console_baud = 9600;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
UBAUD = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) |
|
|
|
|
PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale);
|
|
|
|
ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7);
|
|
|
|
ustcnt |= USTCNT_8_7;
|
|
|
|
ustcnt |= USTCNT_TXEN;
|
|
|
|
USTCNT = ustcnt;
|
|
|
|
m68328_console_initted = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int m68328_console_setup(struct console *cp, char *arg)
|
|
|
|
{
|
|
|
|
int i, n = CONSOLE_BAUD_RATE;
|
|
|
|
|
|
|
|
if (!cp)
|
|
|
|
return(-1);
|
|
|
|
|
|
|
|
if (arg)
|
|
|
|
n = simple_strtoul(arg,NULL,0);
|
|
|
|
|
2009-12-10 04:31:30 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(baud_table); i++)
|
2005-04-17 06:20:36 +08:00
|
|
|
if (baud_table[i] == n)
|
|
|
|
break;
|
2010-05-25 05:32:55 +08:00
|
|
|
if (i < ARRAY_SIZE(baud_table)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
m68328_console_baud = n;
|
|
|
|
m68328_console_cbaud = 0;
|
|
|
|
if (i > 15) {
|
|
|
|
m68328_console_cbaud |= CBAUDEX;
|
|
|
|
i -= 15;
|
|
|
|
}
|
|
|
|
m68328_console_cbaud |= i;
|
|
|
|
}
|
|
|
|
|
|
|
|
m68328_set_baud(); /* make sure baud rate changes */
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct tty_driver *m68328_console_device(struct console *c, int *index)
|
|
|
|
{
|
|
|
|
*index = c->index;
|
|
|
|
return serial_driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void m68328_console_write (struct console *co, const char *str,
|
|
|
|
unsigned int count)
|
|
|
|
{
|
|
|
|
if (!m68328_console_initted)
|
|
|
|
m68328_set_baud();
|
|
|
|
while (count--) {
|
|
|
|
if (*str == '\n')
|
|
|
|
rs_put_char('\r');
|
|
|
|
rs_put_char( *str++ );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct console m68328_driver = {
|
|
|
|
.name = "ttyS",
|
|
|
|
.write = m68328_console_write,
|
|
|
|
.device = m68328_console_device,
|
|
|
|
.setup = m68328_console_setup,
|
|
|
|
.flags = CON_PRINTBUFFER,
|
|
|
|
.index = -1,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int __init m68328_console_init(void)
|
|
|
|
{
|
|
|
|
register_console(&m68328_driver);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
console_initcall(m68328_console_init);
|