📄 sh-sci.c
字号:
/* $Id: sh-sci.c,v 1.40 2000/04/15 06:57:29 gniibe Exp $ * * linux/drivers/char/sh-sci.c * * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) * Copyright (C) 1999, 2000 Niibe Yutaka * Copyright (C) 2000 Sugioka Toshinobu * Modified to support multiple serial ports. Stuart Menefy (May 2000). * * TTY code is based on sx.c (Specialix SX driver) by: * * (C) 1998 R.E.Wolff@BitWizard.nl * */#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/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/malloc.h>#include <linux/init.h>#include <linux/delay.h>#ifdef CONFIG_SERIAL_CONSOLE#include <linux/console.h>#endif#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <linux/generic_serial.h>#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB#include <asm/sh_bios.h>#endif#include "sh-sci.h"#ifdef CONFIG_SERIAL_CONSOLEstatic struct console sercons;static struct sci_port* sercons_port=0;static int sercons_baud;#endif/* Function prototypes */static void sci_init_pins_sci(struct sci_port* port, unsigned int cflag);#ifndef SCI_ONLYstatic void sci_init_pins_scif(struct sci_port* port, unsigned int cflag);#if defined(__sh3__)static void sci_init_pins_irda(struct sci_port* port, unsigned int cflag);#endif#endifstatic void sci_disable_tx_interrupts(void *ptr);static void sci_enable_tx_interrupts(void *ptr);static void sci_disable_rx_interrupts(void *ptr);static void sci_enable_rx_interrupts(void *ptr);static int sci_get_CD(void *ptr);static void sci_shutdown_port(void *ptr);static int sci_set_real_termios(void *ptr);static void sci_hungup(void *ptr);static void sci_close(void *ptr);static int sci_chars_in_buffer(void *ptr);static int sci_init_drivers(void);static struct tty_driver sci_driver, sci_callout_driver;static struct sci_port sci_ports[SCI_NPORTS] = SCI_INIT;static struct tty_struct *sci_table[SCI_NPORTS] = { NULL, };static struct termios *sci_termios[SCI_NPORTS];static struct termios *sci_termios_locked[SCI_NPORTS];int sci_refcount;int sci_debug = 0;#ifdef MODULEMODULE_PARM(sci_debug, "i");#endif#define dprintk(x...) do { if (sci_debug) printk(x); } while(0)static void put_char(struct sci_port *port, char c){ unsigned long flags; unsigned short status; save_and_cli(flags); do status = sci_in(port, SCxSR); while (!(status & SCxSR_TDxE(port))); sci_out(port, SCxTDR, c); sci_in(port, SCxSR); /* Dummy read */ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); restore_flags(flags);}#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUBstatic void handle_error(struct sci_port *port){ /* Clear error flags */ sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));}static int get_char(struct sci_port *port){ unsigned long flags; unsigned short status; int c; save_and_cli(flags); do { status = sci_in(port, SCxSR); if (status & SCxSR_ERRORS(port)) { handle_error(port); continue; } } while (!(status & SCxSR_RDxF(port))); c = sci_in(port, SCxRDR); sci_in(port, SCxSR); /* Dummy read */ sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); restore_flags(flags); return c;}/* Taken from sh-stub.c of GDB 4.18 */static const char hexchars[] = "0123456789abcdef";static __inline__ char highhex(int x){ return hexchars[(x >> 4) & 0xf];}static __inline__ char lowhex(int x){ return hexchars[x & 0xf];}#endif/* * Send the packet in buffer. The host gets one chance to read it. * This routine does not wait for a positive acknowledge. */static void put_string(struct sci_port *port, const char *buffer, int count){ int i; const unsigned char *p = buffer;#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB int checksum; /* This call only does a trap the first time it is * called, and so is safe to do here unconditionally */ if (sh_bios_in_gdb_mode()) { /* $<packet info>#<checksum>. */ do { unsigned char c; put_char(port, '$'); put_char(port, 'O'); /* 'O'utput to console */ checksum = 'O'; for (i=0; i<count; i++) { /* Don't use run length encoding */ int h, l; c = *p++; h = highhex(c); l = lowhex(c); put_char(port, h); put_char(port, l); checksum += h + l; } put_char(port, '#'); put_char(port, highhex(checksum)); put_char(port, lowhex(checksum)); } while (get_char(port) != '+'); } else#endif for (i=0; i<count; i++) { if (*p == 10) put_char(port, '\r'); put_char(port, *p++); }}static struct real_driver sci_real_driver = { sci_disable_tx_interrupts, sci_enable_tx_interrupts, sci_disable_rx_interrupts, sci_enable_rx_interrupts, sci_get_CD, sci_shutdown_port, sci_set_real_termios, sci_chars_in_buffer, sci_close, sci_hungup, NULL};#if defined(SCI_ONLY) || defined(SCI_AND_SCIF)static void sci_init_pins_sci(struct sci_port* port, unsigned int cflag){}#endif#if defined(SCIF_ONLY) || defined(SCI_AND_SCIF)#if defined(__sh3__)/* For SH7707, SH7709, SH7709A, SH7729 */static void sci_init_pins_scif(struct sci_port* port, unsigned int cflag){ unsigned int fcr_val = 0; { unsigned short data; /* We need to set SCPCR to enable RTS/CTS */ data = ctrl_inw(SCPCR); /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ ctrl_outw(data&0x0fcf, SCPCR); } if (cflag & CRTSCTS) fcr_val |= SCFCR_MCE; else { unsigned short data; /* We need to set SCPCR to enable RTS/CTS */ data = ctrl_inw(SCPCR); /* Clear out SCP7MD1,0, SCP4MD1,0, Set SCP6MD1,0 = {01} (output) */ ctrl_outw((data&0x0fcf)|0x1000, SCPCR); data = ctrl_inb(SCPDR); /* Set /RTS2 (bit6) = 0 */ ctrl_outb(data&0xbf, SCPDR); } sci_out(port, SCFCR, fcr_val);}static void sci_init_pins_irda(struct sci_port* port, unsigned int cflag){ unsigned int fcr_val = 0; if (cflag & CRTSCTS) fcr_val |= SCFCR_MCE; sci_out(port, SCFCR, fcr_val);}#else/* For SH7750 */static void sci_init_pins_scif(struct sci_port* port, unsigned int cflag){ unsigned int fcr_val = 0; if (cflag & CRTSCTS) { fcr_val |= SCFCR_MCE; } else { ctrl_outw(0x0080, SCSPTR2); /* Set RTS = 1 */ } sci_out(port, SCFCR, fcr_val);}#endif#endif /* SCIF_ONLY || SCI_AND_SCIF */static void sci_setsignals(struct sci_port *port, int dtr, int rts){ /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ /* If you have signals for DTR and DCD, please implement here. */ ;}static int sci_getsignals(struct sci_port *port){ /* This routine is used for geting signals of: DTR, DCD, DSR, RI, and CTS/RTS */ return TIOCM_DTR|TIOCM_RTS|TIOCM_DSR;/* (((o_stat & OP_DTR)?TIOCM_DTR:0) | ((o_stat & OP_RTS)?TIOCM_RTS:0) | ((i_stat & IP_CTS)?TIOCM_CTS:0) | ((i_stat & IP_DCD)?TIOCM_CAR:0) | ((i_stat & IP_DSR)?TIOCM_DSR:0) | ((i_stat & IP_RI) ?TIOCM_RNG:0)*/}static void sci_set_baud(struct sci_port *port, int baud){ int t; switch (baud) { case 0: t = -1; break; case 2400: t = BPS_2400; break; case 4800: t = BPS_4800; break; case 9600: t = BPS_9600; break; case 19200: t = BPS_19200; break; case 38400: t = BPS_38400; break; case 57600: t = BPS_57600; break; default: printk(KERN_INFO "sci: unsupported baud rate: %d, using 115200 instead.\n", baud); case 115200: t = BPS_115200; break; } if (t > 0) { sci_setsignals (port, 1, -1); if(t >= 256) { sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); t >>= 2; } else { sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); } sci_out(port, SCBRR, t); udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ } else { sci_setsignals (port, 0, -1); }}static void sci_set_termios_cflag(struct sci_port *port, int cflag, int baud){ unsigned int status; unsigned int smr_val; do status = sci_in(port, SCxSR); while (!(status & SCxSR_TEND(port))); sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ if (port->type == PORT_SCIF) { sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); } smr_val = sci_in(port, SCSMR) & 3; if ((cflag & CSIZE) == CS7) smr_val |= 0x40; if (cflag & PARENB) smr_val |= 0x20; if (cflag & PARODD) smr_val |= 0x10; if (cflag & CSTOPB) smr_val |= 0x08; sci_out(port, SCSMR, smr_val); sci_set_baud(port, baud); port->init_pins(port, cflag); sci_out(port, SCSCR, SCSCR_INIT(port));}static int sci_set_real_termios(void *ptr){ struct sci_port *port = ptr; if (port->old_cflag != port->gs.tty->termios->c_cflag) { port->old_cflag = port->gs.tty->termios->c_cflag; sci_set_termios_cflag(port, port->old_cflag, port->gs.baud); sci_enable_rx_interrupts(port); } /* Tell line discipline whether we will do input cooking */ if (I_OTHER(port->gs.tty)) clear_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); else set_bit(TTY_HW_COOK_IN, &port->gs.tty->flags);/* Tell line discipline whether we will do output cooking. * If OPOST is set and no other output flags are set then we can do output * processing. Even if only *one* other flag in the O_OTHER group is set * we do cooking in software. */ if (O_OPOST(port->gs.tty) && !O_OTHER(port->gs.tty)) set_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); else clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); return 0;}/* ********************************************************************** * * the interrupt related routines * * ********************************************************************** *//* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -