?? zs.c
字號:
/* $Id: zs.c,v 1.68.2.2 2002/01/12 07:04:33 davem Exp $ * zs.c: Zilog serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Fixes by Pete A. Zaitcev <zaitcev@yahoo.com>. * * Fixed to use tty_get_baud_rate(). * Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 * * /proc/tty/driver/serial now exists and is readable. * Alex Buell <alex.buell@tahallah.demon.co.uk>, 2001-12-23 * */#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/config.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/kernel.h>#include <linux/keyboard.h>#include <linux/console.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/bootmem.h>#include <linux/sysrq.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/oplib.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/kdebug.h>#include <asm/page.h>#include <asm/pgtable.h>#include <asm/sbus.h>#ifdef __sparc_v9__#include <asm/fhc.h>#endif#ifdef CONFIG_PCI#include <linux/pci.h>#endif#include "sunserial.h"#include "zs.h"#include "sunkbd.h"#include "sunmouse.h"static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */#define NUM_SERIAL num_serial#define NUM_CHANNELS (NUM_SERIAL * 2)#define KEYBOARD_LINE 0x2#define MOUSE_LINE 0x3/* On 32-bit sparcs we need to delay after register accesses * to accomodate sun4 systems, but we do not need to flush writes. * On 64-bit sparc we only need to flush single writes to ensure * completion. */#ifndef __sparc_v9__#define ZSDELAY() udelay(5)#define ZSDELAY_LONG() udelay(20)#define ZS_WSYNC(channel) do { } while(0)#else#define ZSDELAY()#define ZSDELAY_LONG()#define ZS_WSYNC(__channel) \ sbus_readb(&((__channel)->control))#endifstruct sun_zslayout **zs_chips;struct sun_zschannel **zs_channels;struct sun_zschannel *zs_mousechan;struct sun_zschannel *zs_kbdchan;struct sun_zschannel *zs_kgdbchan;int *zs_nodes;struct sun_serial *zs_soft;struct sun_serial *zs_chain; /* IRQ servicing chain */int zilog_irq;struct tty_struct *zs_ttys;/* Console hooks... */#ifdef CONFIG_SERIAL_CONSOLEstatic struct console zs_console;static int zs_console_init(void);/* * Define this to get the zs_fair_output() functionality. */#undef SERIAL_CONSOLE_FAIR_OUTPUT#endif /* CONFIG_SERIAL_CONSOLE */static unsigned char kgdb_regs[16] = { 0, 0, 0, /* write 0, 1, 2 */ (Rx8 | RxENAB), /* write 3 */ (X16CLK | SB1 | PAR_EVEN), /* write 4 */ (DTR | Tx8 | TxENAB), /* write 5 */ 0, 0, 0, /* write 6, 7, 8 */ (NV), /* write 9 */ (NRZ), /* write 10 */ (TCBR | RCBR), /* write 11 */ 0, 0, /* BRG time constant, write 12 + 13 */ (BRSRC | BRENAB), /* write 14 */ (DCDIE) /* write 15 */};static unsigned char zscons_regs[16] = { 0, /* write 0 */ (EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */ 0, /* write 2 */ (Rx8 | RxENAB), /* write 3 */ (X16CLK), /* write 4 */ (DTR | Tx8 | TxENAB), /* write 5 */ 0, 0, 0, /* write 6, 7, 8 */ (NV | MIE), /* write 9 */ (NRZ), /* write 10 */ (TCBR | RCBR), /* write 11 */ 0, 0, /* BRG time constant, write 12 + 13 */ (BRSRC | BRENAB), /* write 14 */ (DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */};#define ZS_CLOCK 4915200 /* Zilog input clock rate */DECLARE_TASK_QUEUE(tq_serial);static struct tty_driver serial_driver, callout_driver;static int serial_refcount;/* serial subtype definitions */#define SERIAL_TYPE_NORMAL 1#define SERIAL_TYPE_CALLOUT 2 /* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256#define SERIAL_DO_RESTART/* Debugging... DEBUG_INTR is bad to use when one of the zs * lines is your console ;( */#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#define RS_STROBE_TIME 10#define RS_ISR_PASS_LIMIT 256#define _INLINE_ inlineint zs_init(void);static void zs_kgdb_hook(int);static void change_speed(struct sun_serial *info);static struct tty_struct **serial_table;static struct termios **serial_termios;static struct termios **serial_termios_locked;#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif#undef ZS_LOG#ifdef ZS_LOGstruct zs_logent { u8 reg, val; u8 write, __pad;#define REGIRQ 0xff#define REGDATA 0xfe#define REGCTRL 0xfd};struct zs_logent zslog[32];int zs_curlog;#define ZSLOG(__reg, __val, __write) \do{ int index = zs_curlog; \ zslog[index].reg = (__reg); \ zslog[index].val = (__val); \ zslog[index].write = (__write); \ zs_curlog = (index + 1) & (32 - 1); \}while(0)int zs_dumplog(char *buffer){ int len = 0; int i; for (i = 0; i < 32; i++) { u8 reg, val, write; reg = zslog[i].reg; val = zslog[i].val; write = zslog[i].write; len += sprintf(buffer + len, "ZSLOG[%2d]: reg %2x val %2x %s\n", i, reg, val, write ? "write" : "read"); } len += sprintf(buffer + len, "ZS current log index %d\n", zs_curlog); return len;}#else#define ZSLOG(x,y,z) do { } while (0)#endif/* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the memcpy_fromfs 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 sun_serial *info, dev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = "Warning: bad magic number for serial struct (%d, %d) in %s\n"; static const char *badinfo = "Warning: null sun_serial for (%d, %d) in %s\n"; if (!info) { printk(badinfo, MAJOR(device), MINOR(device), routine); return 1; } if (info->magic != SERIAL_MAGIC) { printk(badmagic, MAJOR(device), MINOR(device), routine); return 1; }#endif return 0;}/* Reading and writing Zilog8530 registers. The delays are to make this * driver work on the Sun4 which needs a settling delay after each chip * register access, other machines handle this in hardware via auxiliary * flip-flops which implement the settle time we do in software. */static unsigned char read_zsreg(struct sun_zschannel *channel, unsigned char reg){ unsigned char retval; sbus_writeb(reg, &channel->control); ZSDELAY(); retval = sbus_readb(&channel->control); ZSDELAY(); ZSLOG(reg, retval, 0); return retval;}static void write_zsreg(struct sun_zschannel *channel, unsigned char reg, unsigned char value){ ZSLOG(reg, value, 1); sbus_writeb(reg, &channel->control); ZSDELAY(); sbus_writeb(value, &channel->control); ZSDELAY();}static void load_zsregs(struct sun_serial *info, unsigned char *regs){ struct sun_zschannel *channel = info->zs_channel; unsigned long flags; unsigned char stat; int i; for (i = 0; i < 1000; i++) { stat = read_zsreg(channel, R1); if (stat & ALL_SNT) break; udelay(100); } write_zsreg(channel, R3, 0); ZS_CLEARSTAT(channel); ZS_CLEARERR(channel); ZS_CLEARFIFO(channel); /* Load 'em up */ save_flags(flags); cli(); if (info->channelA) write_zsreg(channel, R9, CHRA); else write_zsreg(channel, R9, CHRB); ZSDELAY_LONG(); write_zsreg(channel, R4, regs[R4]); write_zsreg(channel, R3, regs[R3] & ~RxENAB); write_zsreg(channel, R5, regs[R5] & ~TxENAB); write_zsreg(channel, R9, regs[R9] & ~MIE); write_zsreg(channel, R10, regs[R10]); write_zsreg(channel, R11, regs[R11]); write_zsreg(channel, R12, regs[R12]); write_zsreg(channel, R13, regs[R13]); write_zsreg(channel, R14, regs[R14] & ~BRENAB); write_zsreg(channel, R14, regs[R14]); write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB); write_zsreg(channel, R3, regs[R3]); write_zsreg(channel, R5, regs[R5]); write_zsreg(channel, R15, regs[R15]); write_zsreg(channel, R0, RES_EXT_INT); write_zsreg(channel, R0, ERR_RES); write_zsreg(channel, R1, regs[R1]); write_zsreg(channel, R9, regs[R9]); restore_flags(flags);}#define ZS_PUT_CHAR_MAX_DELAY 2000 /* 10 ms */static void zs_put_char(struct sun_zschannel *channel, char ch){ int loops = ZS_PUT_CHAR_MAX_DELAY; /* Do not change this to use ZSDELAY as this is * a timed polling loop and on sparc64 ZSDELAY * is a nop. -DaveM */ do { u8 val = sbus_readb(&channel->control); ZSLOG(REGCTRL, val, 0); if (val & Tx_BUF_EMP) break; udelay(5); } while (--loops); sbus_writeb(ch, &channel->data); ZSDELAY(); ZS_WSYNC(channel); ZSLOG(REGDATA, ch, 1);}/* Sets or clears DTR/RTS on the requested line */static void zs_rtsdtr(struct sun_serial *ss, int set){ unsigned long flags; save_flags(flags); cli(); if(set) { ss->curregs[5] |= (RTS | DTR); write_zsreg(ss->zs_channel, 5, ss->curregs[5]); } else { ss->curregs[5] &= ~(RTS | DTR); write_zsreg(ss->zs_channel, 5, ss->curregs[5]); } restore_flags(flags); return;}static void kgdb_chaninit(struct sun_serial *ss, int intson, int bps){ int brg; if(intson) { kgdb_regs[R1] = INT_ALL_Rx; kgdb_regs[R9] |= MIE; } else { kgdb_regs[R1] = 0; kgdb_regs[R9] &= ~MIE; } brg = BPS_TO_BRG(bps, ZS_CLOCK/16); kgdb_regs[R12] = (brg & 255); kgdb_regs[R13] = ((brg >> 8) & 255); load_zsregs(ss, kgdb_regs);}/* * ------------------------------------------------------------ * zs_stop() and zs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void zs_stop(struct tty_struct *tty){ struct sun_serial *info = (struct sun_serial *) tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "zs_stop")) return; save_flags(flags); cli(); if (info->curregs[5] & TxENAB) { info->curregs[5] &= ~TxENAB; write_zsreg(info->zs_channel, 5, info->curregs[5]); } restore_flags(flags);}static void zs_start(struct tty_struct *tty){ struct sun_serial *info = (struct sun_serial *) tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "zs_start")) return; save_flags(flags); cli(); if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) { info->curregs[5] |= TxENAB; write_zsreg(info->zs_channel, 5, info->curregs[5]); } restore_flags(flags);}/* Drop into either the boot monitor or kadb upon receiving a break * from keyboard/console input. */void batten_down_hatches(void){ 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();#ifndef __sparc_v9__ if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) && (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR)) sp_enter_debugger(); else#endif prom_cmdline(); /* XXX We want to notify the keyboard driver that all * XXX keys are in the up state or else weird things * XXX happen... */ return;}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following * subroutines are declared as inline and are folded into * zs_interrupt(). They were separated out for readability's sake. * * Note: zs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off. People who may want to modify * zs_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 zs_sched_event(struct sun_serial *info, int event){ info->event |= 1 << event; queue_task(&info->tqueue, &tq_serial); mark_bh(SERIAL_BH);}#ifndef __sparc_v9__extern void breakpoint(void); /* For the KGDB frame character */#endifstatic void receive_chars(struct sun_serial *info, struct pt_regs *regs){ struct tty_struct *tty = info->tty; int do_queue_task = 0; while (1) { unsigned char ch, r1; r1 = read_zsreg(info->zs_channel, R1); if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { sbus_writeb(ERR_RES, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, ERR_RES, 1); } ch = sbus_readb(&info->zs_channel->data);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -