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

📄 mx21_16c2552.c

📁 arm926e的mx21处理器的串口驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 *  linux/drivers/char/serial_mx1_ext_uart.c
 *
 *  Driver for AMBA serial ports - modified to work with 162552 UART controller
 *
 *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
 *
 *  Copyright 1999 ARM Limited
 *  Copyright (C) 2000 Deep Blue Solutions Ltd.
 *  Copyright (C) 2002 Motorola Semiconductors HK Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * This is a generic driver for ARM AMBA-type serial ports.  They
 * have a lot of 16550-like features, but are not register compatable.
 * Note that although they do have CTS, DCD and DSR inputs, they do
 * not have an RI input, nor do they have DTR or RTS outputs.  If
 * required, these have to be supplied via some other means (eg, GPIO)
 * and hooked into this driver.
 *
 * This could very easily become a generic serial driver for dumb UARTs
 * (eg, {82,16x}50, 21285, SA1100).
 */
 
#ifndef __KERNEL__
#  define __KERNEL__
#endif
#ifndef MODULE
#  define MODULE
#endif


#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.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/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>

#include <asm/arch/mx2.h>

#include "mx21_16c2552.h"

#define  CONFIG_MX1_EXT_UART       1

#define SERIAL_AMBA_NAME	"ttyAM0"
#define SERIAL_AMBA_MAJOR	204
#define SERIAL_AMBA_MINOR	16
// #define SERIAL_AMBA_NR		2
// MX1ADS
#define SERIAL_AMBA_NR		1

#define CALLOUT_AMBA_NAME	"cuaam"
#define CALLOUT_AMBA_MAJOR	205
#define CALLOUT_AMBA_MINOR	16
#define CALLOUT_AMBA_NR		SERIAL_AMBA_NR

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif



/*
 * Access routines for the AMBA UARTs
 */
/* Thomas Wong (2001/12/10)
 * We're going to rewrite all of the following macros to make the external
 * UART (16C2552) we used to behave in the same way as the AMBA UART.
 * A global variable uartTem (unsigned char) is created for tem storage.
 * To make the porting easier, we'll change some of the macros to function
 * calls when necessary.
 * Since we need to access some type definition in the routines, we move
 * the routines definition after all type definitions in this file.
 */

// tem storage for macros
unsigned char uartTem;

// macros for external UART operations
#define IO_READ(p)         (*(volatile unsigned char *)(p))
#define IO_WRITE(p, c)     (*(unsigned char *)(p) = (unsigned char)(c))
#define GET_STATUS(p)      (IO_READ((p) + AMBA_UARTFR) & 0xFF)
#define GET_CHAR(p)        (IO_READ((p) + AMBA_UARTDR))
#define PUT_CHAR(p, c)     (IO_WRITE(((p) + AMBA_UARTDR), (c)))
#define RX_DATA(s)         (((s) & AMBA_UARTFR_RXFE) == 0)
#define TX_READY(s)        (((s) & AMBA_UARTFR_TXFF) == 0)
#define TX_EMPTY(p)        ((GET_STATUS(p) & AMBA_UARTFR_TMSK) == 0)

// register offset definition for UART 16C2552
#define	UART16C2552_RHR	0x8
#define	UART16C2552_THR	0x8
#define	UART16C2552_IER	0x9
#define	UART16C2552_FCR	0xA
#define	UART16C2552_ISR	0xA
#define	UART16C2552_LCR	0xB
#define	UART16C2552_MCR	0xC
#define	UART16C2552_LSR	0xD
#define	UART16C2552_MSR	0xE
#define	UART16C2552_DLL	0x8
#define	UART16C2552_DLM	0x9

// macros for UART 16C2552 access
#define UART16C2552_GET_RHR(p)	IO_READ((p)->uart_base+UART16C2552_RHR)
#define UART16C2552_GET_IER(p)	IO_READ((p)->uart_base+UART16C2552_IER)
#define UART16C2552_GET_ISR(p)	IO_READ((p)->uart_base+UART16C2552_ISR)
#define UART16C2552_GET_LCR(p)	IO_READ((p)->uart_base+UART16C2552_LCR)
#define UART16C2552_GET_MCR(p)	IO_READ((p)->uart_base+UART16C2552_MCR)
#define UART16C2552_GET_LSR(p)	IO_READ((p)->uart_base+UART16C2552_LSR)
#define UART16C2552_GET_MSR(p)	IO_READ((p)->uart_base+UART16C2552_MSR)
#define UART16C2552_GET_DLL(p)	IO_READ((p)->uart_base+UART16C2552_DLL)
#define UART16C2552_GET_DLM(p)	IO_READ((p)->uart_base+UART16C2552_DLM)
#define UART16C2552_PUT_THR(p,c) IO_WRITE((p)->uart_base+UART16C2552_THR, (c))
#define UART16C2552_PUT_IER(p,c) IO_WRITE((p)->uart_base+UART16C2552_IER, (c))
#define UART16C2552_PUT_FCR(p,c) IO_WRITE((p)->uart_base+UART16C2552_FCR, (c))
#define UART16C2552_PUT_LCR(p,c) IO_WRITE((p)->uart_base+UART16C2552_LCR, (c))
#define UART16C2552_PUT_MCR(p,c) IO_WRITE((p)->uart_base+UART16C2552_MCR, (c))
#define UART16C2552_PUT_DLL(p,c) IO_WRITE((p)->uart_base+UART16C2552_DLL, (c))
#define UART16C2552_PUT_DLM(p,c) IO_WRITE((p)->uart_base+UART16C2552_DLM, (c))


static int st16c2552_gpio_init(void);
static int st16c2552_gpio_enable_inter(void);
static int st16c2552_gpio_disable_inter(void);
static int st16c2552_reset();
int __exit st16c2552_cleanup(void);

/*
 * Things needed by tty driver
 */
static struct tty_driver ambanormal_driver, ambacallout_driver;
static int ambauart_refcount;
static struct tty_struct *ambauart_table[SERIAL_AMBA_NR];
static struct termios *ambauart_termios[SERIAL_AMBA_NR];
static struct termios *ambauart_termios_locked[SERIAL_AMBA_NR];

//#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#if defined(CONFIG_MX1_EXT_UART) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif

/*
 * Things needed internally to this driver
 */

/*
 * tmp_buf is used as a temporary buffer by serial_write.  We need to
 * lock it in case the copy_from_user 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 u_char *tmp_buf;
static DECLARE_MUTEX(tmp_buf_sem);

#define HIGH_BITS_OFFSET	((sizeof(long)-sizeof(int))*8)

/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS		256
#define AMBA_ISR_PASS_LIMIT	256

#define EVT_WRITE_WAKEUP	0

#define MX21_EXT_UART_BASE      0xE3200000

struct amba_icount {
	__u32	cts;
	__u32	dsr;
	__u32	rng;
	__u32	dcd;
	__u32	rx;
	__u32	tx;
	__u32	frame;
	__u32	overrun;
	__u32	parity;
	__u32	brk;
	__u32	buf_overrun;
};

/*
 * Static information about the port
 */
struct amba_port {
	unsigned int		uart_base;
	unsigned int		irq;
	unsigned int		uartclk;
	unsigned int		fifosize;
	unsigned int		tiocm_support;
	void (*set_mctrl)(struct amba_port *, u_int mctrl);
};	

/*
 * This is the state information which is persistent across opens
 */
struct amba_state {
	struct amba_icount	icount;
	unsigned int		line;
	unsigned int		close_delay;
	unsigned int		closing_wait;
	unsigned int		custom_divisor;
	unsigned int		flags;
	struct termios		normal_termios;
	struct termios		callout_termios;

	int			count;
	struct amba_info	*info;
};

#define AMBA_XMIT_SIZE 1024
/*
 * This is the state information which is only valid when the port is open.
 */
struct amba_info {
	struct amba_port	*port;
	struct amba_state	*state;
	struct tty_struct	*tty;
	unsigned char		x_char;
	unsigned char		old_status;
	unsigned char		read_status_mask;
	unsigned char		ignore_status_mask;
	struct circ_buf		xmit;
	unsigned int		flags;
#ifdef SUPPORT_SYSRQ
	unsigned long		sysrq;
#endif

	unsigned int		event;
	unsigned int		timeout;
	unsigned int		lcr_h;
	unsigned int		mctrl;
	int			blocked_open;
	pid_t			session;
	pid_t			pgrp;

	struct tasklet_struct	tlet;

	wait_queue_head_t	open_wait;
	wait_queue_head_t	close_wait;
	wait_queue_head_t	delta_msr_wait;
};

//#ifdef CONFIG_SERIAL_AMBA_CONSOLE
#ifdef CONFIG_MX1_EXT_UART
static struct console ambauart_cons;
#endif
static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios);
static void ambauart_wait_until_sent(struct tty_struct *tty, int timeout);

#if 1 //def CONFIG_SERIAL_INTEGRATOR
static void amba_set_mctrl_null(struct amba_port *port, u_int mctrl)
{
}

// MX1ADS - there is only one UART port for console
static struct amba_port amba_ports[SERIAL_AMBA_NR] = {
	{
		uart_base:	MX21_EXT_UART_BASE,
		irq:		8,
		uartclk:	3686400,
		fifosize:	8,
		set_mctrl:	amba_set_mctrl_null,

	}
};
#endif

static struct amba_state amba_state[SERIAL_AMBA_NR];

// UART access routines
// #define UART_GET_INT_STATUS(p)   IO_READ((p)->uart_base + AMBA_UARTIIR)
// IIR (Interrupt Identification Register) bits:
// 7:4 - reserved
// 3 - RTIS (Receive Timeout Interrupt Status)
// 2 - TIS (Tx Interrupt Status)
// 1 - RIS (Rx Interrupt Status)
// 0 - MIS (Modem Interrupt Status)

unsigned char UART_GET_INT_STATUS(struct amba_port *p)
{
	unsigned char	rv, isr;

	isr = UART16C2552_GET_ISR(p) & 0xF;
	rv = 0;
	switch (isr)
	{
		case 6:		// LSR (not supposed to happen)
			break;
		case 4:		// Rx Interrupt
			rv = 2; 
			break;
		case 12:		// Rx Timeout
			rv = 8;
			break;
		case 2:		// Tx Interrupt
			rv = 4;
			break;
		case 0:		// Modem Interrupt
			rv = 1;
	}
	return rv;
}

// #define UART_PUT_ICR(p, c) IO_WRITE((p)->uart_base + AMBA_UARTICR, (c))
// ICR (Interrupt Clear Register)
// There is no ICR for 16C2552. Interrupts are cleared when ISR is read.

#define UART_PUT_ICR(p, c)		uartTem = 0;	// dummy write

// #define UART_GET_FR(p)     IO_READ((p)->uart_base + AMBA_UARTFR)
// FR (flag register) bits:
// 7 - Tx FIFO empty
// 6 - Rx FIFO full
// 5 - Tx FIFO full
// 4 - Rx FIFO empty
// 3 - UART busy
// 2 - DCD (Data Carrier Detect)
// 1 - DSR (Data Set Ready)
// 0 - CTS (Clear To Send)

unsigned char UART_GET_FR(struct amba_port *p)
{
	unsigned char rv, lsr, msr, flag;

	lsr = UART16C2552_GET_LSR(p);
	msr = UART16C2552_GET_MSR(p);
	flag = (lsr & 0x40) >> 6;		// "THR & TSR empty"
	rv = (flag << 7) | ((!flag) << 5) | ((!flag) << 3);
	flag = lsr & 1;					// "receive data ready"
	rv |= ((flag << 6) | ((!flag) << 4));
	rv |= ((msr & 0x80) >> 5);		// "CD"
	rv |= ((msr & 0x20) >> 4);		// "DSR"
	rv |= ((msr & 0x10) >> 4);		// "CTS"
   return rv;
}

// #define UART_GET_CHAR(p)   IO_READ((p)->uart_base + AMBA_UARTDR)
#define UART_GET_CHAR(p)	UART16C2552_GET_RHR(p);

// #define UART_PUT_CHAR(p, c)   IO_WRITE((p)->uart_base + AMBA_UARTDR, (c))
#define UART_PUT_CHAR(p,c)	UART16C2552_PUT_THR(p,c); 

// #define UART_GET_RSR(p)    IO_READ((p)->uart_base + AMBA_UARTRSR)
// RSR (Receive Status Register) bits:
// 7:4 - reserved
// 3 - OE (Overrun Error)
// 2 - BE (Break Error)
// 1 - PE (Parity Error)
// 0 - FE (Framing Error)

unsigned char UART_GET_RSR(struct amba_port *p)
{
	unsigned char rv, lsr;

	lsr = UART16C2552_GET_LSR(p);
	rv = (lsr & 2) << 2;				// OE
	rv |= ((lsr & 0x10) >> 2);		// BE
	rv |= ((lsr & 4) >> 1);			// PE
	rv |= ((lsr & 8) >> 3);			// FE
	return rv;
}

// #define UART_GET_CR(p)     IO_READ((p)->uart_base + AMBA_UARTCR)
// CR (Control Register) bits
// 7 - LBE (Loop Back Enable)
// 6 - RTIE (Rx Timeout Enable)
// 5 - TIE (Tx Interrupt Enable)
// 4 - RIE (Rx Interrupt Enable)
// 3 - MSIE (Modem Status Interrupt Enable)
// 2 - SIRLP (IrDA SIR Low Power Mode), not supported
// 1 - SIREN (SIR Enable), not supported
// 0 - UARTEN (UART Enable), not supported (UART always enabled)

unsigned char UART_GET_CR(struct amba_port *p)
{
	unsigned char rv, mcr, ier;

	mcr = UART16C2552_GET_MCR(p);
	ier = UART16C2552_GET_IER(p);
	rv = ((mcr & 0x10) << 3) | 1;		// loop back, UARTEN always 1
	rv |= ((ier & 1) << 6);				// Rx Intr
	rv |= ((ier & 2) << 4);				// Tx Intr
	rv |= ((ier & 1) << 4);				// Rx Intr
	rv |= (ier & 8);						// Modem Status Intr
	return rv;
}

//#define UART_PUT_CR(p,c)   IO_WRITE((p)->uart_base + AMBA_UARTCR, (c))
// CR (Control Register) bits
// 7 - LBE (Loop Back Enable)
// 6 - RTIE (Rx Timeout Enable)
// 5 - TIE (Tx Interrupt Enable)
// 4 - RIE (Rx Interrupt Enable)
// 3 - MSIE (Modem Status Interrupt Enable)
// 2 - SIRLP (IrDA SIR Low Power Mode), not supported
// 1 - SIREN (SIR Enable), not supported
// 0 - UARTEN (UART Enable), not supported (UART always enabled)
void UART_PUT_CR(struct amba_port *p, char c)
{
	unsigned char val;

	val = (c & 0x80) >> 3;	// Loop Back
	UART16C2552_PUT_MCR(p,(UART16C2552_GET_MCR(p) & 0xEF) | val);
	// we are going to group Rx Intr, Tx Intr and Modem Intr into one value
	val = ((c & 0x40) >> 6) | ((c & 0x10) >> 4);		// Rx Intr
	val |= ((c & 0x20) >> 4);	// Tx Intr
	val |= (c & 0x08);	// Modem Intr
	UART16C2552_PUT_IER(p,val);	// write to IER
}

// #define UART_GET_LCRL(p)   IO_READ((p)->uart_base + AMBA_UARTLCR_L)
unsigned char UART_GET_LCRL(struct amba_port *p)
{
	unsigned char 	lcr, rv;

⌨️ 快捷键说明

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