📄 sab82532.c
字号:
/* $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"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_BHstatic 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_DEVICEstatic void dprint_init(int tty);#endifstatic 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_CONSOLEextern 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))#endifstatic 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 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; } 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; } 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++; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -