⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serial_s3c44b0x.c

📁 S3C44B0x串行口驱动,支持两个UART口
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * serial_s3c44b0x.c: Serial port driver for the Samsung S3C44B0X builtin UART * * Copyright (c) 2007 NSY Technologies *      by Jesse Jiang <jiangxiaogen@sina.com> * * Copyright (c) 2004	sympat GmbH *			by Michael Frommberger <michael.frommberger@sympat.de> * * Copyright (c) 2003	sympat GmbH *			by Thomas Eschenbacher <thomas.eschenbacher@gmx.de> * * Copyright (c) 2001	Arcturus Networks Inc.  * 			by Oleksandr Zhadan  <oleks@arcturusnetworks.com> * Copyright (c) 2002	Arcturus Networks Inc.  * 		       	by Michael Leslie    <mleslie@arcturusnetworks.com> * * Based on: drivers/char/trioserial.c * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>, *                     D. Jeff Dionne <jeff@arcturusnetworks.com>, *                     The Silver Hammer Group, Ltd. * * Based on: drivers/char/68328serial.c * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * * 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. * */#include <linux/config.h>#include <linux/version.h>#include <linux/types.h>#include <linux/serial.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/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/kernel.h>#include <linux/console.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/arch/irq.h>#include <asm/system.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/delay.h>#include <asm/hardware.h>#define queue_task_irq_off queue_task#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)#include <asm/uaccess.h>#include "serial_s3c44b0x.h"#define USART_CNT		2					/* Receive FIFO trigger level	*/#define RX_FIFO_LEVEL		4	/*  0 (off) 4 or 8 or 12 or 16	*/					/* Transfer FIFO trigger level	*/#define TX_FIFO_LEVEL		0	/*	0 or 4 or 8 or 12	*/#define TX_FIFO_DEPTH		(5) 	/* maximum transmit FIFO length */#define RX_FIFO_DEPTH		(8) 	/* maximum receive FIFO length  *//* 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/* 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#undef SERIAL_DEBUG_SPEED#undef SERIAL_DEBUG_THROTTLE#define _INLINE_ inline#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endifstatic struct s3c44b0x_serial s3c44b0x_info[USART_CNT];/* * 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[SERIAL_XMIT_SIZE];	/* This is cheating */static DECLARE_MUTEX (tmp_buf_sem);/* Console hooks... */#if defined(CONFIG_SERIAL_S3C44B0X_CONSOLE)static int console_number =  0;#endif	DECLARE_TASK_QUEUE(tq_s3c44b0x_serial);static struct tty_driver serial_driver, callout_driver;static int serial_refcount;static struct tty_struct 	*serial_table[USART_CNT];static struct termios 		*serial_termios[USART_CNT];static struct termios		*serial_termios_locked[USART_CNT];static void change_speed	(struct s3c44b0x_serial *info);static _INLINE_ void rx_enable	(struct s3c44b0x_serial *info);static _INLINE_ void rx_disable	(struct s3c44b0x_serial *info);static _INLINE_ void tx_enable	(struct s3c44b0x_serial *info);static _INLINE_ void tx_disable	(struct s3c44b0x_serial *info);static _INLINE_ void wait_tx_empty(struct s3c44b0x_serial *info, 				   int with_scheduling);static _INLINE_ void tx_stop	(struct s3c44b0x_serial *info);static _INLINE_ void tx_start	(struct s3c44b0x_serial *info);static _INLINE_ void rx_stop	(struct s3c44b0x_serial *info);static _INLINE_ void rx_start	(struct s3c44b0x_serial *info);static _INLINE_ void xmit_char	(struct s3c44b0x_serial *info, char ch);static _INLINE_ void rs_s3c44b0x_sched_event \   				(struct s3c44b0x_serial *info, int event);static _INLINE_ void uart_speed	(struct s3c44b0x_serial *info, 				 unsigned int cflag);static _INLINE_ void handle_status	(struct s3c44b0x_serial *info, 					 unsigned int status);static _INLINE_ void fifo_reset	(struct s3c44b0x_serial *info);static _INLINE_ void fifo_init	(struct s3c44b0x_serial *info);static void set_ints_mode	(struct s3c44b0x_serial *info, int yes);static void rs_s3c44b0x_interruptTxa	(int irq, void *dev_id, struct pt_regs *regs);static void rs_s3c44b0x_interruptTxb	(int irq, void *dev_id, struct pt_regs *regs);static void rs_s3c44b0x_interruptTx	(int irq, void *dev_id, struct pt_regs *regs, 					 struct s3c44b0x_serial *serial_info);static void rs_s3c44b0x_interruptRxa	(int irq, void *dev_id, struct pt_regs *regs);static void rs_s3c44b0x_interruptRxb	(int irq, void *dev_id, struct pt_regs *regs);static void rs_s3c44b0x_interruptRx	(int irq, void *dev_id, struct pt_regs *regs, 					 struct s3c44b0x_serial *serial_info);static void rs_s3c44b0x_interruptErr	(int irq, void *dev_id, struct pt_regs *regs);extern void show_net_buffers	(void);extern void hard_reset_now	(void);static void handle_termios_tcsets(struct termios *ptermios, struct s3c44b0x_serial *pinfo);/******************************************************************************/static _INLINE_ void rx_enable(struct s3c44b0x_serial *info){	int ucon, ucon_old, ufcon;	/* enable rx */	ucon = ucon_old = inl(S3C44B0X_UCON0 + info->uart_offset);	ucon |= S3C44B0X_UCON_RX_MODE_INT_POLL;	outl(ucon, S3C44B0X_UCON0 + info->uart_offset);	/* flush fifo */	if (ucon_old != ucon){		ufcon = inl(S3C44B0X_UFCON0 + info->uart_offset);		ufcon |= S3C44B0X_UFCON_RX_FIFO_RST;		outl(ufcon, S3C44B0X_UFCON0 + info->uart_offset);	}}static _INLINE_ void rx_disable(struct s3c44b0x_serial *info){	int ucon, ucon_rx;	/* disable rx  */	ucon = ucon_rx = inl(S3C44B0X_UCON0 + info->uart_offset);	ucon_rx &= (S3C44B0X_UCON_RX_MASK & S3C44B0X_UCON_RX_DIS);	ucon &= ~S3C44B0X_UCON_RX_MASK;	ucon |= ucon_rx;	outl(ucon, S3C44B0X_UCON0 + info->uart_offset);}static _INLINE_ void tx_enable(struct s3c44b0x_serial *info){  char umcon = inb(S3C44B0X_UMCON0 + info->uart_offset);    	umcon &= ~S3C44B0X_UMCON_AFC;     /* disable auto flow-control */	umcon |= S3C44B0X_UMCON_RQST_SEND;	outb(umcon, S3C44B0X_UMCON0 + info->uart_offset);}static _INLINE_ void tx_disable(struct s3c44b0x_serial *info){	char umcon;		/* wait until data is sent */	wait_tx_empty(info, 0);		/* disabel tx */	umcon = inb(S3C44B0X_UMCON0 + info->uart_offset);	umcon &= ~S3C44B0X_UMCON_AFC;        /* disable auto flow-control */	umcon &= ~S3C44B0X_UMCON_RQST_SEND;  /* Inactivate nRTS */	outb(umcon, S3C44B0X_UMCON0 + info->uart_offset);}static _INLINE_ void wait_tx_empty(struct s3c44b0x_serial *info, 				   int with_scheduling){	char ustat, ufstat;		ustat = inb(S3C44B0X_UTRSTAT0 + info->uart_offset);	ufstat = inb(S3C44B0X_UFSTAT0 + info->uart_offset);	/* wait until data is sent */	do{		if (with_scheduling)			yield();		ustat = inb(S3C44B0X_UTRSTAT0 + info->uart_offset);		ufstat = inb(S3C44B0X_UFSTAT0 + info->uart_offset);	}while(!(ustat & S3C44B0X_UTRSTAT_TSE) || 		(ufstat & S3C44B0X_UFSTAT_TX_FIFO_COUNT));}/******************************************************************************/static _INLINE_ void tx_delay(void){#if 0	volatile int i;	for (i=0; i < 50; i++) {		i = i+1;		i = i-1;	};#endif}static _INLINE_ void tx_start(struct s3c44b0x_serial *info){	if (info->use_ints && info->xmit_cnt) 		enable_irq(info->irq);}static _INLINE_ void tx_stop(struct s3c44b0x_serial *info){	disable_irq(info->irq);}static _INLINE_ void rx_start(struct s3c44b0x_serial *info){	if (info->use_ints) {		enable_irq(info->irq_rx);		enable_irq(S3C44B0X_INTERRUPT_UERR);	}}static _INLINE_ void rx_stop(struct s3c44b0x_serial *info){	disable_irq(info->irq_rx);	/* the error-interrupt is shared -> check if the other uart needs it */	if (((s3c44b0x_info[0].count == 1) && (s3c44b0x_info[1].count == 0)) ||	    ((s3c44b0x_info[0].count == 0) && (s3c44b0x_info[1].count == 1)) ||	    ((s3c44b0x_info[0].count == 0) && (s3c44b0x_info[1].count == 0)))		disable_irq(S3C44B0X_INTERRUPT_UERR);}static void set_ints_mode(struct s3c44b0x_serial *info, int yes){	info->use_ints = yes;	if (yes) 	{		s3c44b0x_unmask_irq(info->irq);		s3c44b0x_unmask_irq(info->irq_rx);		s3c44b0x_unmask_irq(S3C44B0X_INTERRUPT_UERR);	} 	else 	{	        /* the error-interrupt is shared -> check if the other uart needs it */		if (((s3c44b0x_info[0].count == 1) && (s3c44b0x_info[1].count == 0)) ||		    ((s3c44b0x_info[0].count == 0) && (s3c44b0x_info[1].count == 1)) ||		    ((s3c44b0x_info[0].count == 0) && (s3c44b0x_info[1].count == 0)))			s3c44b0x_mask_irq(S3C44B0X_INTERRUPT_UERR);		s3c44b0x_mask_irq(info->irq_rx);		s3c44b0x_mask_irq(info->irq);	}}static _INLINE_ void fifo_reset(struct s3c44b0x_serial *info){	u_int8_t ufcon = inb(S3C44B0X_UFCON0 + info->uart_offset);	ufcon |= S3C44B0X_UFCON_RX_FIFO_RST | S3C44B0X_UFCON_TX_FIFO_RST;	outb(ufcon, S3C44B0X_UFCON0 + info->uart_offset);}static _INLINE_ void fifo_init(struct s3c44b0x_serial *info){	u_int8_t ufcon = inb(S3C44B0X_UFCON0 + info->uart_offset);	if (RX_FIFO_LEVEL + TX_FIFO_LEVEL)		ufcon |= S3C44B0X_UFCON_FIFO_EN                      | S3C44B0X_UCON_TXINT_LEVEL                      | S3C44B0X_UCON_RXINT_LEVEL;	switch (RX_FIFO_LEVEL) {		case 4 :	ufcon |= S3C44B0X_UFCON_RX_FIFO_4; break;		case 8 :	ufcon |= S3C44B0X_UFCON_RX_FIFO_8; break;		case 12:	ufcon |= S3C44B0X_UFCON_RX_FIFO_12; break;		case 16:	ufcon |= S3C44B0X_UFCON_RX_FIFO_16; break;	}	switch (TX_FIFO_LEVEL) {		case 0 :	ufcon |= S3C44B0X_UFCON_TX_FIFO_0; break;		case 4 :	ufcon |= S3C44B0X_UFCON_TX_FIFO_4; break;		case 8 :	ufcon |= S3C44B0X_UFCON_TX_FIFO_8; break;		case 12 :	ufcon |= S3C44B0X_UFCON_TX_FIFO_12; break;	}	outb(ufcon, S3C44B0X_UFCON0 + info->uart_offset);}static inline int serial_paranoia_check(struct s3c44b0x_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 s3c44b0x_serial struct 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;}/* Sets or clears DTR on the requested line */static inline void set_dtr(struct s3c44b0x_serial *info, int set){	/* we do not need hw flow control */}/* Sets or clears RTS on the requested line *//* Note: rts is only directly controllable if hw flow * control is not enabled */static inline void set_rts(struct s3c44b0x_serial *info, int set){	/* we do not need hw flow control */}/* Reads value of serial status signals */static unsigned int get_status(struct s3c44b0x_serial *info){	/* we do not need hw flow control */	return 0;}/* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void rs_stop(struct tty_struct *tty){    struct s3c44b0x_serial *info = (struct s3c44b0x_serial *) tty->driver_data;    unsigned long flags = 0;    if	(serial_paranoia_check(info, tty->device, "rs_stop"))    	return;    save_flags(flags);    cli();    tx_stop(info);    rx_stop(info);    restore_flags(flags);}#if !defined(CONFIG_CONSOLE_NULL)static void rs_put_char(struct s3c44b0x_serial *info, char ch){    unsigned long flags = 0;    save_flags(flags);    cli();    xmit_char(info, ch);    restore_flags(flags);}#endifstatic void rs_start(struct tty_struct *tty){    struct s3c44b0x_serial *info = (struct s3c44b0x_serial *) tty->driver_data;    unsigned long flags = 0;    if  (serial_paranoia_check(info, tty->device, "rs_start"))			return;    save_flags(flags);    cli();    rx_start(info);    tx_start(info);    restore_flags(flags);}/* * ---------------------------------------------------------------------- * * 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 assembly 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 _INLINE_ void rs_s3c44b0x_sched_event(struct s3c44b0x_serial *info, int event){	info->event |= 1 << event;	queue_task_irq_off(&info->tqueue, &tq_s3c44b0x_serial);	mark_bh(SERIAL_BH);}/*   *	This is the serial driver's generic interrupt routine */ static void rs_s3c44b0x_interruptTxa(int irq, void *dev_id, struct pt_regs *regs){    rs_s3c44b0x_interruptTx(irq, dev_id, regs, &s3c44b0x_info[1]);}static void rs_s3c44b0x_interruptTxb(int irq, void *dev_id, struct pt_regs *regs){    rs_s3c44b0x_interruptTx(irq, dev_id, regs, &s3c44b0x_info[0]);}static void rs_s3c44b0x_interruptTx(int irq, void *dev_id, 				    struct pt_regs *regs,				    struct s3c44b0x_serial *serial_info){	unsigned int count, status;		struct s3c44b0x_serial *info = serial_info;		if  (info->x_char) {		xmit_char(info, info->x_char);		info->x_char = 0;		return;	}	if  ((info->xmit_cnt <= 0) || 	      info->tty->stopped || 	      info->tty->hw_stopped ) {		s3c44b0x_mask_ack_irq(info->irq);		tx_stop(info);		/* set the settings for receiving */		switch (info->ser_mode){		    	case SERIAL_MODE_RS422:		    	case SERIAL_MODE_RS422_LISTEN:				/* enable tx and rx */				tx_enable(info);				rx_enable(info);				break;				    			case SERIAL_MODE_RS485_ECHO:			case SERIAL_MODE_RS485_NO_ECHO:				/* disable tx and enable rx */				tx_disable(info);				rx_enable(info);				break;							case SERIAL_MODE_NONE:				/* disable tx and rx */				tx_disable(info);				rx_disable(info);				break;			default:				/* do nothing */				break;		}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -