📄 mx21_16c2552.c
字号:
/*
* 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 + -