sunsab.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,166 行 · 第 1/2 页
C
1,166 行
/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2002 David S. Miller (davem@redhat.com) * * 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 * * Ported to new 2.5.x UART layer. * David S. Miller <davem@redhat.com> */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/string.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/circ_buf.h>#include <linux/serial.h>#include <linux/sysrq.h>#include <linux/console.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/oplib.h>#include <asm/ebus.h>#if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/serial_core.h>#include "suncore.h"#include "sunsab.h"struct uart_sunsab_port { struct uart_port port; /* Generic UART port */ union sab82532_async_regs *regs; /* Chip registers */ unsigned long irqflags; /* IRQ state flags */ int dsr; /* Current DSR state */ unsigned int cec_timeout; /* Chip poll timeout... */ unsigned int tec_timeout; /* likewise */ unsigned char interrupt_mask0;/* ISR0 masking */ unsigned char interrupt_mask1;/* ISR1 masking */ unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */ unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */ int type; /* SAB82532 version */};/* * This assumes you have a 29.4912 MHz clock for your UART. */#define SAB_BASE_BAUD ( 29491200 / 16 )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)"};#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) */#define SAB82532_RECV_FIFO_SIZE 32 /* Standard async fifo sizes */#define SAB82532_XMIT_FIFO_SIZE 32static __inline__ void sunsab_tec_wait(struct uart_sunsab_port *up){ int timeout = up->tec_timeout; while ((readb(&up->regs->r.star) & SAB82532_STAR_TEC) && --timeout) udelay(1);}static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up){ int timeout = up->cec_timeout; while ((readb(&up->regs->r.star) & SAB82532_STAR_CEC) && --timeout) udelay(1);}static struct tty_struct *receive_chars(struct uart_sunsab_port *up, union sab82532_irq_status *stat, struct pt_regs *regs){ struct tty_struct *tty = NULL; unsigned char buf[32]; int saw_console_brk = 0; int free_fifo = 0; int count = 0; int i; if (up->port.info != NULL) /* Unopened serial console */ tty = up->port.info->tty; /* Read number of BYTES (Character + Status) available. */ if (stat->sreg.isr0 & SAB82532_ISR0_RPF) { count = SAB82532_RECV_FIFO_SIZE; free_fifo++; } if (stat->sreg.isr0 & SAB82532_ISR0_TCD) { count = readb(&up->regs->r.rbcl) & (SAB82532_RECV_FIFO_SIZE - 1); free_fifo++; } /* Issue a FIFO read command in case we where idle. */ if (stat->sreg.isr0 & SAB82532_ISR0_TIME) { sunsab_cec_wait(up); writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr); return tty; } if (stat->sreg.isr0 & SAB82532_ISR0_RFO) free_fifo++; /* Read the FIFO. */ for (i = 0; i < count; i++) buf[i] = readb(&up->regs->r.rfifo[i]); /* Issue Receive Message Complete command. */ if (free_fifo) { sunsab_cec_wait(up); writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr); } for (i = 0; i < count; i++) { unsigned char ch = buf[i]; if (tty == NULL) { uart_handle_sysrq_char(&up->port, ch, regs); continue; } if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { tty->flip.work.func((void *)tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) return tty; // if TTY_DONT_FLIP is set } *tty->flip.char_buf_ptr = ch; *tty->flip.flag_buf_ptr = TTY_NORMAL; up->port.icount.rx++; if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR | SAB82532_ISR0_FERR | SAB82532_ISR0_RFO)) || unlikely(stat->sreg.isr1 & SAB82532_ISR1_BRK)) { /* * For statistics only */ if (stat->sreg.isr1 & SAB82532_ISR1_BRK) { stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR | SAB82532_ISR0_FERR); up->port.icount.brk++; if (up->port.line == up->port.cons->index) saw_console_brk = 1; /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask * or read_status_mask. */ if (uart_handle_break(&up->port)) continue; } else if (stat->sreg.isr0 & SAB82532_ISR0_PERR) up->port.icount.parity++; else if (stat->sreg.isr0 & SAB82532_ISR0_FERR) up->port.icount.frame++; if (stat->sreg.isr0 & SAB82532_ISR0_RFO) up->port.icount.overrun++; /* * Mask off conditions which should be ingored. */ stat->sreg.isr0 &= (up->port.read_status_mask & 0xff); stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff); if (stat->sreg.isr1 & SAB82532_ISR1_BRK) { *tty->flip.flag_buf_ptr = TTY_BREAK; } else if (stat->sreg.isr0 & SAB82532_ISR0_PERR) *tty->flip.flag_buf_ptr = TTY_PARITY; else if (stat->sreg.isr0 & SAB82532_ISR0_FERR) *tty->flip.flag_buf_ptr = TTY_FRAME; } if (uart_handle_sysrq_char(&up->port, ch, regs)) continue; if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 && (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0){ tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; } if ((stat->sreg.isr0 & SAB82532_ISR0_RFO) && tty->flip.count < TTY_FLIPBUF_SIZE) { /* * Overrun is special, since it's reported * immediately, and doesn't affect the current * character. */ *tty->flip.flag_buf_ptr = TTY_OVERRUN; tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; } } if (saw_console_brk) sun_do_break(); return tty;}static void sunsab_stop_tx(struct uart_port *, unsigned int);static void transmit_chars(struct uart_sunsab_port *up, union sab82532_irq_status *stat){ struct circ_buf *xmit = &up->port.info->xmit; int i; if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) { up->interrupt_mask1 |= SAB82532_IMR1_ALLS; writeb(up->interrupt_mask1, &up->regs->w.imr1); set_bit(SAB82532_ALLS, &up->irqflags); }#if 0 /* bde@nwlink.com says this check causes problems */ if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR)) return;#endif if (!(readb(&up->regs->r.star) & SAB82532_STAR_XFW)) return; set_bit(SAB82532_XPR, &up->irqflags); if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { up->interrupt_mask1 |= SAB82532_IMR1_XPR; writeb(up->interrupt_mask1, &up->regs->w.imr1); uart_write_wakeup(&up->port); return; } up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); writeb(up->interrupt_mask1, &up->regs->w.imr1); clear_bit(SAB82532_ALLS, &up->irqflags); /* Stuff 32 bytes into Transmit FIFO. */ clear_bit(SAB82532_XPR, &up->irqflags); for (i = 0; i < up->port.fifosize; i++) { writeb(xmit->buf[xmit->tail], &up->regs->w.xfifo[i]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); up->port.icount.tx++; if (uart_circ_empty(xmit)) break; } /* Issue a Transmit Frame command. */ sunsab_cec_wait(up); writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); if (uart_circ_empty(xmit)) sunsab_stop_tx(&up->port, 0);}static void check_status(struct uart_sunsab_port *up, union sab82532_irq_status *stat){ if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) uart_handle_dcd_change(&up->port, !(readb(&up->regs->r.vstr) & SAB82532_VSTR_CD)); if (stat->sreg.isr1 & SAB82532_ISR1_CSC) uart_handle_cts_change(&up->port, (readb(&up->regs->r.star) & SAB82532_STAR_CTS)); if ((readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ^ up->dsr) { up->dsr = (readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ? 0 : 1; up->port.icount.dsr++; } wake_up_interruptible(&up->port.info->delta_msr_wait);}static irqreturn_t sunsab_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct uart_sunsab_port *up = dev_id; struct tty_struct *tty; union sab82532_irq_status status; unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); status.stat = 0; if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA0) status.sreg.isr0 = readb(&up->regs->r.isr0); if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA1) status.sreg.isr1 = readb(&up->regs->r.isr1); tty = NULL; if (status.stat) { if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) tty = receive_chars(up, &status, regs); if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || (status.sreg.isr1 & SAB82532_ISR1_CSC)) check_status(up, &status); if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR)) transmit_chars(up, &status); } spin_unlock(&up->port.lock); if (tty) tty_flip_buffer_push(tty); up++; spin_lock(&up->port.lock); status.stat = 0; if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB0) status.sreg.isr0 = readb(&up->regs->r.isr0); if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB1) status.sreg.isr1 = readb(&up->regs->r.isr1); tty = NULL; if (status.stat) { if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) tty = receive_chars(up, &status, regs); if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC))) check_status(up, &status); if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR)) transmit_chars(up, &status); } spin_unlock_irqrestore(&up->port.lock, flags); if (tty) tty_flip_buffer_push(tty); return IRQ_HANDLED;}/* port->lock is not held. */static unsigned int sunsab_tx_empty(struct uart_port *port){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; int ret; /* Do not need a lock for a state test like this. */ if (test_bit(SAB82532_ALLS, &up->irqflags)) ret = TIOCSER_TEMT; else ret = 0; return ret;}/* port->lock held by caller. */static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; if (mctrl & TIOCM_RTS) { writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS, &up->regs->rw.mode); writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS, &up->regs->rw.mode); } else { writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS, &up->regs->rw.mode); writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS, &up->regs->rw.mode); } if (mctrl & TIOCM_DTR) { writeb(readb(&up->regs->rw.pvr) & ~(up->pvr_dtr_bit), &up->regs->rw.pvr); } else { writeb(readb(&up->regs->rw.pvr) | up->pvr_dtr_bit, &up->regs->rw.pvr); }}/* port->lock is not held. */static unsigned int sunsab_get_mctrl(struct uart_port *port){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; unsigned long flags; unsigned char val; unsigned int result; result = 0; spin_lock_irqsave(&up->port.lock, flags); val = readb(&up->regs->r.pvr); result |= (val & up->pvr_dsr_bit) ? 0 : TIOCM_DSR; val = readb(&up->regs->r.vstr); result |= (val & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR; val = readb(&up->regs->r.star); result |= (val & SAB82532_STAR_CTS) ? TIOCM_CTS : 0; spin_unlock_irqrestore(&up->port.lock, flags); return result;}/* port->lock held by caller. */static void sunsab_stop_tx(struct uart_port *port, unsigned int tty_stop){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; up->interrupt_mask1 |= SAB82532_IMR1_XPR; writeb(up->interrupt_mask1, &up->regs->w.imr1);}/* port->lock held by caller. */static void sunsab_start_tx(struct uart_port *port, unsigned int tty_start){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; struct circ_buf *xmit = &up->port.info->xmit; int i; up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); writeb(up->interrupt_mask1, &up->regs->w.imr1); if (!test_bit(SAB82532_XPR, &up->irqflags)) return; clear_bit(SAB82532_ALLS, &up->irqflags); clear_bit(SAB82532_XPR, &up->irqflags); for (i = 0; i < up->port.fifosize; i++) { writeb(xmit->buf[xmit->tail], &up->regs->w.xfifo[i]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); up->port.icount.tx++; if (uart_circ_empty(xmit)) break; } /* Issue a Transmit Frame command. */ sunsab_cec_wait(up); writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);}/* port->lock is not held. */static void sunsab_send_xchar(struct uart_port *port, char ch){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); sunsab_tec_wait(up); writeb(ch, &up->regs->w.tic); spin_unlock_irqrestore(&up->port.lock, flags);}/* port->lock held by caller. */static void sunsab_stop_rx(struct uart_port *port){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; up->interrupt_mask0 |= SAB82532_ISR0_TCD; writeb(up->interrupt_mask1, &up->regs->w.imr0);}/* port->lock held by caller. */static void sunsab_enable_ms(struct uart_port *port){ /* For now we always receive these interrupts. */}/* port->lock is not held. */static void sunsab_break_ctl(struct uart_port *port, int break_state){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; unsigned long flags; unsigned char val; spin_lock_irqsave(&up->port.lock, flags); val = readb(&up->regs->rw.dafo); if (break_state) val |= SAB82532_DAFO_XBRK; else val &= ~SAB82532_DAFO_XBRK; writeb(val, &up->regs->rw.dafo); spin_unlock_irqrestore(&up->port.lock, flags);}/* port->lock is not held. */static int sunsab_startup(struct uart_port *port){ struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; unsigned long flags; unsigned char tmp; spin_lock_irqsave(&up->port.lock, flags); /* * Wait for any commands or immediate characters */ sunsab_cec_wait(up); sunsab_tec_wait(up); /* * Clear the FIFO buffers. */ writeb(SAB82532_CMDR_RRES, &up->regs->w.cmdr); sunsab_cec_wait(up); writeb(SAB82532_CMDR_XRES, &up->regs->w.cmdr); /* * Clear the interrupt registers. */ (void) readb(&up->regs->r.isr0); (void) readb(&up->regs->r.isr1); /* * Now, initialize the UART */ writeb(0, &up->regs->w.ccr0); /* power-down */ writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ | SAB82532_CCR0_SM_ASYNC, &up->regs->w.ccr0); writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &up->regs->w.ccr1); writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL | SAB82532_CCR2_TOE, &up->regs->w.ccr2); writeb(0, &up->regs->w.ccr3); writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4); writeb(SAB82532_MODE_RTS | SAB82532_MODE_FCTS | SAB82532_MODE_RAC, &up->regs->w.mode); writeb(SAB82532_RFC_DPS|SAB82532_RFC_RFTH_32, &up->regs->w.rfc); tmp = readb(&up->regs->rw.ccr0); tmp |= SAB82532_CCR0_PU; /* power-up */ writeb(tmp, &up->regs->rw.ccr0); /* * Finally, enable interrupts */ up->interrupt_mask0 = (SAB82532_IMR0_PERR | SAB82532_IMR0_FERR | SAB82532_IMR0_PLLA); writeb(up->interrupt_mask0, &up->regs->w.imr0); up->interrupt_mask1 = (SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS | SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN | SAB82532_IMR1_CSC | SAB82532_IMR1_XON | SAB82532_IMR1_XPR); writeb(up->interrupt_mask1, &up->regs->w.imr1); set_bit(SAB82532_ALLS, &up->irqflags); set_bit(SAB82532_XPR, &up->irqflags);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?