📄 serial_tx3912.c
字号:
/* * drivers/char/serial_tx3912.c * * Copyright (C) 1999 Harald Koerfgen * Copyright (C) 2000 Jim Pick <jim@jimpick.com> * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Serial driver for TMPR3912/05 and PR31700 processors */#include <linux/init.h>#include <linux/config.h>#include <linux/tty.h>#include <linux/major.h>#include <linux/ptrace.h>#include <linux/init.h>#include <linux/console.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/pm.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/delay.h>#include <asm/wbflush.h>#include <asm/tx3912.h>#include "serial_tx3912.h"/* * Forward declarations for serial routines */static void rs_disable_tx_interrupts (void * ptr);static void rs_enable_tx_interrupts (void * ptr); static void rs_disable_rx_interrupts (void * ptr); static void rs_enable_rx_interrupts (void * ptr); static int rs_get_CD (void * ptr); static void rs_shutdown_port (void * ptr); static int rs_set_real_termios (void *ptr);static int rs_chars_in_buffer (void * ptr); static void rs_hungup (void *ptr);static void rs_close (void *ptr);/* * Used by generic serial driver to access hardware */static struct real_driver rs_real_driver = { disable_tx_interrupts: rs_disable_tx_interrupts, enable_tx_interrupts: rs_enable_tx_interrupts, disable_rx_interrupts: rs_disable_rx_interrupts, enable_rx_interrupts: rs_enable_rx_interrupts, get_CD: rs_get_CD, shutdown_port: rs_shutdown_port, set_real_termios: rs_set_real_termios, chars_in_buffer: rs_chars_in_buffer, close: rs_close, hungup: rs_hungup,}; /* * Structures and such for TTY sessions and usage counts */static struct tty_driver rs_driver, rs_callout_driver;static struct tty_struct * rs_table[TX3912_UART_NPORTS] = { NULL, };static struct termios ** rs_termios;static struct termios ** rs_termios_locked;struct rs_port *rs_ports;int rs_refcount;int rs_initialized = 0;/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following * subroutines are declared as inline and are folded into * rs_interrupt(). They were separated out for readability's sake. * * Note: rs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off. People who may want to modify * rs_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 * ----------------------------------------------------------------------- */static inline void receive_char_pio(struct rs_port *port){ struct tty_struct *tty = port->gs.tty; unsigned char ch; int counter = 2048; /* While there are characters, get them ... */ while (counter>0) { if (!(inl(port->base + TX3912_UART_CTRL1) & UART_RX_HOLD_FULL)) break; ch = inb(port->base + TX3912_UART_DATA); if (tty->flip.count < TTY_FLIPBUF_SIZE) { *tty->flip.char_buf_ptr++ = ch; *tty->flip.flag_buf_ptr++ = 0; tty->flip.count++; } udelay(1); /* Allow things to happen - it take a while */ counter--; } if (!counter) printk( "Ugh, looped in receive_char_pio!\n" ); tty_flip_buffer_push(tty);#if 0 /* Now handle error conditions */ if (*status & (INTTYPE(UART_RXOVERRUN_INT) | INTTYPE(UART_FRAMEERR_INT) | INTTYPE(UART_PARITYERR_INT) | INTTYPE(UART_BREAK_INT))) { /* * Now check to see if character should be * ignored, and mask off conditions which * should be ignored. */ if (*status & port->ignore_status_mask) { goto ignore_char; } *status &= port->read_status_mask; if (*status & INTTYPE(UART_BREAK_INT)) { rs_dprintk(TX3912_UART_DEBUG_INTERRUPTS, "handling break...."); *tty->flip.flag_buf_ptr = TTY_BREAK; } else if (*status & INTTYPE(UART_PARITYERR_INT)) { *tty->flip.flag_buf_ptr = TTY_PARITY; } else if (*status & INTTYPE(UART_FRAMEERR_INT)) { *tty->flip.flag_buf_ptr = TTY_FRAME; } if (*status & INTTYPE(UART_RXOVERRUN_INT)) { /* * Overrun is special, since it's * reported immediately, and doesn't * affect the current character */ if (tty->flip.count < TTY_FLIPBUF_SIZE) { tty->flip.count++; tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; *tty->flip.flag_buf_ptr = TTY_OVERRUN; } } } tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++;ignore_char: tty_flip_buffer_push(tty);#endif}static inline void transmit_char_pio(struct rs_port *port){ /* While I'm able to transmit ... */ for (;;) { if (!(inl(port->base + TX3912_UART_CTRL1) & UART_TX_EMPTY)) break; else if (port->x_char) { outb(port->x_char, port->base + TX3912_UART_DATA); port->icount.tx++; port->x_char = 0; } else if (port->gs.xmit_cnt <= 0 || port->gs.tty->stopped || port->gs.tty->hw_stopped) { break; } else { outb(port->gs.xmit_buf[port->gs.xmit_tail++], port->base + TX3912_UART_DATA); port->icount.tx++; port->gs.xmit_tail &= SERIAL_XMIT_SIZE-1; if (--port->gs.xmit_cnt <= 0) { break; } } udelay(10); /* Allow things to happen - it take a while */ } if (port->gs.xmit_cnt <= 0 || port->gs.tty->stopped || port->gs.tty->hw_stopped) { rs_disable_tx_interrupts(port); } if (port->gs.xmit_cnt <= port->gs.wakeup_chars) { if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && port->gs.tty->ldisc.write_wakeup) (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); rs_dprintk (TX3912_UART_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", port->gs.wakeup_chars); wake_up_interruptible(&port->gs.tty->write_wait); } }static inline void check_modem_status(struct rs_port *port){ /* We don't have a carrier detect line - but just respond like we had one anyways so that open() becomes unblocked */ wake_up_interruptible(&port->gs.open_wait);}int count = 0;/* * This is the serial driver's interrupt routine (inlined, because * there are two different versions of this, one for each serial port, * differing only by the bits used in interrupt status 2 register) */static inline void rs_rx_interrupt(int irq, void *dev_id, struct pt_regs * regs, int intshift){ struct rs_port * port; unsigned long int2status; unsigned long flags; unsigned long ints; save_and_cli(flags); port = (struct rs_port *)dev_id; rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "rs_interrupt (port %p, shift %d)...", port, intshift); /* Get the interrrupts we have enabled */ int2status = IntStatus2 & IntEnable2; /* Get interrupts in easy to use form */ ints = int2status >> intshift; /* Clear any interrupts we might be about to handle */ IntClear2 = int2status & ( (INTTYPE(UART_RXOVERRUN_INT) | INTTYPE(UART_FRAMEERR_INT) | INTTYPE(UART_BREAK_INT) | INTTYPE(UART_PARITYERR_INT) | INTTYPE(UART_RX_INT)) << intshift); if (!port || !port->gs.tty) { restore_flags(flags); return; } /* RX Receiver Holding Register Overrun */ if (ints & INTTYPE(UART_RXOVERRUN_INT)) { rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "overrun"); port->icount.overrun++; } /* RX Frame Error */ if (ints & INTTYPE(UART_FRAMEERR_INT)) { rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "frame error"); port->icount.frame++; } /* Break signal received */ if (ints & INTTYPE(UART_BREAK_INT)) { rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "break"); port->icount.brk++; } /* RX Parity Error */ if (ints & INTTYPE(UART_PARITYERR_INT)) { rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "parity error"); port->icount.parity++; } /* Receive byte (non-DMA) */ if (ints & INTTYPE(UART_RX_INT)) { receive_char_pio(port); } restore_flags(flags); rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "end.\n");}static inline void rs_tx_interrupt(int irq, void *dev_id, struct pt_regs * regs, int intshift){ struct rs_port * port; unsigned long int2status; unsigned long flags; unsigned long ints; save_and_cli(flags); port = (struct rs_port *)dev_id; rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "rs_interrupt (port %p, shift %d)...", port, intshift); /* Get the interrrupts we have enabled */ int2status = IntStatus2 & IntEnable2; if (!port || !port->gs.tty) { restore_flags(flags); return; } /* Get interrupts in easy to use form */ ints = int2status >> intshift; /* Clear any interrupts we might be about to handle */ IntClear2 = int2status & ( (INTTYPE(UART_TX_INT) | INTTYPE(UART_EMPTY_INT) | INTTYPE(UART_TXOVERRUN_INT)) << intshift); /* TX holding register empty, so transmit byte (non-DMA) */ if (ints & (INTTYPE(UART_TX_INT) | INTTYPE(UART_EMPTY_INT))) { transmit_char_pio(port); } /* TX Transmit Holding Register Overrun (shouldn't happen) */ if (ints & INTTYPE(UART_TXOVERRUN_INT)) { printk ( "rs: TX overrun\n"); } /* check_modem_status(); */ restore_flags(flags); rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "end.\n");}static void rs_rx_interrupt_uarta(int irq, void *dev_id, struct pt_regs * regs){ rs_rx_interrupt(irq, dev_id, regs, UARTA_SHIFT);}static void rs_tx_interrupt_uarta(int irq, void *dev_id, struct pt_regs * regs){ rs_tx_interrupt(irq, dev_id, regs, UARTA_SHIFT);}/* *********************************************************************** * Here are the routines that actually * * interface with the generic_serial driver * *********************************************************************** */static void rs_disable_tx_interrupts (void * ptr) { struct rs_port *port = ptr; unsigned long flags; save_and_cli(flags); port->gs.flags &= ~GS_TX_INTEN; IntEnable2 &= ~((INTTYPE(UART_TX_INT) | INTTYPE(UART_EMPTY_INT) | INTTYPE(UART_TXOVERRUN_INT)) << port->intshift); IntClear2 = (INTTYPE(UART_TX_INT) | INTTYPE(UART_EMPTY_INT) | INTTYPE(UART_TXOVERRUN_INT)) << port->intshift; restore_flags(flags);}static void rs_enable_tx_interrupts (void * ptr) { struct rs_port *port = ptr; unsigned long flags; save_and_cli(flags); IntClear2 = (INTTYPE(UART_TX_INT) | INTTYPE(UART_EMPTY_INT) | INTTYPE(UART_TXOVERRUN_INT)) << port->intshift; IntEnable2 |= (INTTYPE(UART_TX_INT) | INTTYPE(UART_EMPTY_INT) | INTTYPE(UART_TXOVERRUN_INT)) << port->intshift; /* Send a char to start TX interrupts happening */ transmit_char_pio(port); restore_flags(flags);}static void rs_disable_rx_interrupts (void * ptr) { struct rs_port *port = ptr; unsigned long flags; save_and_cli(flags); IntEnable2 &= ~((INTTYPE(UART_RX_INT) | INTTYPE(UART_RXOVERRUN_INT) | INTTYPE(UART_FRAMEERR_INT) | INTTYPE(UART_BREAK_INT) | INTTYPE(UART_PARITYERR_INT)) << port->intshift); IntClear2 = (INTTYPE(UART_RX_INT) | INTTYPE(UART_RXOVERRUN_INT) | INTTYPE(UART_FRAMEERR_INT) | INTTYPE(UART_BREAK_INT) | INTTYPE(UART_PARITYERR_INT)) << port->intshift; restore_flags(flags);}static void rs_enable_rx_interrupts (void * ptr) { struct rs_port *port = ptr; unsigned long flags; save_and_cli(flags); IntEnable2 |= (INTTYPE(UART_RX_INT) | INTTYPE(UART_RXOVERRUN_INT) | INTTYPE(UART_FRAMEERR_INT) | INTTYPE(UART_BREAK_INT) | INTTYPE(UART_PARITYERR_INT)) << port->intshift; /* Empty the input buffer - apparently this is *vital* */ while (inl(port->base + TX3912_UART_CTRL1) & UART_RX_HOLD_FULL) { inb(port->base + TX3912_UART_DATA); } IntClear2 = (INTTYPE(UART_RX_INT) | INTTYPE(UART_RXOVERRUN_INT) | INTTYPE(UART_FRAMEERR_INT) | INTTYPE(UART_BREAK_INT) | INTTYPE(UART_PARITYERR_INT)) << port->intshift; restore_flags(flags);}static int rs_get_CD (void * ptr) { /* No Carried Detect in Hardware - just return true */ func_exit(); return (1);}static void rs_shutdown_port (void * ptr) { struct rs_port *port = ptr; func_enter(); port->gs.flags &= ~GS_ACTIVE; func_exit();}static int rs_set_real_termios (void *ptr){ struct rs_port *port = ptr; int t; switch (port->gs.baud) { /* Save some typing work... */#define e(x) case x:t= TX3912_UART_CTRL2_B ## x ; break e(300);e(600);e(1200);e(2400);e(4800);e(9600); e(19200);e(38400);e(57600);e(76800);e(115200);e(230400); case 0 :t = -1; break; default: /* Can I return "invalid"? */ t = TX3912_UART_CTRL2_B9600; printk (KERN_INFO "rs: unsupported baud rate: %d.\n", port->gs.baud); break; }#undef e if (t >= 0) { /* Jim: Set Hardware Baud rate - there is some good code in drivers/char/serial.c */ /* Program hardware for parity, data bits, stop bits (note: these are hardcoded to 8N1 */ UartA_Ctrl1 &= 0xf000000f; UartA_Ctrl1 &= ~(UART_DIS_TXD | SER_SEVEN_BIT | SER_EVEN_PARITY | SER_TWO_STOP);#define CFLAG port->gs.tty->termios->c_cflag if (C_PARENB(port->gs.tty)) { if (!C_PARODD(port->gs.tty)) UartA_Ctrl1 |= SER_EVEN_PARITY; else UartA_Ctrl1 |= SER_ODD_PARITY; } if ((CFLAG & CSIZE)==CS6) printk(KERN_ERR "6 bits not supported\n"); if ((CFLAG & CSIZE)==CS5) printk(KERN_ERR "5 bits not supported\n"); if ((CFLAG & CSIZE)==CS7) UartA_Ctrl1 |= SER_SEVEN_BIT; if (C_CSTOPB(port->gs.tty)) UartA_Ctrl1 |= SER_TWO_STOP; outl(t, port->base + TX3912_UART_CTRL2); outl(0, port->base + TX3912_UART_DMA_CTRL1); outl(0, port->base + TX3912_UART_DMA_CTRL2); UartA_Ctrl1 |= TX3912_UART_CTRL1_UARTON; /* wait until UARTA is stable */ while (~UartA_Ctrl1 & TX3912_UART_CTRL1_UARTON); } func_exit (); return 0;}static int rs_chars_in_buffer (void * ptr) { struct rs_port *port = ptr; int scratch; scratch = inl(port->base + TX3912_UART_CTRL1); return ((scratch & UART_TX_EMPTY) ? 0 : 1);}/* ********************************************************************** * * Here are the routines that actually * * interface with the rest of the system * * ********************************************************************** */static int rs_open (struct tty_struct * tty, struct file * filp)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -