File: [Development] / linux-2.4-xfs / drivers / sbus / char / sab82532.c (download)
Revision 1.4, Wed Dec 21 14:32:30 2005 UTC (11 years, 9 months ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
CVS Tags: HEAD Changes since 1.3: +0 -0
lines
Merge up to 2.4.32.
Merge of 2.4.x-xfs-melb:linux:24898a by kenmcd.
|
/* $Id: sab82532.c,v 1.65 2001/10/13 08:27:50 davem Exp $
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*
* Rewrote buffer handling to use CIRC(Circular Buffer) macros.
* Maxim Krasnyanskiy <maxk@qualcomm.com>
*
* Fixed to use tty_get_baud_rate, and to allow for arbitrary baud
* rates to be programmed into the UART. Also eliminated a lot of
* duplicated code in the console setup.
* Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#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/serial.h>
#include <linux/serialP.h>
#include <linux/serial_reg.h>
#include <linux/console.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/delay.h>
#include <asm/sab82532.h>
#include <asm/uaccess.h>
#include <asm/ebus.h>
#include <asm/irq.h>
#include "sunserial.h"
#if defined(CONFIG_KDB)
#include <linux/kdb.h>
/*
* NOTE: The kernel ignores characters on the serial line unless a user space
* program has opened the line first. To enter kdb before user space has opened
* the serial line, you can use the 'kdb=early' flag to lilo and set the
* appropriate breakpoints.
*/
static const char *kdb_serial_ptr = kdb_serial_str;
#endif /* CONFIG_KDB */
static DECLARE_TASK_QUEUE(tq_serial);
/* This is (one of many) a special gross hack to allow SU and
* SAB serials to co-exist on the same machine. -DaveM
*/
#undef SERIAL_BH
#define SERIAL_BH AURORA_BH
static struct tty_driver serial_driver, callout_driver;
static int sab82532_refcount;
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
#undef SERIAL_PARANOIA_CHECK
#define SERIAL_DO_RESTART
/* Set of debugging defines */
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
#undef SERIAL_DEBUG_MODEM
#undef SERIAL_DEBUG_WAIT_UNTIL_SENT
#undef SERIAL_DEBUG_SEND_BREAK
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_FIFO
#define SERIAL_DEBUG_OVERFLOW 1
/* Trace things on serial device, useful for console debugging: */
#undef SERIAL_LOG_DEVICE
#ifdef SERIAL_LOG_DEVICE
static void dprint_init(int tty);
#endif
static void change_speed(struct sab82532 *info);
static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout);
/*
* This assumes you have a 29.4912 MHz clock for your UART.
*/
#define BASE_BAUD ( 29491200 / 16 )
static struct sab82532 *sab82532_chain = 0;
static struct tty_struct *sab82532_table[NR_PORTS];
static struct termios *sab82532_termios[NR_PORTS];
static struct termios *sab82532_termios_locked[NR_PORTS];
#ifdef MODULE
#undef CONFIG_SERIAL_CONSOLE
#endif
#ifdef CONFIG_SERIAL_CONSOLE
extern int serial_console;
static struct console sab82532_console;
static int sab82532_console_init(void);
static void batten_down_hatches(struct sab82532 *info);
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
static char *sab82532_version[16] = {
"V1.0", "V2.0", "V3.2", "V(0x03)",
"V(0x04)", "V(0x05)", "V(0x06)", "V(0x07)",
"V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)",
"V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)"
};
static char serial_version[16];
/*
* tmp_buf is used as a temporary buffer by sab82532_write. We need to
* lock it in case the copy_from_user blocks while swapping in a page,
* and some other program tries to do a serial write at the same time.
* Since the lock will only come under contention when the system is
* swapping and available memory is low, it makes sense to share one
* buffer across all the serial ports, since it significantly saves
* memory if large numbers of serial ports are open.
*/
static unsigned char *tmp_buf = 0;
static DECLARE_MUTEX(tmp_buf_sem);
static inline int serial_paranoia_check(struct sab82532 *info,
kdev_t device, 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 sab82532 for (%s) in %s\n";
if (!info) {
printk(badinfo, kdevname(device), routine);
return 1;
}
if (info->magic != SERIAL_MAGIC) {
printk(badmagic, kdevname(device), routine);
return 1;
}
#endif
return 0;
}
/*
* This is used to figure out the divisor speeds.
*
* The formula is: Baud = BASE_BAUD / ((N + 1) * (1 << M)),
*
* with 0 <= N < 64 and 0 <= M < 16
*
* 12-Oct-2001 - Replaced table driven approach with code written by
* Theodore Ts'o <tytso@alum.mit.edu> which exactly replicates the
* table. (Modulo bugs for the 307200 and 61440 baud rates, which
* were clearly incorrectly calculated in the original table. This is
* why tables filled with magic constants are evil.)
*/
static void calc_ebrg(int baud, int *n_ret, int *m_ret)
{
int n, m;
if (baud == 0) {
*n_ret = 0;
*m_ret = 0;
return;
}
/*
* We scale numbers by 10 so that we get better accuracy
* without having to use floating point. Here we increment m
* until n is within the valid range.
*/
n = (BASE_BAUD*10) / baud;
m = 0;
while (n >= 640) {
n = n / 2;
m++;
}
n = (n+5) / 10;
/*
* We try very hard to avoid speeds with M == 0 since they may
* not work correctly for XTAL frequences above 10 MHz.
*/
if ((m == 0) && ((n & 1) == 0)) {
n = n / 2;
m++;
}
*n_ret = n - 1;
*m_ret = m;
}
#define SAB82532_MAX_TEC_TIMEOUT 200000 /* 1 character time (at 50 baud) */
#define SAB82532_MAX_CEC_TIMEOUT 50000 /* 2.5 TX CLKs (at 50 baud) */
static __inline__ void sab82532_tec_wait(struct sab82532 *info)
{
int timeout = info->tec_timeout;
while ((readb(&info->regs->r.star) & SAB82532_STAR_TEC) && --timeout)
udelay(1);
}
static __inline__ void sab82532_cec_wait(struct sab82532 *info)
{
int timeout = info->cec_timeout;
while ((readb(&info->regs->r.star) & SAB82532_STAR_CEC) && --timeout)
udelay(1);
}
static __inline__ void sab82532_start_tx(struct sab82532 *info)
{
unsigned long flags;
int i;
save_flags(flags); cli();
if (info->xmit.head == info->xmit.tail)
goto out;
if (!test_bit(SAB82532_XPR, &info->irqflags))
goto out;
info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
clear_bit(SAB82532_ALLS, &info->irqflags);
clear_bit(SAB82532_XPR, &info->irqflags);
for (i = 0; i < info->xmit_fifo_size; i++) {
writeb(info->xmit.buf[info->xmit.tail],
&info->regs->w.xfifo[i]);
info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
info->icount.tx++;
if (info->xmit.head == info->xmit.tail)
break;
}
/* Issue a Transmit Frame command. */
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
out:
restore_flags(flags);
}
/*
* ------------------------------------------------------------
* sab82532_stop() and sab82532_start()
*
* This routines are called before setting or resetting tty->stopped.
* They enable or disable transmitter interrupts, as necessary.
* ------------------------------------------------------------
*/
static void sab82532_stop(struct tty_struct *tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_stop"))
return;
save_flags(flags); cli();
info->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
restore_flags(flags);
}
static void sab82532_start(struct tty_struct *tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_start"))
return;
save_flags(flags); cli();
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
sab82532_start_tx(info);
restore_flags(flags);
}
/*
* ----------------------------------------------------------------------
*
* Here starts the interrupt handling routines. All of the following
* subroutines are declared as inline and are folded into
* sab82532_interrupt(). They were separated out for readability's sake.
*
* Note: sab82532_interrupt() is a "fast" interrupt, which means that it
* runs with interrupts turned off. People who may want to modify
* sab82532_interrupt() should try to keep the interrupt handler as fast as
* possible. After you are done making modifications, it is not a bad
* idea to do:
*
* gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
*
* and look at the resulting assemble code in serial.s.
*
* - Ted Ts'o (tytso@mit.edu), 7-Mar-93
* -----------------------------------------------------------------------
*/
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
static void sab82532_sched_event(struct sab82532 *info, int event)
{
info->event |= 1 << event;
queue_task(&info->tqueue, &tq_serial);
mark_bh(SERIAL_BH);
}
static void receive_chars(struct sab82532 *info,
union sab82532_irq_status *stat,
struct pt_regs *regs)
{
#if defined(CONFIG_KDB)
int need_kdb = 0;
#endif
struct tty_struct *tty = info->tty;
unsigned char buf[32];
unsigned char status;
int free_fifo = 0;
int i, count = 0;
/* Read number of BYTES (Character + Status) available. */
if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
count = info->recv_fifo_size;
free_fifo++;
}
if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
count = readb(&info->regs->r.rbcl) & (info->recv_fifo_size - 1);
free_fifo++;
}
/* Issue a FIFO read command in case we where idle. */
if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RFRD, &info->regs->w.cmdr);
return;
}
if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
#ifdef SERIAL_DEBUG_OVERFLOW
printk("sab82532: receive_chars: RFO");
#endif
free_fifo++;
}
/* Read the FIFO. */
for (i = 0; i < count; i++)
buf[i] = readb(&info->regs->r.rfifo[i]);
/* Issue Receive Message Complete command. */
if (free_fifo) {
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RMC, &info->regs->w.cmdr);
}
if (!tty)
return;
for (i = 0; i < count; ) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
#ifdef SERIAL_DEBUG_OVERFLOW
printk("sab82532: receive_chars: tty overrun\n");
#endif
info->icount.buf_overrun++;
break;
}
#if defined(CONFIG_KDB)
if (info->is_console && kdb_on) {
if (buf[i] == *kdb_serial_ptr) {
if (!(*++kdb_serial_ptr)) {
need_kdb = 1;
kdb_serial_ptr = kdb_serial_str;
break;
}
} else
kdb_serial_ptr = kdb_serial_str;
}
#endif /* CONFIG_KDB */
tty->flip.count++;
*tty->flip.char_buf_ptr++ = buf[i++];
status = buf[i++];
info->icount.rx++;
#ifdef SERIAL_DEBUG_INTR
printk("DR%02x:%02x...", (unsigned char)*(tty->flip.char_buf_ptr - 1), status);
#endif
if (status & SAB82532_RSTAT_PE) {
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
info->icount.parity++;
} else if (status & SAB82532_RSTAT_FE) {
*tty->flip.flag_buf_ptr++ = TTY_FRAME;
info->icount.frame++;
}
else
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
}
#ifdef CONFIG_KDB
if (need_kdb)
kdb(KDB_REASON_KEYBOARD, 0, regs);
#endif
queue_task(&tty->flip.tqueue, &tq_timer);
}
static void transmit_chars(struct sab82532 *info,
union sab82532_irq_status *stat)
{
int i;
if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
info->interrupt_mask1 |= SAB82532_IMR1_ALLS;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
set_bit(SAB82532_ALLS, &info->irqflags);
}
if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR))
return;
if (!(readb(&info->regs->r.star) & SAB82532_STAR_XFW)) {
#ifdef SERIAL_DEBUG_FIFO
printk("%s: XPR, but no XFW (?)\n", __FUNCTION__);
#endif
return;
}
set_bit(SAB82532_XPR, &info->irqflags);
if (!info->tty) {
info->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
return;
}
if ((info->xmit.head == info->xmit.tail) ||
info->tty->stopped || info->tty->hw_stopped) {
info->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
return;
}
info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
clear_bit(SAB82532_ALLS, &info->irqflags);
/* Stuff 32 bytes into Transmit FIFO. */
clear_bit(SAB82532_XPR, &info->irqflags);
for (i = 0; i < info->xmit_fifo_size; i++) {
writeb(info->xmit.buf[info->xmit.tail],
&info->regs->w.xfifo[i]);
info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
info->icount.tx++;
if (info->xmit.head == info->xmit.tail)
break;
}
/* Issue a Transmit Frame command. */
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP);
#ifdef SERIAL_DEBUG_INTR
printk("THRE...");
#endif
}
static void check_status(struct sab82532 *info,
union sab82532_irq_status *stat)
{
struct tty_struct *tty = info->tty;
int modem_change = 0;
if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
#ifdef CONFIG_SERIAL_CONSOLE
if (info->is_console) {
batten_down_hatches(info);
return;
}
#endif
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
info->icount.buf_overrun++;
goto check_modem;
}
tty->flip.count++;
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
*tty->flip.char_buf_ptr++ = 0;
info->icount.brk++;
}
if (!tty)
return;
if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
info->icount.buf_overrun++;
goto check_modem;
}
tty->flip.count++;
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
*tty->flip.char_buf_ptr++ = 0;
info->icount.overrun++;
}
check_modem:
if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) {
info->dcd = (readb(&info->regs->r.vstr) & SAB82532_VSTR_CD) ? 0 : 1;
info->icount.dcd++;
modem_change++;
#ifdef SERIAL_DEBUG_MODEM
printk("DCD change: %d\n", info->icount.dcd);
#endif
}
if (stat->sreg.isr1 & SAB82532_ISR1_CSC) {
info->cts = readb(&info->regs->r.star) & SAB82532_STAR_CTS;
info->icount.cts++;
modem_change++;
#ifdef SERIAL_DEBUG_MODEM
printk("CTS change: %d, CTS %s\n", info->icount.cts, info->cts ? "on" : "off");
#endif
}
if ((readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ^ info->dsr) {
info->dsr = (readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ? 0 : 1;
info->icount.dsr++;
modem_change++;
#ifdef SERIAL_DEBUG_MODEM
printk("DSR change: %d\n", info->icount.dsr);
#endif
}
if (modem_change)
wake_up_interruptible(&info->delta_msr_wait);
if ((info->flags & ASYNC_CHECK_CD) &&
(stat->sreg.isr0 & SAB82532_ISR0_CDSC)) {
#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
printk("ttys%d CD now %s...", info->line,
(info->dcd) ? "on" : "off");
#endif
if (info->dcd)
wake_up_interruptible(&info->open_wait);
else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_CALLOUT_NOHUP))) {
#ifdef SERIAL_DEBUG_OPEN
printk("scheduling hangup...");
#endif
MOD_INC_USE_COUNT;
if (schedule_task(&info->tqueue_hangup) == 0)
MOD_DEC_USE_COUNT;
}
}
if (info->flags & ASYNC_CTS_FLOW) {
if (info->tty->hw_stopped) {
if (info->cts) {
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
printk("CTS tx start...");
#endif
info->tty->hw_stopped = 0;
sab82532_sched_event(info,
RS_EVENT_WRITE_WAKEUP);
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
sab82532_start_tx(info);
}
} else {
if (!(info->cts)) {
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
printk("CTS tx stop...");
#endif
info->tty->hw_stopped = 1;
}
}
}
}
/*
* This is the serial driver's generic interrupt routine
*/
static void sab82532_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct sab82532 *info = dev_id;
union sab82532_irq_status status;
#ifdef SERIAL_DEBUG_INTR
printk("sab82532_interrupt(%d)...", irq);
#endif
status.stat = 0;
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISA0)
status.sreg.isr0 = readb(&info->regs->r.isr0);
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISA1)
status.sreg.isr1 = readb(&info->regs->r.isr1);
#ifdef SERIAL_DEBUG_INTR
printk("%d<%02x.%02x>", info->line,
status.sreg.isr0, status.sreg.isr1);
#endif
if (!status.stat)
goto next;
if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
receive_chars(info, &status, regs);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
check_status(info, &status);
if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
transmit_chars(info, &status);
next:
info = info->next;
status.stat = 0;
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISB0)
status.sreg.isr0 = readb(&info->regs->r.isr0);
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISB1)
status.sreg.isr1 = readb(&info->regs->r.isr1);
#ifdef SERIAL_DEBUG_INTR
printk("%d<%02x.%02x>", info->line,
status.sreg.isr0, status.sreg.isr1);
#endif
if (!status.stat)
goto done;
if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
receive_chars(info, &status, regs);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
check_status(info, &status);
if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
transmit_chars(info, &status);
done:
;
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
* -------------------------------------------------------------------
*/
/*
* This routine is used to handle the "bottom half" processing for the
* serial driver, known also the "software interrupt" processing.
* This processing is done at the kernel interrupt level, after the
* sab82532_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
* is where time-consuming activities which can not be done in the
* interrupt driver proper are done; the interrupt driver schedules
* them using sab82532_sched_event(), and they get done here.
*/
static void do_serial_bh(void)
{
run_task_queue(&tq_serial);
}
static void do_softint(void *private_)
{
struct sab82532 *info = (struct sab82532 *)private_;
struct tty_struct *tty;
tty = info->tty;
if (!tty)
return;
if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
tty_wakeup(tty);
}
}
/*
* This routine is called from the scheduler tqueue when the interrupt
* routine has signalled that a hangup has occurred. The path of
* hangup processing is:
*
* serial interrupt routine -> (scheduler tqueue) ->
* do_serial_hangup() -> tty->hangup() -> sab82532_hangup()
*
*/
static void do_serial_hangup(void *private_)
{
struct sab82532 *info = (struct sab82532 *) private_;
struct tty_struct *tty;
tty = info->tty;
if (tty)
tty_hangup(tty);
MOD_DEC_USE_COUNT;
}
static void
sab82532_init_line(struct sab82532 *info)
{
unsigned char stat, tmp;
/*
* Wait for any commands or immediate characters
*/
sab82532_cec_wait(info);
sab82532_tec_wait(info);
/*
* Clear the FIFO buffers.
*/
writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr);
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XRES, &info->regs->w.cmdr);
/*
* Clear the interrupt registers.
*/
stat = readb(&info->regs->r.isr0);
stat = readb(&info->regs->r.isr1);
/*
* Now, initialize the UART
*/
writeb(0, &info->regs->w.ccr0); /* power-down */
writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |
SAB82532_CCR0_SM_ASYNC, &info->regs->w.ccr0);
writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &info->regs->w.ccr1);
writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |
SAB82532_CCR2_TOE, &info->regs->w.ccr2);
writeb(0, &info->regs->w.ccr3);
writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &info->regs->w.ccr4);
writeb(SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
SAB82532_MODE_RAC, &info->regs->w.mode);
writeb(SAB82532_RFC_DPS | SAB82532_RFC_RFDF, &info->regs->w.rfc);
switch (info->recv_fifo_size) {
case 1:
tmp = readb(&info->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_1;
writeb(tmp, &info->regs->w.rfc);
break;
case 4:
tmp = readb(&info->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_4;
writeb(tmp, &info->regs->w.rfc);
break;
case 16:
tmp = readb(&info->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_16;
writeb(tmp, &info->regs->w.rfc);
break;
default:
info->recv_fifo_size = 32;
/* fall through */
case 32:
tmp = readb(&info->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_32;
writeb(tmp, &info->regs->w.rfc);
break;
}
tmp = readb(&info->regs->rw.ccr0);
tmp |= SAB82532_CCR0_PU; /* power-up */
writeb(tmp, &info->regs->rw.ccr0);
}
static int startup(struct sab82532 *info)
{
unsigned long flags;
unsigned long page;
int retval = 0;
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
save_flags(flags); cli();
if (info->flags & ASYNC_INITIALIZED) {
free_page(page);
goto errout;
}
if (!info->regs) {
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
free_page(page);
retval = -ENODEV;
goto errout;
}
if (info->xmit.buf)
free_page(page);
else
info->xmit.buf = (unsigned char *)page;
#ifdef SERIAL_DEBUG_OPEN
printk("starting up serial port %d...", info->line);
#endif
/*
* Initialize the Hardware
*/
sab82532_init_line(info);
if (info->tty->termios->c_cflag & CBAUD) {
u8 tmp;
tmp = readb(&info->regs->rw.mode);
tmp &= ~(SAB82532_MODE_FRTS);
tmp |= SAB82532_MODE_RTS;
writeb(tmp, &info->regs->rw.mode);
tmp = readb(&info->regs->rw.pvr);
tmp &= ~(info->pvr_dtr_bit);
writeb(tmp, &info->regs->rw.pvr);
}
/*
* Finally, enable interrupts
*/
info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
set_bit(SAB82532_ALLS, &info->irqflags);
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit.head = info->xmit.tail = 0;
set_bit(SAB82532_XPR, &info->irqflags);
/*
* and set the speed of the serial port
*/
change_speed(info);
info->flags |= ASYNC_INITIALIZED;
restore_flags(flags);
return 0;
errout:
restore_flags(flags);
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.
*/
static void shutdown(struct sab82532 *info)
{
unsigned long flags;
u8 tmp;
if (!(info->flags & ASYNC_INITIALIZED))
return;
#ifdef SERIAL_DEBUG_OPEN
printk("Shutting down serial port %d...", info->line);
#endif
save_flags(flags); cli(); /* Disable interrupts */
/*
* clear delta_msr_wait queue to avoid mem leaks: we may free the irq
* here so the queue might never be waken up
*/
wake_up_interruptible(&info->delta_msr_wait);
if (info->xmit.buf) {
free_page((unsigned long)info->xmit.buf);
info->xmit.buf = 0;
}
#ifdef CONFIG_SERIAL_CONSOLE
if (info->is_console) {
info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
return;
}
#endif
/* Disable Interrupts */
info->interrupt_mask0 = 0xff;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
info->interrupt_mask1 = 0xff;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
tmp = readb(&info->regs->r.mode);
tmp |= (SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
writeb(tmp, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit,
&info->regs->rw.pvr);
}
/* Disable break condition */
tmp = readb(&info->regs->rw.dafo);
tmp &= ~(SAB82532_DAFO_XBRK);
writeb(tmp, &info->regs->rw.dafo);
/* Disable Receiver */
tmp = readb(&info->regs->rw.mode);
tmp &= ~(SAB82532_MODE_RAC);
writeb(tmp, &info->regs->rw.mode);
/* Power Down */
tmp = readb(&info->regs->rw.ccr0);
tmp &= ~(SAB82532_CCR0_PU);
writeb(tmp, &info->regs->rw.ccr0);
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
}
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
*/
static void change_speed(struct sab82532 *info)
{
unsigned long flags;
unsigned int ebrg;
tcflag_t cflag;
unsigned char dafo;
int bits, n, m;
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
/* Byte size and parity */
switch (cflag & CSIZE) {
case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break;
case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break;
case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break;
/* Never happens, but GCC is too dumb to figure it out */
default: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
}
if (cflag & CSTOPB) {
dafo |= SAB82532_DAFO_STOP;
bits++;
}
if (cflag & PARENB) {
dafo |= SAB82532_DAFO_PARE;
bits++;
}
if (cflag & PARODD) {
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_MARK;
else
#endif
dafo |= SAB82532_DAFO_PAR_ODD;
} else {
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_SPACE;
else
#endif
dafo |= SAB82532_DAFO_PAR_EVEN;
}
/* Determine EBRG values based on baud rate */
info->baud = tty_get_baud_rate(info->tty);
calc_ebrg(info->baud, &n, &m);
ebrg = n | (m << 6);
if (info->baud) {
info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud;
info->tec_timeout = (10 * 1000000) / info->baud;
info->cec_timeout = info->tec_timeout >> 2;
} else {
info->timeout = 0;
info->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
info->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
}
info->timeout += HZ / 50; /* Add .02 seconds of slop */
/* CTS flow control flags */
if (cflag & CRTSCTS)
info->flags |= ASYNC_CTS_FLOW;
else
info->flags &= ~(ASYNC_CTS_FLOW);
if (cflag & CLOCAL)
info->flags &= ~(ASYNC_CHECK_CD);
else
info->flags |= ASYNC_CHECK_CD;
if (info->tty)
info->tty->hw_stopped = 0;
/*
* Set up parity check flag
* XXX: not implemented, yet.
*/
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
/*
* Characters to ignore
* XXX: not implemented, yet.
*/
/*
* !!! ignore all characters if CREAD is not set
* XXX: not implemented, yet.
*/
if ((cflag & CREAD) == 0)
info->ignore_status_mask |= SAB82532_ISR0_RPF |
SAB82532_ISR0_TCD |
SAB82532_ISR0_TIME;
save_flags(flags); cli();
sab82532_cec_wait(info);
sab82532_tec_wait(info);
writeb(dafo, &info->regs->w.dafo);
writeb(ebrg & 0xff, &info->regs->w.bgr);
writeb(readb(&info->regs->rw.ccr2) & ~(0xc0), &info->regs->rw.ccr2);
writeb(readb(&info->regs->rw.ccr2) | ((ebrg >> 2) & 0xc0), &info->regs->rw.ccr2);
if (info->flags & ASYNC_CTS_FLOW) {
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_RTS), &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FCTS), &info->regs->rw.mode);
info->interrupt_mask1 &= ~(SAB82532_IMR1_CSC);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
} else {
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FCTS, &info->regs->rw.mode);
info->interrupt_mask1 |= SAB82532_IMR1_CSC;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
}
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RAC, &info->regs->rw.mode);
restore_flags(flags);
}
static void sab82532_put_char(struct tty_struct *tty, unsigned char ch)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_put_char"))
return;
if (!tty || !info->xmit.buf)
return;
save_flags(flags); cli();
if (!CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)) {
restore_flags(flags);
return;
}
info->xmit.buf[info->xmit.head] = ch;
info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
restore_flags(flags);
}
static void sab82532_flush_chars(struct tty_struct *tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_flush_chars"))
return;
if ((info->xmit.head == info->xmit.tail) ||
tty->stopped || tty->hw_stopped || !info->xmit.buf)
return;
save_flags(flags); cli();
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
sab82532_start_tx(info);
restore_flags(flags);
}
static int sab82532_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
int c, ret = 0;
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_write"))
return 0;
if (!tty || !info->xmit.buf || !tmp_buf)
return 0;
save_flags(flags);
if (from_user) {
down(&tmp_buf_sem);
while (1) {
int c1;
c = CIRC_SPACE_TO_END(info->xmit.head,
info->xmit.tail,
SERIAL_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
c -= copy_from_user(tmp_buf, buf, c);
if (!c) {
if (!ret)
ret = -EFAULT;
break;
}
cli();
c1 = CIRC_SPACE_TO_END(info->xmit.head,
info->xmit.tail,
SERIAL_XMIT_SIZE);
if (c1 < c)
c = c1;
memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
info->xmit.head = (info->xmit.head + c) & (SERIAL_XMIT_SIZE-1);
restore_flags(flags);
buf += c;
count -= c;
ret += c;
}
up(&tmp_buf_sem);
} else {
cli();
while (1) {
c = CIRC_SPACE_TO_END(info->xmit.head,
info->xmit.tail,
SERIAL_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
memcpy(info->xmit.buf + info->xmit.head, buf, c);
info->xmit.head = (info->xmit.head + c) & (SERIAL_XMIT_SIZE-1);
buf += c;
count -= c;
ret += c;
}
restore_flags(flags);
}
if ((info->xmit.head != info->xmit.tail) &&
!tty->stopped && !tty->hw_stopped) {
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
sab82532_start_tx(info);
}
restore_flags(flags);
return ret;
}
static int sab82532_write_room(struct tty_struct *tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "sab82532_write_room"))
return 0;
return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
}
static int sab82532_chars_in_buffer(struct tty_struct *tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "sab82532_chars_in_buffer"))
return 0;
return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
}
static void sab82532_flush_buffer(struct tty_struct *tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer"))
return;
save_flags(flags); cli();
info->xmit.head = info->xmit.tail = 0;
restore_flags(flags);
tty_wakeup(tty);
}
/*
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
static void sab82532_send_xchar(struct tty_struct *tty, char ch)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar"))
return;
save_flags(flags); cli();
sab82532_tec_wait(info);
writeb(ch, &info->regs->w.tic);
restore_flags(flags);
}
/*
* ------------------------------------------------------------
* sab82532_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
* incoming characters should be throttled.
* ------------------------------------------------------------
*/
static void sab82532_throttle(struct tty_struct * tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("throttle %s: %d....\n", _tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
if (serial_paranoia_check(info, tty->device, "sab82532_throttle"))
return;
if (I_IXOFF(tty))
sab82532_send_xchar(tty, STOP_CHAR(tty));
if (tty->termios->c_cflag & CRTSCTS) {
u8 mode = readb(&info->regs->r.mode);
mode &= ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
writeb(mode, &info->regs->w.mode);
}
}
static void sab82532_unthrottle(struct tty_struct * tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
if (serial_paranoia_check(info, tty->device, "sab82532_unthrottle"))
return;
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
else
sab82532_send_xchar(tty, START_CHAR(tty));
}
if (tty->termios->c_cflag & CRTSCTS) {
u8 mode = readb(&info->regs->r.mode);
mode &= ~(SAB82532_MODE_RTS);
mode |= SAB82532_MODE_FRTS;
writeb(mode, &info->regs->w.mode);
}
}
/*
* ------------------------------------------------------------
* sab82532_ioctl() and friends
* ------------------------------------------------------------
*/
static int get_serial_info(struct sab82532 *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 = (unsigned long)info->regs;
tmp.irq = info->irq;
tmp.flags = info->flags;
tmp.xmit_fifo_size = info->xmit_fifo_size;
tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
tmp.closing_wait = info->closing_wait;
tmp.custom_divisor = info->custom_divisor;
tmp.hub6 = 0;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
static int set_serial_info(struct sab82532 *info,
struct serial_struct *new_info)
{
return 0;
}
/*
* 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 sab82532 * info, unsigned int *value)
{
unsigned int result;
result = (!info->xmit.buf && test_bit(SAB82532_ALLS, &info->irqflags))
? TIOCSER_TEMT : 0;
return put_user(result, value);
}
static int get_modem_info(struct sab82532 * info, unsigned int *value)
{
unsigned int result;
result = ((readb(&info->regs->r.mode) & SAB82532_MODE_RTS) ?
((readb(&info->regs->r.mode) & SAB82532_MODE_FRTS) ? 0 : TIOCM_RTS)
: TIOCM_RTS)
| ((readb(&info->regs->r.pvr) & info->pvr_dtr_bit) ? 0 : TIOCM_DTR)
| ((readb(&info->regs->r.vstr) & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR)
| ((readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ? 0 : TIOCM_DSR)
| ((readb(&info->regs->r.star) & SAB82532_STAR_CTS) ? TIOCM_CTS : 0);
return put_user(result,value);
}
static int set_modem_info(struct sab82532 * info, unsigned int cmd,
unsigned int *value)
{
unsigned int arg;
if (get_user(arg, value))
return -EFAULT;
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS) {
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
}
if (arg & TIOCM_DTR) {
writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
}
break;
case TIOCMBIC:
if (arg & TIOCM_RTS) {
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
}
if (arg & TIOCM_DTR) {
writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit, &info->regs->rw.pvr);
}
break;
case TIOCMSET:
if (arg & TIOCM_RTS) {
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
} else {
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
}
if (arg & TIOCM_DTR) {
writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
} else {
writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit, &info->regs->rw.pvr);
}
break;
default:
return -EINVAL;
}
return 0;
}
/*
* This routine sends a break character out the serial port.
*/
static void sab82532_break(struct tty_struct *tty, int break_state)
{
struct sab82532 * info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_break"))
return;
if (!info->regs)
return;
#ifdef SERIAL_DEBUG_SEND_BREAK
printk("sab82532_break(%d) jiff=%lu...", break_state, jiffies);
#endif
save_flags(flags); cli();
if (break_state == -1)
writeb(readb(&info->regs->rw.dafo) | SAB82532_DAFO_XBRK, &info->regs->rw.dafo);
else
writeb(readb(&info->regs->rw.dafo) & ~(SAB82532_DAFO_XBRK), &info->regs->rw.dafo);
restore_flags(flags);
}
static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct sab82532 * info = (struct sab82532 *)tty->driver_data;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
if (serial_paranoia_check(info, tty->device, "sab82532_ioctl"))
return -ENODEV;
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
(cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
switch (cmd) {
case TIOCGSOFTCAR:
return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
case TIOCSSOFTCAR:
if (get_user(arg, (unsigned int *) arg))
return -EFAULT;
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
return 0;
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
return set_modem_info(info, cmd, (unsigned int *) arg);
case TIOCGSERIAL:
return get_serial_info(info,
(struct serial_struct *) arg);
case TIOCSSERIAL:
return set_serial_info(info,
(struct serial_struct *) arg);
case TIOCSERGETLSR: /* Get line status register */
return get_lsr_info(info, (unsigned int *) arg);
case TIOCSERGSTRUCT:
if (copy_to_user((struct sab82532 *) arg,
info, sizeof(struct sab82532)))
return -EFAULT;
return 0;
/*
* 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:
cli();
/* note the counters on entry */
cprev = info->icount;
sti();
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
if (signal_pending(current))
return -ERESTARTSYS;
cli();
cnow = info->icount; /* atomic copy */
sti();
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; /* no change => error */
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)) ) {
return 0;
}
cprev = cnow;
}
/* NOTREACHED */
/*
* 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:
cli();
cnow = info->icount;
sti();
p_cuser = (struct serial_icounter_struct *) arg;
if (put_user(cnow.cts, &p_cuser->cts) ||
put_user(cnow.dsr, &p_cuser->dsr) ||
put_user(cnow.rng, &p_cuser->rng) ||
put_user(cnow.dcd, &p_cuser->dcd))
return -EFAULT;
return 0;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static void sab82532_set_termios(struct tty_struct *tty,
struct termios *old_termios)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
if ( (tty->termios->c_cflag == old_termios->c_cflag)
&& ( RELEVANT_IFLAG(tty->termios->c_iflag)
== RELEVANT_IFLAG(old_termios->c_iflag)))
return;
change_speed(info);
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) &&
!(tty->termios->c_cflag & CBAUD)) {
writeb(readb(&info->regs->w.mode) | SAB82532_MODE_FRTS, &info->regs->w.mode);
writeb(readb(&info->regs->w.mode) | SAB82532_MODE_RTS, &info->regs->w.mode);
writeb(readb(&info->regs->w.pvr) | info->pvr_dtr_bit, &info->regs->w.pvr);
}
/* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) &&
(tty->termios->c_cflag & CBAUD)) {
writeb(readb(&info->regs->w.pvr) & ~(info->pvr_dtr_bit), &info->regs->w.pvr);
if (tty->termios->c_cflag & CRTSCTS) {
writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_RTS), &info->regs->w.mode);
writeb(readb(&info->regs->w.mode) | SAB82532_MODE_FRTS, &info->regs->w.mode);
} else if (test_bit(TTY_THROTTLED, &tty->flags)) {
writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS), &info->regs->w.mode);
} else {
writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_FRTS), &info->regs->w.mode);
writeb(readb(&info->regs->w.mode) | SAB82532_MODE_RTS, &info->regs->w.mode);
}
}
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
sab82532_start(tty);
}
}
/*
* ------------------------------------------------------------
* sab82532_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
* async structure from the interrupt chain if necessary, and we free
* that IRQ if nothing is left in the chain.
* ------------------------------------------------------------
*/
static void sab82532_close(struct tty_struct *tty, struct file * filp)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (!info || serial_paranoia_check(info, tty->device, "sab82532_close"))
return;
save_flags(flags); cli();
if (tty_hung_up_p(filp)) {
MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_close ttys%d, count = %d\n", info->line, info->count);
#endif
if ((tty->count == 1) && (info->count != 1)) {
/*
* 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("sab82532_close: bad serial port count; tty->count is 1,"
" info->count is %d\n", info->count);
info->count = 1;
}
if (--info->count < 0) {
printk("sab82532_close: bad serial port count for ttys%d: %d\n",
info->line, info->count);
info->count = 0;
}
if (info->count) {
MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
info->flags |= ASYNC_CLOSING;
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
if (info->flags & ASYNC_NORMAL_ACTIVE)
info->normal_termios = *tty->termios;
if (info->flags & ASYNC_CALLOUT_ACTIVE)
info->callout_termios = *tty->termios;
/*
* 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->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, info->closing_wait);
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and turn off
* the receiver.
*/
info->interrupt_mask0 |= SAB82532_IMR0_TCD;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
if (info->flags & ASYNC_INITIALIZED) {
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
sab82532_wait_until_sent(tty, info->timeout);
}
shutdown(info);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
tty_ldisc_flush(tty);
tty->closing = 0;
info->event = 0;
info->tty = 0;
if (info->blocked_open) {
if (info->close_delay) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(info->close_delay);
}
wake_up_interruptible(&info->open_wait);
}
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
ASYNC_CLOSING);
wake_up_interruptible(&info->close_wait);
MOD_DEC_USE_COUNT;
restore_flags(flags);
}
/*
* sab82532_wait_until_sent() --- wait until the transmitter is empty
*/
static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long orig_jiffies, char_time;
if (serial_paranoia_check(info,tty->device,"sab82532_wait_until_sent"))
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 = (info->timeout - HZ/50) / info->xmit_fifo_size;
char_time = char_time / 5;
if (char_time == 0)
char_time = 1;
if (timeout)
char_time = MIN(char_time, timeout);
#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
printk("In sab82532_wait_until_sent(%d) check=%lu "
"xmit_cnt = %ld, alls = %d (jiff=%lu)...\n",
timeout, char_time,
CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
test_bit(SAB82532_ALLS, &info->irqflags), jiffies);
#endif
orig_jiffies = jiffies;
while ((info->xmit.head != info->xmit.tail) ||
!test_bit(SAB82532_ALLS, &info->irqflags)) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(char_time);
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
}
#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
printk("xmit_cnt = %ld, alls = %d (jiff=%lu)...done\n",
CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
test_bit(SAB82532_ALLS, &info->irqflags), jiffies);
#endif
}
/*
* sab82532_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
static void sab82532_hangup(struct tty_struct *tty)
{
struct sab82532 * info = (struct sab82532 *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "sab82532_hangup"))
return;
#ifdef CONFIG_SERIAL_CONSOLE
if (info->is_console)
return;
#endif
sab82532_flush_buffer(tty);
shutdown(info);
info->event = 0;
info->count = 0;
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
info->tty = 0;
wake_up_interruptible(&info->open_wait);
}
/*
* ------------------------------------------------------------
* sab82532_open() and friends
* ------------------------------------------------------------
*/
static int block_til_ready(struct tty_struct *tty, struct file * filp,
struct sab82532 *info)
{
DECLARE_WAITQUEUE(wait, current);
int retval;
int do_clocal = 0;
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
#ifdef SERIAL_DO_RESTART
if (info->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
#else
return -EAGAIN;
#endif
}
/*
* If this is a callout device, then just make sure the normal
* device isn't being used.
*/
if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
if (info->flags & ASYNC_NORMAL_ACTIVE)
return -EBUSY;
if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_SESSION_LOCKOUT) &&
(info->session != current->session))
return -EBUSY;
if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_PGRP_LOCKOUT) &&
(info->pgrp != current->pgrp))
return -EBUSY;
info->flags |= ASYNC_CALLOUT_ACTIVE;
return 0;
}
/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
if (info->flags & ASYNC_CALLOUT_ACTIVE)
return -EBUSY;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
if (info->flags & ASYNC_CALLOUT_ACTIVE) {
if (info->normal_termios.c_cflag & CLOCAL)
do_clocal = 1;
} else {
if (tty->termios->c_cflag & CLOCAL)
do_clocal = 1;
}
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, info->count is dropped by one, so that
* sab82532_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&info->open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready before block: ttyS%d, count = %d\n",
info->line, info->count);
#endif
cli();
if (!tty_hung_up_p(filp))
info->count--;
sti();
info->blocked_open++;
while (1) {
cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
(tty->termios->c_cflag & CBAUD)) {
writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_RTS), &info->regs->rw.mode);
}
sti();
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(info->flags & ASYNC_INITIALIZED)) {
#ifdef SERIAL_DO_RESTART
if (info->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
#else
retval = -EAGAIN;
#endif
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
!(info->flags & ASYNC_CLOSING) &&
(do_clocal || !(readb(&info->regs->r.vstr) & SAB82532_VSTR_CD)))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x\n",
info->line, info->count, info->flags, do_clocal, readb(&info->regs->r.vstr));
#endif
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
if (!tty_hung_up_p(filp))
info->count++;
info->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: ttys%d, count = %d\n",
info->line, info->count);
#endif
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
/*
* This routine is called whenever a serial port is opened. It
* enables interrupts for a serial port, linking in its async structure into
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
static int sab82532_open(struct tty_struct *tty, struct file * filp)
{
struct sab82532 *info = sab82532_chain;
int retval, line;
unsigned long page;
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_open: count = %d\n", info->count);
#endif
line = MINOR(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PORTS))
return -ENODEV;
while (info) {
if (info->line == line)
break;
info = info->next;
}
if (!info) {
printk("sab82532_open: can't find info for line %d\n",
line);
return -ENODEV;
}
if (serial_paranoia_check(info, tty->device, "sab82532_open"))
return -ENODEV;
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_open %s%d, count = %d\n", tty->driver.name, info->line,
info->count);
#endif
if (!tmp_buf) {
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
if (tmp_buf)
free_page(page);
else
tmp_buf = (unsigned char *) page;
}
info->count++;
tty->driver_data = info;
info->tty = tty;
/*
* If the port is in the middle of closing, bail out now.
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
#ifdef SERIAL_DO_RESTART
return ((info->flags & ASYNC_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS);
#else
return -EAGAIN;
#endif
}
/*
* Start up serial port
*/
retval = startup(info);
if (retval)
return retval;
MOD_INC_USE_COUNT;
retval = block_til_ready(tty, filp, info);
if (retval) {
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_open returning after block_til_ready with %d\n",
retval);
#endif
return retval;
}
if ((info->count == 1) &&
(info->flags & ASYNC_SPLIT_TERMIOS)) {
if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
*tty->termios = info->normal_termios;
else
*tty->termios = info->callout_termios;
change_speed(info);
}
#ifdef CONFIG_SERIAL_CONSOLE
if (sab82532_console.cflag && sab82532_console.index == line) {
tty->termios->c_cflag = sab82532_console.cflag;
sab82532_console.cflag = 0;
change_speed(info);
}
#endif
info->session = current->session;
info->pgrp = current->pgrp;
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_open ttys%d successful... count %d", info->line, info->count);
#endif
return 0;
}
/*
* /proc fs routines....
*/
static __inline__ int
line_info(char *buf, struct sab82532 *info)
{
unsigned long flags;
char stat_buf[30];
int ret;
ret = sprintf(buf, "%u: uart:SAB82532 ", info->line);
switch (info->type) {
case 0:
ret += sprintf(buf+ret, "V1.0 ");
break;
case 1:
ret += sprintf(buf+ret, "V2.0 ");
break;
case 2:
ret += sprintf(buf+ret, "V3.2 ");
break;
default:
ret += sprintf(buf+ret, "V?.? ");
break;
}
ret += sprintf(buf+ret, "port:%lX irq:%s",
(unsigned long)info->regs, __irq_itoa(info->irq));
if (!info->regs) {
ret += sprintf(buf+ret, "\n");
return ret;
}
/*
* Figure out the current RS-232 lines
*/
stat_buf[0] = 0;
stat_buf[1] = 0;
save_flags(flags); cli();
if (readb(&info->regs->r.mode) & SAB82532_MODE_RTS) {
if (!(readb(&info->regs->r.mode) & SAB82532_MODE_FRTS))
strcat(stat_buf, "|RTS");
} else {
strcat(stat_buf, "|RTS");
}
if (readb(&info->regs->r.star) & SAB82532_STAR_CTS)
strcat(stat_buf, "|CTS");
if (!(readb(&info->regs->r.pvr) & info->pvr_dtr_bit))
strcat(stat_buf, "|DTR");
if (!(readb(&info->regs->r.pvr) & info->pvr_dsr_bit))
strcat(stat_buf, "|DSR");
if (!(readb(&info->regs->r.vstr) & SAB82532_VSTR_CD))
strcat(stat_buf, "|CD");
restore_flags(flags);
if (info->baud)
ret += sprintf(buf+ret, " baud:%u", info->baud);
ret += sprintf(buf+ret, " tx:%u rx:%u",
info->icount.tx, info->icount.rx);
if (info->icount.frame)
ret += sprintf(buf+ret, " fe:%u", info->icount.frame);
if (info->icount.parity)
ret += sprintf(buf+ret, " pe:%u", info->icount.parity);
if (info->icount.brk)
ret += sprintf(buf+ret, " brk:%u", info->icount.brk);
if (info->icount.overrun)
ret += sprintf(buf+ret, " oe:%u", info->icount.overrun);
/*
* Last thing is the RS-232 status lines.
*/
ret += sprintf(buf+ret, " %s\n", stat_buf + 1);
return ret;
}
int sab82532_read_proc(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
struct sab82532 *info = sab82532_chain;
off_t begin = 0;
int len = 0;
len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
for (info = sab82532_chain; info && len < 4000; info = info->next) {
len += line_info(page + len, info);
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);
}
/*
* ---------------------------------------------------------------------
* sab82532_init() and friends
*
* sab82532_init() is called at boot-time to initialize the serial driver.
* ---------------------------------------------------------------------
*/
static int __init get_sab82532(unsigned long *memory_start)
{
struct linux_ebus *ebus;
struct linux_ebus_device *edev = 0;
struct sab82532 *sab;
unsigned long regs, offset;
int i;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->prom_name, "se"))
goto ebus_done;
if (!strcmp(edev->prom_name, "serial")) {
char compat[32];
int clen;
/* On RIO this can be an SE, check it. We could
* just check ebus->is_rio, but this is more portable.
*/
clen = prom_getproperty(edev->prom_node, "compatible",
compat, sizeof(compat));
if (clen > 0) {
if (strncmp(compat, "sab82532", 8) == 0) {
/* Yep. */
goto ebus_done;
}
}
}
}
}
ebus_done:
if (!edev)
return -ENODEV;
regs = edev->resource[0].start;
offset = sizeof(union sab82532_async_regs);
for (i = 0; i < 2; i++) {
if (memory_start) {
*memory_start = (*memory_start + 7) & ~(7);
sab = (struct sab82532 *)*memory_start;
*memory_start += sizeof(struct sab82532);
} else {
sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532),
GFP_KERNEL);
if (!sab) {
printk("sab82532: can't alloc sab struct\n");
break;
}
}
memset(sab, 0, sizeof(struct sab82532));
sab->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs));
sab->irq = edev->irqs[0];
sab->line = 1 - i;
sab->xmit_fifo_size = 32;
sab->recv_fifo_size = 32;
writeb(SAB82532_IPC_IC_ACT_LOW, &sab->regs->w.ipc);
sab->next = sab82532_chain;
sab82532_chain = sab;
offset -= sizeof(union sab82532_async_regs);
}
return 0;
}
#ifndef MODULE
static void __init sab82532_kgdb_hook(int line)
{
prom_printf("sab82532: kgdb support is not implemented, yet\n");
prom_halt();
}
#endif
static inline void __init show_serial_version(void)
{
char *revision = "$Revision: 1.65 $";
char *version, *p;
version = strchr(revision, ' ');
strcpy(serial_version, ++version);
p = strchr(serial_version, ' ');
*p = '\0';
printk("SAB82532 serial driver version %s\n", serial_version);
}
extern int su_num_ports;
/*
* The serial driver boot-time initialization code!
*/
int __init sab82532_init(void)
{
struct sab82532 *info;
int i;
if (!sab82532_chain)
get_sab82532(0);
if (!sab82532_chain)
return -ENODEV;
init_bh(SERIAL_BH, do_serial_bh);
show_serial_version();
/* Initialize the tty_driver structure */
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "serial";
#ifdef CONFIG_DEVFS_FS
serial_driver.name = "tts/%d";
#else
serial_driver.name = "ttyS";
#endif
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64 + su_num_ports;
serial_driver.num = NR_PORTS;
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 =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
serial_driver.flags = TTY_DRIVER_REAL_RAW;
serial_driver.refcount = &sab82532_refcount;
serial_driver.table = sab82532_table;
serial_driver.termios = sab82532_termios;
serial_driver.termios_locked = sab82532_termios_locked;
serial_driver.open = sab82532_open;
serial_driver.close = sab82532_close;
serial_driver.write = sab82532_write;
serial_driver.put_char = sab82532_put_char;
serial_driver.flush_chars = sab82532_flush_chars;
serial_driver.write_room = sab82532_write_room;
serial_driver.chars_in_buffer = sab82532_chars_in_buffer;
serial_driver.flush_buffer = sab82532_flush_buffer;
serial_driver.ioctl = sab82532_ioctl;
serial_driver.throttle = sab82532_throttle;
serial_driver.unthrottle = sab82532_unthrottle;
serial_driver.send_xchar = sab82532_send_xchar;
serial_driver.set_termios = sab82532_set_termios;
serial_driver.stop = sab82532_stop;
serial_driver.start = sab82532_start;
serial_driver.hangup = sab82532_hangup;
serial_driver.break_ctl = sab82532_break;
serial_driver.wait_until_sent = sab82532_wait_until_sent;
serial_driver.read_proc = sab82532_read_proc;
/*
* The callout device is just like normal device except for
* major number and the subtype code.
*/
callout_driver = serial_driver;
#ifdef CONFIG_DEVFS_FS
callout_driver.name = "cua/%d";
#else
callout_driver.name = "cua";
#endif
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
callout_driver.read_proc = 0;
callout_driver.proc_entry = 0;
if (tty_register_driver(&serial_driver))
panic("Couldn't register serial driver\n");
if (tty_register_driver(&callout_driver))
panic("Couldn't register callout driver\n");
for (info = sab82532_chain, i = 0; info; info = info->next, i++) {
info->magic = SERIAL_MAGIC;
info->type = readb(&info->regs->r.vstr) & 0x0f;
writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &info->regs->w.pcr);
writeb(0xff, &info->regs->w.pim);
if (info->line == 0) {
info->pvr_dsr_bit = (1 << 0);
info->pvr_dtr_bit = (1 << 1);
} else {
info->pvr_dsr_bit = (1 << 3);
info->pvr_dtr_bit = (1 << 2);
}
writeb((1 << 1) | (1 << 2) | (1 << 4), &info->regs->w.pvr);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
info->custom_divisor = 16;
info->close_delay = 5*HZ/10;
info->closing_wait = 30*HZ;
info->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
info->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
info->x_char = 0;
info->event = 0;
info->blocked_open = 0;
info->tqueue.routine = do_softint;
info->tqueue.data = info;
info->tqueue_hangup.routine = do_serial_hangup;
info->tqueue_hangup.data = info;
info->callout_termios = callout_driver.init_termios;
info->normal_termios = serial_driver.init_termios;
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->close_wait);
init_waitqueue_head(&info->delta_msr_wait);
info->icount.cts = info->icount.dsr =
info->icount.rng = info->icount.dcd = 0;
info->icount.rx = info->icount.tx = 0;
info->icount.frame = info->icount.parity = 0;
info->icount.overrun = info->icount.brk = 0;
if (!(info->line & 0x01)) {
if (request_irq(info->irq, sab82532_interrupt, SA_SHIRQ,
"serial(sab82532)", info)) {
printk("sab82532: can't get IRQ %x\n",
info->irq);
panic("sab82532 initialization failed");
}
}
printk(KERN_INFO
"ttyS%02d at 0x%lx (irq = %s) is a SAB82532 %s\n",
info->line + su_num_ports, (unsigned long)info->regs,
__irq_itoa(info->irq), sab82532_version[info->type]);
}
#ifdef SERIAL_LOG_DEVICE
dprint_init(SERIAL_LOG_DEVICE);
#endif
return 0;
}
int __init sab82532_probe(void)
{
int node, enode, snode;
char model[32];
int len;
node = prom_getchild(prom_root_node);
node = prom_searchsiblings(node, "pci");
/*
* Check for SUNW,sabre on Ultra 5/10/AXi.
*/
len = prom_getproperty(node, "model", model, sizeof(model));
if ((len > 0) && !strncmp(model, "SUNW,sabre", len)) {
node = prom_getchild(node);
node = prom_searchsiblings(node, "pci");
}
/*
* For each PCI bus...
*/
while (node) {
enode = prom_getchild(node);
enode = prom_searchsiblings(enode, "ebus");
/*
* For each EBus on this PCI...
*/
while (enode) {
int child;
child = prom_getchild(enode);
snode = prom_searchsiblings(child, "se");
if (snode)
goto found;
snode = prom_searchsiblings(child, "serial");
if (snode) {
char compat[32];
int clen;
clen = prom_getproperty(snode, "compatible",
compat, sizeof(compat));
if (clen > 0) {
if (strncmp(compat, "sab82532", 8) == 0)
goto found;
}
}
enode = prom_getsibling(enode);
enode = prom_searchsiblings(enode, "ebus");
}
node = prom_getsibling(node);
node = prom_searchsiblings(node, "pci");
}
return -ENODEV;
found:
#ifdef CONFIG_SERIAL_CONSOLE
sunserial_setinitfunc(sab82532_console_init);
#endif
#ifndef MODULE
sunserial_setinitfunc(sab82532_init);
rs_ops.rs_kgdb_hook = sab82532_kgdb_hook;
#endif
return 0;
}
#ifdef MODULE
MODULE_LICENSE("GPL");
int init_module(void)
{
if (get_sab82532(0))
return -ENODEV;
return sab82532_init();
}
void cleanup_module(void)
{
struct sab82532 *sab;
unsigned long flags;
int e1, e2;
/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
save_flags(flags);
cli();
remove_bh(SERIAL_BH);
if ((e1 = tty_unregister_driver(&serial_driver)))
printk("SERIAL: failed to unregister serial driver (%d)\n",
e1);
if ((e2 = tty_unregister_driver(&callout_driver)))
printk("SERIAL: failed to unregister callout driver (%d)\n",
e2);
restore_flags(flags);
if (tmp_buf) {
free_page((unsigned long) tmp_buf);
tmp_buf = NULL;
}
for (sab = sab82532_chain; sab; sab = sab->next) {
if (!(sab->line & 0x01))
free_irq(sab->irq, sab);
iounmap(sab->regs);
}
}
#endif /* MODULE */
#ifdef CONFIG_SERIAL_CONSOLE
static void
batten_down_hatches(struct sab82532 *info)
{
unsigned char saved_rfc, tmp;
if (!stop_a_enabled)
return;
/* If we are doing kadb, we call the debugger
* else we just drop into the boot monitor.
* Note that we must flush the user windows
* first before giving up control.
*/
printk("\n");
flush_user_windows();
/*
* Set FIFO to single character mode.
*/
saved_rfc = readb(&info->regs->r.rfc);
tmp = readb(&info->regs->rw.rfc);
tmp &= ~(SAB82532_RFC_RFDF);
writeb(tmp, &info->regs->rw.rfc);
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr);
#ifndef __sparc_v9__
if ((((unsigned long)linux_dbvec) >= DEBUG_FIRSTVADDR) &&
(((unsigned long)linux_dbvec) <= DEBUG_LASTVADDR))
sp_enter_debugger();
else
#endif
prom_cmdline();
/*
* Reset FIFO to character + status mode.
*/
writeb(saved_rfc, &info->regs->w.rfc);
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr);
}
static __inline__ void
sab82532_console_putchar(struct sab82532 *info, char c)
{
unsigned long flags;
save_flags(flags); cli();
sab82532_tec_wait(info);
writeb(c, &info->regs->w.tic);
restore_flags(flags);
}
static void
sab82532_console_write(struct console *con, const char *s, unsigned n)
{
struct sab82532 *info;
int i;
info = sab82532_chain;
for (i = con->index; i; i--) {
info = info->next;
if (!info)
return;
}
for (i = 0; i < n; i++) {
if (*s == '\n')
sab82532_console_putchar(info, '\r');
sab82532_console_putchar(info, *s++);
}
sab82532_tec_wait(info);
}
static kdev_t
sab82532_console_device(struct console *con)
{
return MKDEV(TTY_MAJOR, 64 + con->index);
}
static int
sab82532_console_setup(struct console *con, char *options)
{
static struct tty_struct c_tty;
static struct termios c_termios;
struct sab82532 *info;
tcflag_t cflag;
int i;
info = sab82532_chain;
for (i = con->index; i; i--) {
info = info->next;
if (!info)
return -ENODEV;
}
info->is_console = 1;
/*
* Initialize the hardware
*/
sab82532_init_line(info);
/*
* Finally, enable interrupts
*/
info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
printk("Console: ttyS%d (SAB82532)\n", info->line);
sunserial_console_termios(con);
cflag = con->cflag;
/*
* Fake up the tty and tty->termios structures so we can use
* change_speed (and eliminate a lot of duplicate code).
*/
if (!info->tty) {
memset(&c_tty, 0, sizeof(c_tty));
info->tty = &c_tty;
}
if (!info->tty->termios) {
memset(&c_termios, 0, sizeof(c_termios));
info->tty->termios = &c_termios;
}
info->tty->termios->c_cflag = con->cflag;
change_speed(info);
/* Now take out the pointers to static structures if necessary */
if (info->tty->termios == &c_termios)
info->tty->termios = 0;
if (info->tty == &c_tty)
info->tty = 0;
return 0;
}
static struct console sab82532_console = {
name: "ttyS",
write: sab82532_console_write,
device: sab82532_console_device,
setup: sab82532_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
int __init sab82532_console_init(void)
{
extern int con_is_present(void);
extern int su_console_registered;
if (con_is_present() || su_console_registered)
return 0;
if (!sab82532_chain) {
prom_printf("sab82532_console_setup: can't get SAB82532 chain");
prom_halt();
}
sab82532_console.index = serial_console - 1;
register_console(&sab82532_console);
return 0;
}
#ifdef SERIAL_LOG_DEVICE
static int serial_log_device = 0;
static void
dprint_init(int tty)
{
serial_console = tty + 1;
sab82532_console.index = tty;
sab82532_console_setup(&sab82532_console, "");
serial_console = 0;
serial_log_device = tty + 1;
}
int
dprintf(const char *fmt, ...)
{
static char buffer[4096];
va_list args;
int i;
if (!serial_log_device)
return 0;
va_start(args, fmt);
i = vsprintf(buffer, fmt, args);
va_end(args);
sab82532_console.write(&sab82532_console, buffer, i);
return i;
}
#endif /* SERIAL_LOG_DEVICE */
#endif /* CONFIG_SERIAL_CONSOLE */