mirror of
https://github.com/sjlongland/adv950.git
synced 2025-09-14 03:03:15 +10:00
This is a check-in of the Advantech ADV950 serial driver, used with the UNO-1150 series of industrial computers, as sourced from: http://support.advantech.com/support/DownloadSRDetail_New.aspx?SR_ID=1-GLBRCD&Doc_Source=Download This builds with kernel 3.0.1, but does not build with contemporary kernels (i.e. ones that are still in support). I suspect this is a hacked up version of Russel King's 8250 serial driver from 2001. First step, let's get it building against the current kernel (v4.10.1) and then we'll look at integrating this into 8250 proper.
3179 lines
87 KiB
C
3179 lines
87 KiB
C
//******************************************************************************
|
|
//
|
|
// Copyright (c) 2011 Advantech Industrial Automation Group.
|
|
//
|
|
// Oxford PCI-954/952/16C950 with Advantech RS232/422/485 capacities
|
|
//
|
|
// 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.
|
|
//
|
|
//
|
|
//
|
|
//******************************************************************************
|
|
|
|
//***********************************************************************
|
|
// File: 8250.c
|
|
// Version: 3.29.1 [2009-06-03]
|
|
// Kernel: 2.6.x
|
|
// Author: Po-Cheng Chen
|
|
// Joshua Lan
|
|
// Devices:
|
|
// UNO: UNO-2050[COM3/COM4] UNO-2059[COM1~COM4] UNOB-2201CB[COM1~COM8]
|
|
// UNO-2176[COM1~COM4] UNO-1150[COM2/COM3] UNO-2679 [COM3~COM6]
|
|
// UNO-4672 [COM3~COM10]
|
|
// ICOM: PCI-1601 PCI-1602 PCI-1603 PCI-1604
|
|
// PCI-1610 PCI-1612 PCI-1620 PCI-1622
|
|
// MIC: MIC-3611 MIC-3612 MIC-3620 MIC-3621
|
|
// PCM: PCM-3614P/I PCM-3641P/I PCM-3618P/I PCM-3681P/I
|
|
// Change Log:
|
|
// <2004-11-29>
|
|
// - Moved code from 2.4.x
|
|
// <2004-12-02>
|
|
// - Fixed UNO-2050 function 1 offset typing error
|
|
// <2004-12-03>
|
|
// - Support FC3
|
|
// <2004-12-15>
|
|
// - Support UART types can be specified by each port
|
|
// to support some kind of devices did not fully
|
|
// implement with auto detection
|
|
// <2004-12-30>
|
|
// - Support serial port ioctl TIOCGSERIAL
|
|
// to get serial type and store at reserved_char[0]
|
|
// <2005-2-17>
|
|
// - Merge some new features in 2.4
|
|
// <2005-4-25>
|
|
// - Remove the folder of 'pclterm' from the package
|
|
// - Test in SuSE 9.2, Mandrake 10.1 and Debian 3.0r2
|
|
// <2005-5-31>
|
|
// - Add ICOM cards support
|
|
// PCI-1601A/B/AU/BU, PCI-1602A/B/AU/BU/UP,
|
|
// PCI-1603, PCI-1604UP and PCI-1622CU
|
|
// <2005-6-14>
|
|
// - Correct PCI-952 2nd port start base address(BAR1)
|
|
// <2005-6-22>
|
|
// - Fix 2.4 driver which miss to set auto DTR correctly
|
|
// <2005-6-24>
|
|
// - Add code to setup auto DTR after detection
|
|
// <2005-6-29>
|
|
// - Fix PCI-1622 ports on function 1 detection problem
|
|
// <2005-7-22>
|
|
// - Add support for UNOB-2201CB carrier board
|
|
// - Add MIC-3612 and MIC-3620 to pci_serial_quirks table
|
|
// - Change the value of UART_NR to 64
|
|
// <2005-8-9>
|
|
// - Fix UNOB-2201CB function 1 detection problem
|
|
// <2005-11-07>
|
|
// - Add support for MIC-3611
|
|
// <2006-2-16>
|
|
// - Add support for Mandriva Linux 2006
|
|
// <2006-2-28>
|
|
// - Add support for SUSE LINUX Enterprise Server 9
|
|
// <2006-5-24>
|
|
// - Add support for Debian 3.1r0a (kernel version 2.6.8-2-686)
|
|
// <2006-6-08>
|
|
// - Add support for SUSE LINUX 10.1 and Fedora Core 5
|
|
// <2006-10-11>
|
|
// - Add support for UNO-2176
|
|
// <2007-03-06>
|
|
// - Fix bug in low active type for 485 mode
|
|
// - Support Ubuntu 6.06(add UBUNTU_2_6_15) and RedHat Enterprise Server 4
|
|
// - Support quick response(use quick_respons utility to enable or disable this function)
|
|
// - Enable 16C950 trigger level and hardware and software flow control
|
|
// <2007-05-29>
|
|
// - Add support for PCM-3614P PCM-3641P PCM-3618P PCM-3681P
|
|
// <2007-09-14>
|
|
// - Support FC6
|
|
// <2007-09-30>
|
|
// - Fix bug in serial8250_get_mctrl
|
|
// <2008-02-13>
|
|
// - Fix bug about flow control
|
|
// <2008-06-20>
|
|
// - to support FC7 and FC8
|
|
// - to support UNO-1150
|
|
// - to support MIC-3621
|
|
// <2008-07-04>
|
|
// - to support Ubuntu 8.04
|
|
// <2008-10-08>
|
|
// - Source headers comply with GPL
|
|
// - modify the driver readme
|
|
// <2008-12-08>
|
|
// - to support UNO-2679, UNO-4672
|
|
// <2009-06-03> V3.29
|
|
// - to support debian 5.0
|
|
// - to support patch CONFIG_PREEMPT_RT kernel
|
|
// - to support device A001,A002,A004,A101,A102,A104
|
|
// - to support device F001,F002,F004,F101,F102,F104
|
|
// <2010-05-06> V3.30
|
|
// - to support FC10,FC11,FC12,SUSE 11.2 Madriva 2010
|
|
// - to support Advantech General COM port
|
|
// <2010-05-06> V3.32
|
|
// - to support FC14, FC14-updata, Debian 6, SUSE 11.3
|
|
// - to support device PCIe-952/4/8(have DMA)
|
|
// <2011-11-07> V3.33
|
|
// - to support 2.6.37,2.6.38,2.6.39,2.6.40 kenrel
|
|
//***********************************************************************
|
|
//***********************************************************************
|
|
//lipeng modify at 2007/09/07
|
|
#include <linux/version.h>
|
|
#ifndef KERNEL_VERSION
|
|
#define KERNEL_VERSION(a, b, c) KERNEL_VERSION((a)*65536+(b)*256+(c))
|
|
#endif
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
|
|
#include <linux/config.h>
|
|
#endif
|
|
//lipeng modify end
|
|
#include <linux/module.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/init.h>
|
|
#include <linux/console.h>
|
|
#include <linux/sysrq.h>
|
|
#include <linux/serial_reg.h>
|
|
#include <linux/serial.h>
|
|
#include <linux/serialP.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/irq.h>
|
|
|
|
#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
|
#define SUPPORT_SYSRQ
|
|
#endif
|
|
|
|
#include <linux/serial_core.h>
|
|
#include "8250.h"
|
|
#ifdef CONFIG_KDB
|
|
#include <linux/kdb.h>
|
|
static int kdb_serial_line = -1;
|
|
static const char *kdb_serial_ptr = kdb_serial_str;
|
|
#endif
|
|
/*
|
|
* Po-Cheng Chen
|
|
*/
|
|
#ifdef CONFIG_SERIAL_8250_RSA
|
|
#undef CONFIG_SERIAL_8250_RSA
|
|
#endif
|
|
#ifdef CONFIG_MCA
|
|
#undef CONFIG_MCA
|
|
#endif
|
|
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
|
#undef CONFIG_SERIAL_8250_CONSOLE
|
|
#endif
|
|
#ifndef SERIAL_NAME
|
|
#define SERIAL_NAME "ttyP"
|
|
#endif
|
|
|
|
int adv_uart_register_driver(struct uart_driver *uart);
|
|
void adv_uart_unregister_driver(struct uart_driver *uart);
|
|
void adv_uart_unregister_port(struct uart_driver *reg, int line);
|
|
int adv_uart_register_port(struct uart_driver *reg, struct uart_port *port);
|
|
int adv_uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
|
|
int adv_uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
|
|
int adv_uart_suspend_port(struct uart_driver *reg, struct uart_port *port);
|
|
int adv_uart_resume_port(struct uart_driver *reg, struct uart_port *port);
|
|
void adv_uart_write_wakeup(struct uart_port *port);
|
|
//lipeng add at 06/08/2006
|
|
//James.dai mask the line off and add a new one @V3.20 to support ubuntu6
|
|
//#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 14)
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 14) && !UBUNTU_2_6_15
|
|
void adv_uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
|
|
int adv_uart_get_baud_rate(struct uart_port *port, struct termios *termios, struct termios *old, unsigned int min, unsigned int max);
|
|
#else
|
|
unsigned int adv_uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, struct ktermios *old, unsigned int min, unsigned int max);
|
|
#endif
|
|
int adv_uart_get_divisor(struct uart_port *port, unsigned int baud);
|
|
#endif
|
|
//lipeng add end
|
|
// END, Po-Cheng Chen
|
|
|
|
/*
|
|
* Configuration:
|
|
* share_irqs - whether we pass SA_SHIRQ to request_irq(). This option
|
|
* is unsafe when used on edge-triggered interrupts.
|
|
*/
|
|
unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
|
|
|
|
/*
|
|
* Debugging.
|
|
*/
|
|
#if 0
|
|
#define DEBUG_AUTOCONF(fmt...) printk(fmt)
|
|
#else
|
|
#define DEBUG_AUTOCONF(fmt...) do { } while (0)
|
|
#endif
|
|
|
|
#if 0
|
|
#define DEBUG_INTR(fmt...) printk(fmt)
|
|
#else
|
|
#define DEBUG_INTR(fmt...) do { } while (0)
|
|
#endif
|
|
|
|
#define PASS_LIMIT 256
|
|
|
|
/*
|
|
* We default to IRQ0 for the "no irq" hack. Some
|
|
* machine types want others as well - they're free
|
|
* to redefine this in their header file.
|
|
*/
|
|
#define is_real_interrupt(irq) ((irq) != 0)
|
|
|
|
/*
|
|
* This converts from our new CONFIG_ symbols to the symbols
|
|
* that asm/serial.h expects. You _NEED_ to comment out the
|
|
* linux/config.h include contained inside asm/serial.h for
|
|
* this to work.
|
|
*/
|
|
#undef CONFIG_SERIAL_MANY_PORTS
|
|
#undef CONFIG_SERIAL_DETECT_IRQ
|
|
#undef CONFIG_SERIAL_MULTIPORT
|
|
#undef CONFIG_HUB6
|
|
|
|
#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
|
|
#define CONFIG_SERIAL_DETECT_IRQ 1
|
|
#endif
|
|
#ifdef CONFIG_SERIAL_8250_MULTIPORT
|
|
#define CONFIG_SERIAL_MULTIPORT 1
|
|
#endif
|
|
#ifdef CONFIG_SERIAL_8250_MANY_PORTS
|
|
#define CONFIG_SERIAL_MANY_PORTS 1
|
|
#endif
|
|
|
|
/*
|
|
* HUB6 is always on. This will be removed once the header
|
|
* files have been cleaned.
|
|
*/
|
|
#define CONFIG_HUB6 1
|
|
|
|
#include <asm/serial.h>
|
|
|
|
/*
|
|
* SERIAL_PORT_DFNS tells us about built-in ports that have no
|
|
* standard enumeration mechanism. Platforms that can find all
|
|
* serial ports via mechanisms like ACPI or PCI need not supply it.
|
|
*/
|
|
#ifndef SERIAL_PORT_DFNS
|
|
#define SERIAL_PORT_DFNS
|
|
#endif
|
|
|
|
static struct old_serial_port old_serial_port[] = {
|
|
SERIAL_PORT_DFNS /* defined in asm/serial.h */
|
|
};
|
|
/* Joshua Lan mask this line and modified UART_NR to 64 2005/07/21 */
|
|
/* In 2.6kernel, CONFIG_SERIAL_8250_NR_UARTS is 4 by default, which is limited*/
|
|
/* #define UART_NR (ARRAY_SIZE(old_serial_port) + CONFIG_SERIAL_8250_NR_UARTS) */
|
|
#define UART_NR 64
|
|
|
|
#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
|
|
|
|
#define PORT_RSA_MAX 4
|
|
static int probe_rsa[PORT_RSA_MAX];
|
|
static int force_rsa[PORT_RSA_MAX];
|
|
#endif /* CONFIG_SERIAL_8250_RSA */
|
|
|
|
|
|
struct irq_info {
|
|
spinlock_t lock;
|
|
struct list_head *head;
|
|
};
|
|
|
|
static struct irq_info irq_lists[NR_IRQS];
|
|
//WangMao add the line below to add quick response @v3_20
|
|
static int RTL;
|
|
extern int quick_response;
|
|
/*
|
|
* Here we define the default xmit fifo size used for each type of UART.
|
|
*/
|
|
/*
|
|
* modify by lipeng 06.02.28 ,from [PORT_MAX_8250+1] to [PORT_MAX_8250+2],PORT_MAX_8250 = 14 in sles 9(kernel 2.6.5-7.97-default)
|
|
*/
|
|
/*static*/ const struct serial_uart_config uart_config[PORT_MAX_8250+2] = {
|
|
{ "unknown", 1, 0 },
|
|
{ "8250", 1, 0 },
|
|
{ "16450", 1, 0 },
|
|
{ "16550", 1, 0 },
|
|
{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
|
|
{ "Cirrus", 1, 0 },
|
|
{ "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH },
|
|
{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
|
|
{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO },
|
|
{ "Startech", 1, 0 },
|
|
{ "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO },
|
|
//{ "16C950/954", 1, UART_CLEAR_FIFO | UART_STARTECH },
|
|
{ "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
|
|
{ "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
|
|
{ "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO },
|
|
{ "NS16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO | UART_NATSEMI },
|
|
{ "XScale", 32, UART_CLEAR_FIFO | UART_USE_FIFO }
|
|
};
|
|
|
|
static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
|
|
{
|
|
offset <<= up->port.regshift;
|
|
|
|
switch (up->port.iotype) {
|
|
case UPIO_HUB6:
|
|
outb(up->port.hub6 - 1 + offset, up->port.iobase);
|
|
return inb(up->port.iobase + 1);
|
|
|
|
case UPIO_MEM:
|
|
return readb(up->port.membase + offset);
|
|
|
|
default:
|
|
return inb(up->port.iobase + offset);
|
|
}
|
|
}
|
|
|
|
static _INLINE_ void
|
|
serial_out(struct uart_8250_port *up, int offset, int value)
|
|
{
|
|
offset <<= up->port.regshift;
|
|
|
|
switch (up->port.iotype) {
|
|
case UPIO_HUB6:
|
|
outb(up->port.hub6 - 1 + offset, up->port.iobase);
|
|
outb(value, up->port.iobase + 1);
|
|
break;
|
|
|
|
case UPIO_MEM:
|
|
writeb(value, up->port.membase + offset);
|
|
break;
|
|
|
|
default:
|
|
outb(value, up->port.iobase + offset);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We used to support using pause I/O for certain machines. We
|
|
* haven't supported this for a while, but just in case it's badly
|
|
* needed for certain old 386 machines, I've left these #define's
|
|
* in....
|
|
*/
|
|
#define serial_inp(up, offset) serial_in(up, offset)
|
|
#define serial_outp(up, offset, value) serial_out(up, offset, value)
|
|
|
|
|
|
/*
|
|
* For the 16C950
|
|
*/
|
|
static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
|
|
{
|
|
//WangMao add @V3.20
|
|
int LCRValue = serial_in(up,UART_LCR);
|
|
if(LCRValue==0xBF)
|
|
serial_out(up,UART_LCR,0x0);
|
|
serial_out(up, UART_SCR, offset);
|
|
serial_out(up, UART_ICR, value);
|
|
serial_out(up,UART_LCR,LCRValue);
|
|
}
|
|
|
|
static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
|
|
{
|
|
unsigned int value;
|
|
|
|
serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
|
|
serial_out(up, UART_SCR, offset);
|
|
value = serial_in(up, UART_ICR);
|
|
serial_icr_write(up, UART_ACR, up->acr);
|
|
|
|
return value;
|
|
}
|
|
|
|
#ifdef CONFIG_SERIAL_8250_RSA
|
|
/*
|
|
* Attempts to turn on the RSA FIFO. Returns zero on failure.
|
|
* We set the port uart clock rate if we succeed.
|
|
*/
|
|
static int __enable_rsa(struct uart_8250_port *up)
|
|
{
|
|
unsigned char mode;
|
|
int result;
|
|
|
|
mode = serial_inp(up, UART_RSA_MSR);
|
|
result = mode & UART_RSA_MSR_FIFO;
|
|
|
|
if (!result) {
|
|
serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
|
|
mode = serial_inp(up, UART_RSA_MSR);
|
|
result = mode & UART_RSA_MSR_FIFO;
|
|
}
|
|
|
|
if (result)
|
|
up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;
|
|
|
|
return result;
|
|
}
|
|
|
|
static void enable_rsa(struct uart_8250_port *up)
|
|
{
|
|
if (up->port.type == PORT_RSA) {
|
|
if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {
|
|
spin_lock_irq(&up->port.lock);
|
|
__enable_rsa(up);
|
|
spin_unlock_irq(&up->port.lock);
|
|
}
|
|
if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
|
|
serial_outp(up, UART_RSA_FRR, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Attempts to turn off the RSA FIFO. Returns zero on failure.
|
|
* It is unknown why interrupts were disabled in here. However,
|
|
* the caller is expected to preserve this behaviour by grabbing
|
|
* the spinlock before calling this function.
|
|
*/
|
|
static void disable_rsa(struct uart_8250_port *up)
|
|
{
|
|
unsigned char mode;
|
|
int result;
|
|
|
|
if (up->port.type == PORT_RSA &&
|
|
up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
|
|
spin_lock_irq(&up->port.lock);
|
|
|
|
mode = serial_inp(up, UART_RSA_MSR);
|
|
result = !(mode & UART_RSA_MSR_FIFO);
|
|
|
|
if (!result) {
|
|
serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
|
|
mode = serial_inp(up, UART_RSA_MSR);
|
|
result = !(mode & UART_RSA_MSR_FIFO);
|
|
}
|
|
|
|
if (result)
|
|
up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;
|
|
spin_unlock_irq(&up->port.lock);
|
|
}
|
|
}
|
|
#endif /* CONFIG_SERIAL_8250_RSA */
|
|
|
|
/*
|
|
* This is a quickie test to see how big the FIFO is.
|
|
* It doesn't work at all the time, more's the pity.
|
|
*/
|
|
static int size_fifo(struct uart_8250_port *up)
|
|
{
|
|
unsigned char old_fcr, old_mcr, old_dll, old_dlm;
|
|
int count;
|
|
|
|
old_fcr = serial_inp(up, UART_FCR);
|
|
old_mcr = serial_inp(up, UART_MCR);
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
|
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
|
|
serial_outp(up, UART_MCR, UART_MCR_LOOP);
|
|
serial_outp(up, UART_LCR, UART_LCR_DLAB);
|
|
old_dll = serial_inp(up, UART_DLL);
|
|
old_dlm = serial_inp(up, UART_DLM);
|
|
serial_outp(up, UART_DLL, 0x01);
|
|
serial_outp(up, UART_DLM, 0x00);
|
|
serial_outp(up, UART_LCR, 0x03);
|
|
for (count = 0; count < 256; count++)
|
|
serial_outp(up, UART_TX, count);
|
|
mdelay(20);/* FIXME - schedule_timeout */
|
|
for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) &&
|
|
(count < 256); count++)
|
|
serial_inp(up, UART_RX);
|
|
serial_outp(up, UART_FCR, old_fcr);
|
|
serial_outp(up, UART_MCR, old_mcr);
|
|
serial_outp(up, UART_LCR, UART_LCR_DLAB);
|
|
serial_outp(up, UART_DLL, old_dll);
|
|
serial_outp(up, UART_DLM, old_dlm);
|
|
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
* This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's.
|
|
* When this function is called we know it is at least a StarTech
|
|
* 16650 V2, but it might be one of several StarTech UARTs, or one of
|
|
* its clones. (We treat the broken original StarTech 16650 V1 as a
|
|
* 16550, and why not? Startech doesn't seem to even acknowledge its
|
|
* existence.)
|
|
*
|
|
* What evil have men's minds wrought...
|
|
*/
|
|
static void autoconfig_has_efr(struct uart_8250_port *up)
|
|
{
|
|
unsigned char id1, id2, id3, rev, saved_dll, saved_dlm;
|
|
|
|
/*
|
|
* First we check to see if it's an Oxford Semiconductor UART.
|
|
*
|
|
* If we have to do this here because some non-National
|
|
* Semiconductor clone chips lock up if you try writing to the
|
|
* LSR register (which serial_icr_read does)
|
|
*/
|
|
|
|
/*
|
|
* Check for Oxford Semiconductor 16C950.
|
|
*
|
|
* EFR [4] must be set else this test fails.
|
|
*
|
|
* This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca)
|
|
* claims that it's needed for 952 dual UART's (which are not
|
|
* recommended for new designs).
|
|
*/
|
|
up->acr = 0;
|
|
serial_out(up, UART_LCR, 0xBF);
|
|
serial_out(up, UART_EFR, UART_EFR_ECB);
|
|
serial_out(up, UART_LCR, 0x00);
|
|
id1 = serial_icr_read(up, UART_ID1);
|
|
id2 = serial_icr_read(up, UART_ID2);
|
|
id3 = serial_icr_read(up, UART_ID3);
|
|
rev = serial_icr_read(up, UART_REV);
|
|
|
|
DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev);
|
|
|
|
if (id1 == 0x16 && id2 == 0xC9 &&
|
|
(id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) {
|
|
up->port.type = PORT_16C950;
|
|
up->rev = rev | (id3 << 8);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We check for a XR16C850 by setting DLL and DLM to 0, and then
|
|
* reading back DLL and DLM. The chip type depends on the DLM
|
|
* value read back:
|
|
* 0x10 - XR16C850 and the DLL contains the chip revision.
|
|
* 0x12 - XR16C2850.
|
|
* 0x14 - XR16C854.
|
|
*/
|
|
serial_outp(up, UART_LCR, UART_LCR_DLAB);
|
|
saved_dll = serial_inp(up, UART_DLL);
|
|
saved_dlm = serial_inp(up, UART_DLM);
|
|
serial_outp(up, UART_DLL, 0);
|
|
serial_outp(up, UART_DLM, 0);
|
|
id2 = serial_inp(up, UART_DLL);
|
|
id1 = serial_inp(up, UART_DLM);
|
|
serial_outp(up, UART_DLL, saved_dll);
|
|
serial_outp(up, UART_DLM, saved_dlm);
|
|
|
|
DEBUG_AUTOCONF("850id=%02x:%02x ", id1, id2);
|
|
|
|
if (id1 == 0x10 || id1 == 0x12 || id1 == 0x14) {
|
|
if (id1 == 0x10)
|
|
up->rev = id2;
|
|
up->port.type = PORT_16850;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* It wasn't an XR16C850.
|
|
*
|
|
* We distinguish between the '654 and the '650 by counting
|
|
* how many bytes are in the FIFO. I'm using this for now,
|
|
* since that's the technique that was sent to me in the
|
|
* serial driver update, but I'm not convinced this works.
|
|
* I've had problems doing this in the past. -TYT
|
|
*/
|
|
if (size_fifo(up) == 64)
|
|
up->port.type = PORT_16654;
|
|
else
|
|
up->port.type = PORT_16650V2;
|
|
}
|
|
|
|
/*
|
|
* We detected a chip without a FIFO. Only two fall into
|
|
* this category - the original 8250 and the 16450. The
|
|
* 16450 has a scratch register (accessible with LCR=0)
|
|
*/
|
|
static void autoconfig_8250(struct uart_8250_port *up)
|
|
{
|
|
unsigned char scratch, status1, status2;
|
|
|
|
up->port.type = PORT_8250;
|
|
|
|
scratch = serial_in(up, UART_SCR);
|
|
serial_outp(up, UART_SCR, 0xa5);
|
|
status1 = serial_in(up, UART_SCR);
|
|
serial_outp(up, UART_SCR, 0x5a);
|
|
status2 = serial_in(up, UART_SCR);
|
|
serial_outp(up, UART_SCR, scratch);
|
|
|
|
if (status1 == 0xa5 && status2 == 0x5a)
|
|
up->port.type = PORT_16450;
|
|
}
|
|
|
|
/*
|
|
* We know that the chip has FIFOs. Does it have an EFR? The
|
|
* EFR is located in the same register position as the IIR and
|
|
* we know the top two bits of the IIR are currently set. The
|
|
* EFR should contain zero. Try to read the EFR.
|
|
*/
|
|
static void autoconfig_16550a(struct uart_8250_port *up)
|
|
{
|
|
unsigned char status1, status2;
|
|
|
|
up->port.type = PORT_16550A;
|
|
|
|
/*
|
|
* Check for presence of the EFR when DLAB is set.
|
|
* Only ST16C650V1 UARTs pass this test.
|
|
*/
|
|
serial_outp(up, UART_LCR, UART_LCR_DLAB);
|
|
if (serial_in(up, UART_EFR) == 0) {
|
|
serial_outp(up, UART_EFR, 0xA8);
|
|
if (serial_in(up, UART_EFR) != 0) {
|
|
DEBUG_AUTOCONF("EFRv1 ");
|
|
up->port.type = PORT_16650;
|
|
} else {
|
|
DEBUG_AUTOCONF("Motorola 8xxx DUART ");
|
|
}
|
|
serial_outp(up, UART_EFR, 0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Maybe it requires 0xbf to be written to the LCR.
|
|
* (other ST16C650V2 UARTs, TI16C752A, etc)
|
|
*/
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
if (serial_in(up, UART_EFR) == 0) {
|
|
DEBUG_AUTOCONF("EFRv2 ");
|
|
//lipeng modify at 2007/09/10
|
|
//printk("\n++++++will autoconfig_has_efr\n");
|
|
//lipeng modify at 2007/09/10
|
|
autoconfig_has_efr(up);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Check for a National Semiconductor SuperIO chip.
|
|
* Attempt to switch to bank 2, read the value of the LOOP bit
|
|
* from EXCR1. Switch back to bank 0, change it in MCR. Then
|
|
* switch back to bank 2, read it from EXCR1 again and check
|
|
* it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2
|
|
* On PowerPC we don't want to change baud_base, as we have
|
|
* a number of different divisors. -- Tom Rini
|
|
*/
|
|
serial_outp(up, UART_LCR, 0);
|
|
status1 = serial_in(up, UART_MCR);
|
|
serial_outp(up, UART_LCR, 0xE0);
|
|
status2 = serial_in(up, 0x02); /* EXCR1 */
|
|
|
|
if (!((status2 ^ status1) & UART_MCR_LOOP)) {
|
|
serial_outp(up, UART_LCR, 0);
|
|
serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP);
|
|
serial_outp(up, UART_LCR, 0xE0);
|
|
status2 = serial_in(up, 0x02); /* EXCR1 */
|
|
serial_outp(up, UART_LCR, 0);
|
|
serial_outp(up, UART_MCR, status1);
|
|
|
|
if ((status2 ^ status1) & UART_MCR_LOOP) {
|
|
#ifndef CONFIG_PPC
|
|
serial_outp(up, UART_LCR, 0xE0);
|
|
status1 = serial_in(up, 0x04); /* EXCR1 */
|
|
status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */
|
|
status1 |= 0x10; /* 1.625 divisor for baud_base --> 921600 */
|
|
serial_outp(up, 0x04, status1);
|
|
serial_outp(up, UART_LCR, 0);
|
|
up->port.uartclk = 921600*16;
|
|
#endif
|
|
|
|
up->port.type = PORT_NS16550A;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* No EFR. Try to detect a TI16750, which only sets bit 5 of
|
|
* the IIR when 64 byte FIFO mode is enabled when DLAB is set.
|
|
* Try setting it with and without DLAB set. Cheap clones
|
|
* set bit 5 without DLAB set.
|
|
*/
|
|
serial_outp(up, UART_LCR, 0);
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
|
|
status1 = serial_in(up, UART_IIR) >> 5;
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
|
serial_outp(up, UART_LCR, UART_LCR_DLAB);
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
|
|
status2 = serial_in(up, UART_IIR) >> 5;
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
|
|
|
DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);
|
|
|
|
if (status1 == 6 && status2 == 7) {
|
|
up->port.type = PORT_16750;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This routine is called by rs_init() to initialize a specific serial
|
|
* port. It determines what type of UART chip this serial port is
|
|
* using: 8250, 16450, 16550, 16550A. The important question is
|
|
* whether or not this UART is a 16550A or not, since this will
|
|
* determine whether or not we can use its FIFO features or not.
|
|
*/
|
|
static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
|
|
{
|
|
unsigned char status1, scratch, scratch2, scratch3;
|
|
unsigned char save_lcr, save_mcr;
|
|
unsigned long flags;
|
|
|
|
if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
|
|
return;
|
|
|
|
DEBUG_AUTOCONF("%s%d: autoconf (0x%04x, 0x%p): ", SERIAL_NAME,
|
|
up->port.line, up->port.iobase, up->port.membase);
|
|
|
|
/*
|
|
* We really do need global IRQs disabled here - we're going to
|
|
* be frobbing the chips IRQ enable register to see if it exists.
|
|
*/
|
|
#ifdef CONFIG_PREEMPT_RT
|
|
spin_lock(&up->port.lock);
|
|
if(up->port.flags & UPF_SHARE_IRQ)
|
|
disable_irq(up->port.irq);
|
|
#else
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
#endif
|
|
|
|
// save_flags(flags); cli();
|
|
|
|
if (!(up->port.flags & UPF_BUGGY_UART)) {
|
|
/*
|
|
* Do a simple existence test first; if we fail this,
|
|
* there's no point trying anything else.
|
|
*
|
|
* 0x80 is used as a nonsense port to prevent against
|
|
* false positives due to ISA bus float. The
|
|
* assumption is that 0x80 is a non-existent port;
|
|
* which should be safe since include/asm/io.h also
|
|
* makes this assumption.
|
|
*
|
|
* Note: this is safe as long as MCR bit 4 is clear
|
|
* and the device is in "PC" mode.
|
|
*/
|
|
scratch = serial_inp(up, UART_IER);
|
|
serial_outp(up, UART_IER, 0);
|
|
#ifdef __i386__
|
|
outb(0xff, 0x080);
|
|
#endif
|
|
scratch2 = serial_inp(up, UART_IER);
|
|
serial_outp(up, UART_IER, 0x0F);
|
|
#ifdef __i386__
|
|
outb(0, 0x080);
|
|
#endif
|
|
scratch3 = serial_inp(up, UART_IER);
|
|
serial_outp(up, UART_IER, scratch);
|
|
if (scratch2 != 0 || scratch3 != 0x0F) {
|
|
/*
|
|
* We failed; there's nothing here
|
|
*/
|
|
DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
|
|
scratch2, scratch3);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
save_mcr = serial_in(up, UART_MCR);
|
|
save_lcr = serial_in(up, UART_LCR);
|
|
|
|
/*
|
|
* Check to see if a UART is really there. Certain broken
|
|
* internal modems based on the Rockwell chipset fail this
|
|
* test, because they apparently don't implement the loopback
|
|
* test mode. So this test is skipped on the COM 1 through
|
|
* COM 4 ports. This *should* be safe, since no board
|
|
* manufacturer would be stupid enough to design a board
|
|
* that conflicts with COM 1-4 --- we hope!
|
|
*/
|
|
if (!(up->port.flags & UPF_SKIP_TEST)) {
|
|
serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
|
|
status1 = serial_inp(up, UART_MSR) & 0xF0;
|
|
serial_outp(up, UART_MCR, save_mcr);
|
|
if (status1 != 0x90) {
|
|
DEBUG_AUTOCONF("LOOP test failed (%02x) ",
|
|
status1);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We're pretty sure there's a port here. Lets find out what
|
|
* type of port it is. The IIR top two bits allows us to find
|
|
* out if its 8250 or 16450, 16550, 16550A or later. This
|
|
* determines what we test for next.
|
|
*
|
|
* We also initialise the EFR (if any) to zero for later. The
|
|
* EFR occupies the same register location as the FCR and IIR.
|
|
*/
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
serial_outp(up, UART_EFR, 0);
|
|
serial_outp(up, UART_LCR, 0);
|
|
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
|
scratch = serial_in(up, UART_IIR) >> 6;
|
|
|
|
DEBUG_AUTOCONF("iir=%d ", scratch);
|
|
|
|
switch (scratch) {
|
|
case 0:
|
|
autoconfig_8250(up);
|
|
break;
|
|
case 1:
|
|
up->port.type = PORT_UNKNOWN;
|
|
break;
|
|
case 2:
|
|
up->port.type = PORT_16550;
|
|
break;
|
|
case 3:
|
|
autoconfig_16550a(up);
|
|
break;
|
|
}
|
|
|
|
#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
|
|
/*
|
|
* Only probe for RSA ports if we got the region.
|
|
*/
|
|
if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) {
|
|
int i;
|
|
|
|
for (i = 0 ; i < PORT_RSA_MAX ; ++i) {
|
|
if (!probe_rsa[i] && !force_rsa[i])
|
|
break;
|
|
if (((probe_rsa[i] != up->port.iobase) ||
|
|
check_region(up->port.iobase + UART_RSA_BASE, 16)) &&
|
|
(force_rsa[i] != up->port.iobase))
|
|
continue;
|
|
if (__enable_rsa(up)) {
|
|
up->port.type = PORT_RSA;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
serial_outp(up, UART_LCR, save_lcr);
|
|
|
|
up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
|
|
up->capabilities = uart_config[up->port.type].flags;
|
|
|
|
if (up->port.type == PORT_UNKNOWN)
|
|
goto out;
|
|
|
|
/*
|
|
* Reset the UART.
|
|
*/
|
|
#ifdef CONFIG_SERIAL_8250_RSA
|
|
if (up->port.type == PORT_RSA)
|
|
serial_outp(up, UART_RSA_FRR, 0);
|
|
#endif
|
|
serial_outp(up, UART_MCR, save_mcr);
|
|
serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO |
|
|
UART_FCR_CLEAR_RCVR |
|
|
UART_FCR_CLEAR_XMIT));
|
|
serial_outp(up, UART_FCR, 0);
|
|
(void)serial_in(up, UART_RX);
|
|
serial_outp(up, UART_IER, 0);
|
|
|
|
out:
|
|
#ifdef CONFIG_PREEMPT_RT
|
|
if(up->port.flags & UPF_SHARE_IRQ)
|
|
enable_irq(up->port.irq);
|
|
spin_unlock(&up->port.lock);
|
|
#else
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
#endif
|
|
// restore_flags(flags);
|
|
DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name);
|
|
}
|
|
/*static*/ void autoconfig_irq(struct uart_8250_port *up)
|
|
{
|
|
unsigned char save_mcr, save_ier;
|
|
unsigned char save_ICP = 0;
|
|
unsigned int ICP = 0;
|
|
unsigned long irqs;
|
|
int irq;
|
|
|
|
if (up->port.flags & UPF_FOURPORT) {
|
|
ICP = (up->port.iobase & 0xfe0) | 0x1f;
|
|
save_ICP = inb_p(ICP);
|
|
outb_p(0x80, ICP);
|
|
(void) inb_p(ICP);
|
|
}
|
|
|
|
/* forget possible initially masked and pending IRQ */
|
|
probe_irq_off(probe_irq_on());
|
|
save_mcr = serial_inp(up, UART_MCR);
|
|
save_ier = serial_inp(up, UART_IER);
|
|
serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
|
|
|
|
irqs = probe_irq_on();
|
|
serial_outp(up, UART_MCR, 0);
|
|
udelay (10);
|
|
if (up->port.flags & UPF_FOURPORT) {
|
|
serial_outp(up, UART_MCR,
|
|
UART_MCR_DTR | UART_MCR_RTS);
|
|
} else {
|
|
serial_outp(up, UART_MCR,
|
|
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
|
|
}
|
|
serial_outp(up, UART_IER, 0x0f); /* enable all intrs */
|
|
(void)serial_inp(up, UART_LSR);
|
|
(void)serial_inp(up, UART_RX);
|
|
(void)serial_inp(up, UART_IIR);
|
|
(void)serial_inp(up, UART_MSR);
|
|
serial_outp(up, UART_TX, 0xFF);
|
|
udelay (20);
|
|
irq = probe_irq_off(irqs);
|
|
|
|
serial_outp(up, UART_MCR, save_mcr);
|
|
serial_outp(up, UART_IER, save_ier);
|
|
|
|
if (up->port.flags & UPF_FOURPORT)
|
|
outb_p(save_ICP, ICP);
|
|
|
|
up->port.irq = (irq > 0) ? irq : 0;
|
|
}
|
|
static inline void __stop_tx(struct uart_8250_port *p)
|
|
{
|
|
if (p->ier & UART_IER_THRI){
|
|
p->ier &= ~UART_IER_THRI;
|
|
serial_out(p, UART_IER, p->ier);
|
|
}
|
|
}
|
|
//lipeng modify at 06/08/2006
|
|
//James.dai maks the line off and add a new one @V3.20 to support ubuntu6 @07/03/06
|
|
//#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) || UBUNTU_2_6_15
|
|
static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
|
|
if (up->ier & UART_IER_THRI) {
|
|
up->ier &= ~UART_IER_THRI;
|
|
serial_out(up, UART_IER, up->ier);
|
|
}
|
|
if (up->port.type == PORT_16C950 && tty_stop) {
|
|
up->acr |= UART_ACR_TXDIS;
|
|
serial_icr_write(up, UART_ACR, up->acr);
|
|
}
|
|
}
|
|
static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
|
|
if (!(up->ier & UART_IER_THRI)) {
|
|
up->ier |= UART_IER_THRI;
|
|
serial_out(up, UART_IER, up->ier);
|
|
}
|
|
/*
|
|
* We only do this from uart_start
|
|
*/
|
|
if (tty_start && up->port.type == PORT_16C950) {
|
|
up->acr &= ~UART_ACR_TXDIS;
|
|
serial_icr_write(up, UART_ACR, up->acr);
|
|
}
|
|
}
|
|
#else
|
|
static void serial8250_stop_tx(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
__stop_tx(up);
|
|
if (up->port.type == PORT_16C950 ) {
|
|
up->acr |= UART_ACR_TXDIS;
|
|
serial_icr_write(up, UART_ACR, up->acr);
|
|
}
|
|
}
|
|
static void serial8250_start_tx(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
|
|
if (!(up->ier & UART_IER_THRI)) {
|
|
up->ier |= UART_IER_THRI;
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
/* if(up->bugs & UART_BUG_TXEN) {
|
|
unsigned char lsr, iir;
|
|
lsr = serial_in(up, UART_LSR);
|
|
iir = serial_in(up, UART_IIR);
|
|
if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT)
|
|
transmit_chars(up);
|
|
}
|
|
*/
|
|
}
|
|
/*
|
|
* We only do this from uart_start
|
|
*/
|
|
if (up->acr & UART_ACR_TXDIS && up->port.type == PORT_16C950) {
|
|
up->acr &= ~UART_ACR_TXDIS;
|
|
serial_icr_write(up, UART_ACR, up->acr);
|
|
}
|
|
}
|
|
#endif
|
|
//lipeng modify end
|
|
static void serial8250_stop_rx(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
|
|
up->ier &= ~UART_IER_RLSI;
|
|
up->port.read_status_mask &= ~UART_LSR_DR;
|
|
serial_out(up, UART_IER, up->ier);
|
|
}
|
|
|
|
static void serial8250_enable_ms(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
|
|
up->ier |= UART_IER_MSI;
|
|
serial_out(up, UART_IER, up->ier);
|
|
}
|
|
//lipeng modify at 03/03/2008
|
|
//lipeng mask at 01/04/2008
|
|
//lipeng modify at 06/08/2006
|
|
//James.dai maks the line off and add a new one @V3.20 to support ubuntu6
|
|
//#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)
|
|
static _INLINE_ void
|
|
receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
|
|
{
|
|
struct tty_struct *tty = up->port.info->tty;
|
|
unsigned char ch;
|
|
int max_count = 256;
|
|
|
|
int count = 0;
|
|
if(up->port.unused[1] & 0x02)
|
|
{
|
|
int flags;
|
|
unsigned char save_rfl;
|
|
|
|
//jinxin added to get number of chars in the receiver FIFO from RFL
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, up->acr|0x80);
|
|
save_rfl = serial_in(up, UART_RFL);
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, up->acr);
|
|
count = (up->port.fifosize < save_rfl)? up->port.fifosize: save_rfl;
|
|
if (count > TTY_FLIPBUF_SIZE - tty->flip.count)
|
|
count = TTY_FLIPBUF_SIZE - tty->flip.count;
|
|
//printk(KERN_INFO "RFL=%d,count=%d,", save_rfl, count);
|
|
|
|
//firstly clear DMA status register
|
|
writel(DMADONE|DMAERR, up->port.membase + DMASTA);
|
|
//copy data to DMA buffer
|
|
writel(up->rx_ring_dma, up->port.membase + DMAADL);
|
|
writel(0x0, up->port.membase + DMAADH);
|
|
writel(DMAREAD|count, up->port.membase + DMATL);
|
|
do{
|
|
flags = readl(up->port.membase + DMASTA);
|
|
} while (flags & DMAACT);
|
|
//ASSERT(!(flags & DMAERR));
|
|
//temp = tty_insert_flip_string(tty, (unsigned char *)up->rx_ring, count);
|
|
memcpy(tty->flip.char_buf_ptr, (unsigned char *)up->rx_ring, count);
|
|
memset(tty->flip.flag_buf_ptr, TTY_NORMAL, count);
|
|
tty->flip.char_buf_ptr += count;
|
|
tty->flip.flag_buf_ptr += count;
|
|
tty->flip.count += count;
|
|
up->port.icount.rx += count;
|
|
}
|
|
else
|
|
{
|
|
count = 0;
|
|
do {
|
|
if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
|
|
tty->flip.work.func((void *)tty);
|
|
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
|
|
return; // if TTY_DONT_FLIP is set
|
|
}
|
|
ch = serial_inp(up, UART_RX);
|
|
*tty->flip.char_buf_ptr = ch;
|
|
*tty->flip.flag_buf_ptr = TTY_NORMAL;
|
|
up->port.icount.rx++;
|
|
count++;
|
|
|
|
if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
|
|
UART_LSR_FE | UART_LSR_OE))) {
|
|
if (*status & UART_LSR_BI) {
|
|
*status &= ~(UART_LSR_FE | UART_LSR_PE);
|
|
up->port.icount.brk++;
|
|
|
|
if (uart_handle_break(&up->port))
|
|
goto ignore_char;
|
|
} else if (*status & UART_LSR_PE)
|
|
up->port.icount.parity++;
|
|
else if (*status & UART_LSR_FE)
|
|
up->port.icount.frame++;
|
|
if (*status & UART_LSR_OE)
|
|
up->port.icount.overrun++;
|
|
|
|
*status &= up->port.read_status_mask;
|
|
|
|
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
|
if (up->port.line == up->port.cons->index) {
|
|
|
|
*status |= up->lsr_break_flag;
|
|
up->lsr_break_flag = 0;
|
|
}
|
|
#endif
|
|
if (*status & UART_LSR_BI) {
|
|
DEBUG_INTR("handling break....");
|
|
*tty->flip.flag_buf_ptr = TTY_BREAK;
|
|
} else if (*status & UART_LSR_PE)
|
|
*tty->flip.flag_buf_ptr = TTY_PARITY;
|
|
else if (*status & UART_LSR_FE)
|
|
*tty->flip.flag_buf_ptr = TTY_FRAME;
|
|
}
|
|
//james dai modify it to support FC7 and FC8
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
|
|
if (uart_handle_sysrq_char(&up->port, ch, regs))
|
|
#else
|
|
if (uart_handle_sysrq_char(&up->port, ch))
|
|
#endif
|
|
//james dai modify end
|
|
goto ignore_char;
|
|
if ((*status & up->port.ignore_status_mask) == 0) {
|
|
tty->flip.flag_buf_ptr++;
|
|
tty->flip.char_buf_ptr++;
|
|
tty->flip.count++;
|
|
}
|
|
if ((*status & UART_LSR_OE) &&
|
|
tty->flip.count < TTY_FLIPBUF_SIZE) {
|
|
|
|
*tty->flip.flag_buf_ptr = TTY_OVERRUN;
|
|
tty->flip.flag_buf_ptr++;
|
|
tty->flip.char_buf_ptr++;
|
|
tty->flip.count++;
|
|
}
|
|
ignore_char:
|
|
*status = serial_inp(up, UART_LSR);
|
|
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
|
|
}
|
|
//WangMao add to test
|
|
//printk("receive_chars: receive %d chars",count);
|
|
tty_flip_buffer_push(tty);
|
|
}
|
|
//lipeng modify modify at 03/03/2008
|
|
#else
|
|
//lipeng mask end
|
|
#define arch_8250_sysrq_via_ctrl_o(a,b) (0)
|
|
static void
|
|
receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
|
|
{
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
|
struct tty_struct *tty = up->port.state->port.tty;
|
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
|
|
struct tty_struct *tty = up->port.info->port.tty;
|
|
#else
|
|
struct tty_struct *tty = up->port.info->tty;
|
|
#endif
|
|
unsigned char ch, lsr = *status;
|
|
int max_count = 256;
|
|
char flag;
|
|
if(up->port.unused[1] & 0x02)
|
|
{
|
|
unsigned int count;
|
|
//unsigned int temp;
|
|
int flags;
|
|
unsigned char save_rfl;
|
|
|
|
//jinxin added to get number of chars in the receiver FIFO from RFL
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, up->acr|0x80);
|
|
save_rfl = serial_in(up, UART_RFL);
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, up->acr);
|
|
count = (up->port.fifosize < save_rfl)? up->port.fifosize: save_rfl;
|
|
//ensure tty core has enough space
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15)
|
|
count = tty_buffer_request_room(tty, count);
|
|
#else
|
|
if (count > TTY_FLIPBUF_SIZE - tty->flip.count)
|
|
count = TTY_FLIPBUF_SIZE - tty->flip.count;
|
|
#endif
|
|
//printk(KERN_INFO "RFL=%d,count=%d,", save_rfl, count);
|
|
|
|
//firstly clear DMA status register
|
|
writel(DMADONE|DMAERR, up->port.membase + DMASTA);
|
|
//copy data to DMA buffer
|
|
writel(up->rx_ring_dma, up->port.membase + DMAADL);
|
|
writel(0x0, up->port.membase + DMAADH);
|
|
writel(DMAREAD|count, up->port.membase + DMATL);
|
|
do{
|
|
flags = readl(up->port.membase + DMASTA);
|
|
} while (flags & DMAACT);
|
|
//ASSERT(!(flags & DMAERR));
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15)
|
|
tty_insert_flip_string(tty, (unsigned char *)up->rx_ring, count);
|
|
#else
|
|
memcpy(tty->flip.char_buf_ptr, (unsigned char *)up->rx_ring, count);
|
|
memset(tty->flip.flag_buf_ptr, TTY_NORMAL, count);
|
|
tty->flip.char_buf_ptr += count;
|
|
tty->flip.flag_buf_ptr += count;
|
|
tty->flip.count += count;
|
|
#endif
|
|
up->port.icount.rx += count;
|
|
}
|
|
else
|
|
{
|
|
do {
|
|
ch = serial_inp(up, UART_RX);
|
|
#ifdef CONFIG_KDB
|
|
if ((up->port.line == kdb_serial_line) && kdb_on == 1) {
|
|
if (ch == *kdb_serial_ptr) {
|
|
if (!(*++kdb_serial_ptr)) {
|
|
kdb(KDB_REASON_KEYBOARD, 0, regs);
|
|
kdb_serial_ptr = kdb_serial_str;
|
|
break;
|
|
}
|
|
} else
|
|
kdb_serial_ptr = kdb_serial_str;
|
|
}
|
|
#endif /* CONFIG_KDB */
|
|
if (arch_8250_sysrq_via_ctrl_o(ch, &up->port))
|
|
goto ignore_char;
|
|
flag = TTY_NORMAL;
|
|
up->port.icount.rx++;
|
|
|
|
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
|
/*
|
|
* Recover the break flag from console xmit
|
|
*/
|
|
if (up->port.line == up->port.cons->index) {
|
|
lsr |= up->lsr_break_flag;
|
|
up->lsr_break_flag = 0;
|
|
}
|
|
#endif
|
|
|
|
if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
|
|
UART_LSR_FE | UART_LSR_OE))) {
|
|
/*
|
|
* For statistics only
|
|
*/
|
|
if (lsr & UART_LSR_BI) {
|
|
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
|
|
up->port.icount.brk++;
|
|
/*
|
|
* We do the SysRQ and SAK checking
|
|
* here because otherwise the break
|
|
* may get masked by ignore_status_mask
|
|
* or read_status_mask.
|
|
*/
|
|
if (uart_handle_break(&up->port))
|
|
goto ignore_char;
|
|
} else if (lsr & UART_LSR_PE)
|
|
up->port.icount.parity++;
|
|
else if (lsr & UART_LSR_FE)
|
|
up->port.icount.frame++;
|
|
if (lsr & UART_LSR_OE)
|
|
up->port.icount.overrun++;
|
|
|
|
/*
|
|
* Mask off conditions which should be ignored.
|
|
*/
|
|
lsr &= up->port.read_status_mask;
|
|
|
|
if (lsr & UART_LSR_BI) {
|
|
DEBUG_INTR("handling break....");
|
|
flag = TTY_BREAK;
|
|
} else if (lsr & UART_LSR_PE)
|
|
flag = TTY_PARITY;
|
|
else if (lsr & UART_LSR_FE)
|
|
flag = TTY_FRAME;
|
|
}
|
|
//james dai modify it to support FC7 and FC8
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
|
|
if (uart_handle_sysrq_char(&up->port, ch, regs))
|
|
#else
|
|
if (uart_handle_sysrq_char(&up->port, ch))
|
|
#endif
|
|
//james dai modify end
|
|
goto ignore_char;
|
|
|
|
uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
|
|
|
|
ignore_char:
|
|
lsr = serial_inp(up, UART_LSR);
|
|
} while ((lsr & UART_LSR_DR) && (max_count-- > 0));
|
|
}
|
|
spin_unlock(&up->port.lock);
|
|
tty_flip_buffer_push(tty);
|
|
spin_lock(&up->port.lock);
|
|
*status = lsr;
|
|
}
|
|
//lipeng modify modify at 03/03/2008
|
|
//lipeng mask at 01/04/2008
|
|
#endif
|
|
//James.dai maks the line off and add a new one @V3.20 to support ubuntu6
|
|
//#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) || UBUNTU_2_6_15
|
|
static _INLINE_ void transmit_chars(struct uart_8250_port *up)
|
|
{
|
|
struct circ_buf *xmit = &up->port.info->xmit;
|
|
int count = 0;
|
|
unsigned int flags;
|
|
//unsigned char save_tfl;
|
|
|
|
if (up->port.x_char) {
|
|
serial_outp(up, UART_TX, up->port.x_char);
|
|
up->port.icount.tx++;
|
|
up->port.x_char = 0;
|
|
return;
|
|
}
|
|
if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
|
|
serial8250_stop_tx(&up->port, 0);
|
|
return;
|
|
}
|
|
|
|
if(up->port.unused[1] & 0x02)
|
|
{
|
|
|
|
//jinxin added to get number of chars in the receiver FIFO from RFL
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, up->acr|0x80);
|
|
//save_tfl = serial_in(up, UART_TFL);
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, up->acr);
|
|
//printk(KERN_INFO "save_tfl=%d,", save_tfl);
|
|
//printk(KERN_INFO "TTL=%d,", serial_icr_read(up,UART_TTL));
|
|
//count = (up->port.fifosize < (128 - save_tfl))?up->port.fifosize:(128 - save_tfl);
|
|
//count = MIN(count, 64);
|
|
//count = MIN(count,uart_circ_chars_pending(xmit));
|
|
//printk(KERN_INFO "count=%d\n", count);
|
|
count = (uart_circ_chars_pending(xmit) < up->port.fifosize)? uart_circ_chars_pending(xmit):up->port.fifosize;
|
|
|
|
if ((xmit->tail + count) > (UART_XMIT_SIZE - 1))
|
|
{
|
|
count = UART_XMIT_SIZE - xmit->tail;
|
|
}
|
|
//firstly clear DMA status register
|
|
writel(DMADONE|DMAERR, up->port.membase + DMASTA);
|
|
//copy data to DMA buffer
|
|
memcpy(up->tx_ring, &xmit->buf[xmit->tail], count);
|
|
writel(up->tx_ring_dma, up->port.membase + DMAADL);
|
|
writel(0x0, up->port.membase + DMAADH);
|
|
writel(count, up->port.membase + DMATL);
|
|
do{
|
|
flags = readl(up->port.membase + DMASTA);
|
|
} while (flags & DMAACT);
|
|
//ASSERT(!(flags & DMAERR));
|
|
|
|
xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
|
|
up->port.icount.tx += count;
|
|
}
|
|
else
|
|
{
|
|
//james dai add at 2007/7/30
|
|
//lipeng modify at 2008/1/8
|
|
count = up->port.fifosize >> 1;
|
|
//lipeng modify end
|
|
//james dai add end
|
|
do {
|
|
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
|
|
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
|
up->port.icount.tx++;
|
|
if (uart_circ_empty(xmit))
|
|
break;
|
|
} while (--count > 0);
|
|
}
|
|
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
|
adv_uart_write_wakeup(&up->port);
|
|
DEBUG_INTR("THRE...");
|
|
if (uart_circ_empty(xmit))
|
|
serial8250_stop_tx(&up->port, 0);
|
|
}
|
|
#else
|
|
static _INLINE_ void transmit_chars(struct uart_8250_port *up)
|
|
{
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
|
struct circ_buf *xmit = &up->port.state->xmit;
|
|
#else
|
|
struct circ_buf *xmit = &up->port.info->xmit;
|
|
#endif
|
|
int count;
|
|
unsigned int flags;
|
|
//unsigned char save_tfl;
|
|
|
|
if (up->port.x_char) {
|
|
serial_outp(up, UART_TX, up->port.x_char);
|
|
up->port.icount.tx++;
|
|
up->port.x_char = 0;
|
|
return;
|
|
}
|
|
if ( uart_tx_stopped(&up->port) ) {
|
|
serial8250_stop_tx(&up->port);
|
|
return;
|
|
}
|
|
if (uart_circ_empty(xmit)) {
|
|
//lipeng modify at 2007/09/07
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
|
|
__stop_tx(up);
|
|
#else
|
|
__stop_tx(&up->port);
|
|
#endif
|
|
//lipeng modify end
|
|
return;
|
|
}
|
|
|
|
//lipeng modify at 2008/1/8
|
|
//lipeng modify end
|
|
if(up->port.unused[1] & 0x02)
|
|
{
|
|
//printk("DMA\n");
|
|
//jinxin added to get number of chars in the receiver FIFO from RFL
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, up->acr|0x80);
|
|
//save_tfl = serial_in(up, UART_TFL);
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, up->acr);
|
|
//printk(KERN_INFO "save_tfl=%d,", save_tfl);
|
|
//printk(KERN_INFO "TTL=%d,", serial_icr_read(up,UART_TTL));
|
|
//count = (up->port.fifosize < (128 - save_tfl))?up->port.fifosize:(128 - save_tfl);
|
|
//count = MIN(count, up->port.fifosize >> 1);
|
|
//count = MIN(count,uart_circ_chars_pending(xmit));
|
|
//printk("uart_circ_chars_pending(xmit) = %ld",uart_circ_chars_pending(xmit));
|
|
count = (uart_circ_chars_pending(xmit) < up->port.fifosize)? uart_circ_chars_pending(xmit):up->port.fifosize;
|
|
|
|
if ((xmit->tail + count) > (UART_XMIT_SIZE - 1))
|
|
{
|
|
count = UART_XMIT_SIZE - xmit->tail;
|
|
}
|
|
//printk(KERN_INFO "count=%d\n", count);
|
|
|
|
//firstly clear DMA status register
|
|
writel(DMADONE|DMAERR, up->port.membase + DMASTA);
|
|
//copy data to DMA buffer
|
|
memcpy(up->tx_ring, &xmit->buf[xmit->tail], count);
|
|
writel(up->tx_ring_dma, up->port.membase + DMAADL);
|
|
writel(0x0, up->port.membase + DMAADH);
|
|
writel(count, up->port.membase + DMATL);
|
|
do{
|
|
flags = readl(up->port.membase + DMASTA);
|
|
} while (flags & DMAACT);
|
|
//ASSERT(!(flags & DMAERR));
|
|
|
|
xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
|
|
up->port.icount.tx += count;
|
|
|
|
}
|
|
else
|
|
{
|
|
//printk("not DMA\n");
|
|
count = up->port.fifosize >> 1;
|
|
do {
|
|
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
|
|
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
|
up->port.icount.tx++;
|
|
if (uart_circ_empty(xmit))
|
|
break;
|
|
} while (--count > 0);
|
|
}
|
|
|
|
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
|
adv_uart_write_wakeup(&up->port);
|
|
|
|
DEBUG_INTR("THRE...");
|
|
if (uart_circ_empty(xmit))
|
|
{
|
|
//lipeng modify at 2007/09/07
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
|
|
__stop_tx(up);
|
|
#else
|
|
__stop_tx(&up->port);
|
|
#endif
|
|
//lipeng modify end
|
|
}
|
|
|
|
}
|
|
#endif
|
|
//lipeng modify end
|
|
static _INLINE_ void check_modem_status(struct uart_8250_port *up)
|
|
{
|
|
int status;
|
|
|
|
status = serial_in(up, UART_MSR);
|
|
|
|
if ((status & UART_MSR_ANY_DELTA) == 0)
|
|
return;
|
|
|
|
if (status & UART_MSR_TERI)
|
|
up->port.icount.rng++;
|
|
if (status & UART_MSR_DDSR)
|
|
up->port.icount.dsr++;
|
|
if (status & UART_MSR_DDCD)
|
|
uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
|
|
if (status & UART_MSR_DCTS)
|
|
uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
|
wake_up_interruptible(&up->port.state->port.delta_msr_wait);
|
|
#else
|
|
wake_up_interruptible(&up->port.info->delta_msr_wait);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* This handles the interrupt from one port.
|
|
*/
|
|
static inline void
|
|
serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
|
|
{
|
|
unsigned int status = serial_inp(up, UART_LSR);
|
|
#ifdef CONFIG_PREEMPT_RT
|
|
unsigned long flags;
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
#endif
|
|
status = serial_inp(up, UART_LSR);
|
|
|
|
DEBUG_INTR("status = %x...", status);
|
|
|
|
if (status & UART_LSR_DR)
|
|
{
|
|
//printk("rs_interrupt: receive interrupt occurs\n");
|
|
receive_chars(up, &status, regs);
|
|
}
|
|
check_modem_status(up);
|
|
if (status & UART_LSR_THRE)
|
|
{
|
|
//printk("rs_interrupt: transmit interrupt occurs\n");
|
|
transmit_chars(up);
|
|
}
|
|
#ifdef CONFIG_PREEMPT_RT
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* This is the serial driver's interrupt routine.
|
|
*
|
|
* Arjan thinks the old way was overly complex, so it got simplified.
|
|
* Alan disagrees, saying that need the complexity to handle the weird
|
|
* nature of ISA shared interrupts. (This is a special exception.)
|
|
*
|
|
* In order to handle ISA shared interrupts properly, we need to check
|
|
* that all ports have been serviced, and therefore the ISA interrupt
|
|
* line has been de-asserted.
|
|
*
|
|
* This means we need to loop through all ports. checking that they
|
|
* don't have an interrupt pending.
|
|
*/
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
|
|
static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
|
#else
|
|
static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
|
|
#endif
|
|
{
|
|
//a irq_info struct manages a irq(whatever shared or not)
|
|
//ports who share this irq is added to list_head of the irq_info struct of this irq
|
|
struct irq_info *i = dev_id;
|
|
struct list_head *l, *end = NULL;
|
|
#ifndef CONFIG_PREEMPT_RT
|
|
int pass_counter = 0;
|
|
#endif
|
|
|
|
DEBUG_INTR("serial8250_interrupt(%d)...", irq);
|
|
|
|
spin_lock(&i->lock);
|
|
|
|
l = i->head;
|
|
do {
|
|
struct uart_8250_port *up;
|
|
unsigned int iir;
|
|
|
|
up = list_entry(l, struct uart_8250_port, list);
|
|
|
|
iir = serial_in(up, UART_IIR);
|
|
if (!(iir & UART_IIR_NO_INT)) {
|
|
#ifndef CONFIG_PREEMPT_RT
|
|
spin_lock(&up->port.lock);
|
|
#endif
|
|
serial8250_handle_port(up,NULL);
|
|
#ifndef CONFIG_PREEMPT_RT
|
|
spin_unlock(&up->port.lock);
|
|
#endif
|
|
|
|
end = NULL;
|
|
} else if (end == NULL)
|
|
end = l;//this code is for exiting from do-while
|
|
|
|
l = l->next;
|
|
#ifndef CONFIG_PREEMPT_RT
|
|
if (l == i->head && pass_counter++ > PASS_LIMIT) {
|
|
/* If we hit this, we're dead. */
|
|
printk(KERN_ERR "serial8250: too much work for "
|
|
"irq%d\n", irq);
|
|
break;
|
|
}
|
|
#endif
|
|
} while (l != end);
|
|
|
|
spin_unlock(&i->lock);
|
|
|
|
DEBUG_INTR("end.\n");
|
|
/* FIXME! Was it really ours? */
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/*
|
|
* To support ISA shared interrupts, we need to have one interrupt
|
|
* handler that ensures that the IRQ line has been deasserted
|
|
* before returning. Failing to do this will result in the IRQ
|
|
* line being stuck active, and, since ISA irqs are edge triggered,
|
|
* no more IRQs will be seen.
|
|
*/
|
|
static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up)
|
|
{
|
|
spin_lock_irq(&i->lock);
|
|
|
|
if (!list_empty(i->head)) {
|
|
if (i->head == &up->list)
|
|
i->head = i->head->next;
|
|
list_del(&up->list);
|
|
} else {
|
|
BUG_ON(i->head != &up->list);
|
|
i->head = NULL;
|
|
}
|
|
|
|
spin_unlock_irq(&i->lock);
|
|
}
|
|
|
|
/*static*/ int serial_link_irq_chain(struct uart_8250_port *up)
|
|
{
|
|
struct irq_info *i = irq_lists + up->port.irq;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
|
|
int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED: 0;
|
|
#else
|
|
int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0;
|
|
#endif
|
|
|
|
spin_lock_irq(&i->lock);
|
|
|
|
if (i->head) {
|
|
list_add(&up->list, i->head);
|
|
spin_unlock_irq(&i->lock);
|
|
|
|
ret = 0;
|
|
} else {
|
|
INIT_LIST_HEAD(&up->list);
|
|
i->head = &up->list;
|
|
spin_unlock_irq(&i->lock);
|
|
|
|
ret = request_irq(up->port.irq, serial8250_interrupt,
|
|
irq_flags, "adv950", i);
|
|
if (ret < 0)
|
|
serial_do_unlink(i, up);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void serial_unlink_irq_chain(struct uart_8250_port *up)
|
|
{
|
|
struct irq_info *i = irq_lists + up->port.irq;
|
|
|
|
BUG_ON(i->head == NULL);
|
|
|
|
if (list_empty(i->head))
|
|
free_irq(up->port.irq, i);
|
|
|
|
serial_do_unlink(i, up);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function is used to handle ports that do not have an
|
|
* interrupt. This doesn't work very well for 16450's, but gives
|
|
* barely passable results for a 16550A. (Although at the expense
|
|
* of much CPU overhead).
|
|
*/
|
|
static void serial8250_timeout(unsigned long data)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)data;
|
|
unsigned int timeout;
|
|
unsigned int iir;
|
|
|
|
iir = serial_in(up, UART_IIR);
|
|
if (!(iir & UART_IIR_NO_INT)) {
|
|
spin_lock(&up->port.lock);
|
|
serial8250_handle_port(up, NULL);
|
|
spin_unlock(&up->port.lock);
|
|
}
|
|
|
|
timeout = up->port.timeout;
|
|
timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
|
|
mod_timer(&up->timer, jiffies + timeout);
|
|
}
|
|
|
|
|
|
static unsigned int serial8250_tx_empty(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
unsigned long flags;
|
|
unsigned int ret;
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int serial8250_get_mctrl(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
unsigned char status;
|
|
unsigned int ret;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) || UBUNTU_2_6_15
|
|
unsigned long flags;
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
#endif
|
|
status = serial_in(up, UART_MSR);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) || UBUNTU_2_6_15
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
#endif
|
|
ret = 0;
|
|
if (status & UART_MSR_DCD)
|
|
ret |= TIOCM_CAR;
|
|
if (status & UART_MSR_RI)
|
|
ret |= TIOCM_RNG;
|
|
if (status & UART_MSR_DSR)
|
|
ret |= TIOCM_DSR;
|
|
if (status & UART_MSR_CTS)
|
|
ret |= TIOCM_CTS;
|
|
return ret;
|
|
}
|
|
|
|
/*static*/ void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
unsigned char mcr = 0;
|
|
|
|
if (mctrl & TIOCM_RTS)
|
|
mcr |= UART_MCR_RTS;
|
|
if (mctrl & TIOCM_DTR)
|
|
mcr |= UART_MCR_DTR;
|
|
if (mctrl & TIOCM_OUT1)
|
|
mcr |= UART_MCR_OUT1;
|
|
if (mctrl & TIOCM_OUT2)
|
|
mcr |= UART_MCR_OUT2;
|
|
if (mctrl & TIOCM_LOOP)
|
|
mcr |= UART_MCR_LOOP;
|
|
|
|
mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
|
|
if(up->port.unused[1] & 0x01){
|
|
mcr |= 0x80;
|
|
}
|
|
//printk("mcr=%x,", mcr);
|
|
serial_out(up, UART_MCR, mcr);
|
|
}
|
|
|
|
static void serial8250_break_ctl(struct uart_port *port, int break_state)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
if (break_state == -1)
|
|
up->lcr |= UART_LCR_SBC;
|
|
else
|
|
up->lcr &= ~UART_LCR_SBC;
|
|
serial_out(up, UART_LCR, up->lcr);
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
}
|
|
|
|
static int serial8250_startup(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
#ifndef CONFIG_PREEMPT_RT
|
|
unsigned long flags;
|
|
#endif
|
|
int retval;
|
|
|
|
up->capabilities = uart_config[up->port.type].flags;
|
|
up->mcr = 0;
|
|
if (up->port.type == PORT_16C950) {
|
|
/* Wake up and initialize UART */
|
|
//up->acr = 0;
|
|
up->acr = up->port.unused[0]; // Default ACR value for auto DTR RS485 or RS232
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
serial_outp(up, UART_EFR, UART_EFR_ECB);
|
|
serial_outp(up, UART_IER, 0);
|
|
serial_outp(up, UART_LCR, 0);
|
|
serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
|
|
|
|
//enable enhanced mode
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
serial_outp(up, UART_EFR, UART_EFR_ECB);
|
|
serial_outp(up, UART_LCR, 0);
|
|
|
|
//WangMao add @07/02/10 to V3.20 to enable 19c650 trigger level
|
|
//printk("startup:will enable trigger level\n");
|
|
//must initial TTL,RTL,then set UART_ACR_TLENB,or too much work for interrupt
|
|
serial_icr_write(up,UART_TTL,0);
|
|
serial_icr_write(up,UART_RTL,1);
|
|
up->acr|=UART_ACR_TLENB;
|
|
serial_icr_write(up, UART_ACR, up->acr);
|
|
|
|
|
|
//lipeng modify at 2008/1/8
|
|
//auto flow control upper trigger level
|
|
serial_icr_write(up,UART_FCL,4);
|
|
//auto flow control bottom trigger level
|
|
serial_icr_write(up,UART_FCH,72);
|
|
//lipeng modify end
|
|
//WangMao add end
|
|
}
|
|
|
|
#ifdef CONFIG_SERIAL_8250_RSA
|
|
/*
|
|
* If this is an RSA port, see if we can kick it up to the
|
|
* higher speed clock.
|
|
*/
|
|
enable_rsa(up);
|
|
#endif
|
|
|
|
/*
|
|
* Clear the FIFO buffers and disable them.
|
|
* (they will be reeanbled in set_termios())
|
|
*/
|
|
if (up->capabilities & UART_CLEAR_FIFO) {
|
|
if(up->port.type == 1)
|
|
printk("will clear FIFO and disable them");
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
|
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
|
|
serial_outp(up, UART_FCR, 0);
|
|
}
|
|
|
|
/*
|
|
* Clear the interrupt registers.
|
|
*/
|
|
(void) serial_inp(up, UART_LSR);
|
|
(void) serial_inp(up, UART_RX);
|
|
(void) serial_inp(up, UART_IIR);
|
|
(void) serial_inp(up, UART_MSR);
|
|
|
|
/*
|
|
* At this point, there's no way the LSR could still be 0xff;
|
|
* if it is, then bail out, because there's likely no UART
|
|
* here.
|
|
*/
|
|
if (!(up->port.flags & UPF_BUGGY_UART) &&
|
|
(serial_inp(up, UART_LSR) == 0xff)) {
|
|
printk("%s%d: LSR safety check engaged!\n",
|
|
SERIAL_NAME, up->port.line);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*
|
|
* If the "interrupt" for this port doesn't correspond with any
|
|
* hardware interrupt, we use a timer-based system. The original
|
|
* driver used to do this with IRQ0.
|
|
*/
|
|
if (!is_real_interrupt(up->port.irq)) {
|
|
unsigned int timeout = up->port.timeout;
|
|
|
|
timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
|
|
|
|
up->timer.data = (unsigned long)up;
|
|
mod_timer(&up->timer, jiffies + timeout);
|
|
} else {
|
|
retval = serial_link_irq_chain(up);
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
|
|
if(up->port.unused[1] & 0x02){
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 23)
|
|
up->tx_ring = dma_alloc_coherent(up->port.dev, TX_BUF_TOT_LEN,
|
|
&up->tx_ring_dma, GFP_KERNEL);
|
|
up->rx_ring = dma_alloc_coherent(up->port.dev, RX_BUF_TOT_LEN,
|
|
&up->rx_ring_dma, GFP_KERNEL);
|
|
if (up->tx_ring == NULL || up->rx_ring == NULL) {
|
|
if (up->tx_ring)
|
|
dma_free_coherent(up->port.dev, TX_BUF_TOT_LEN,
|
|
up->tx_ring, up->tx_ring_dma);
|
|
if (up->rx_ring)
|
|
dma_free_coherent(up->port.dev, RX_BUF_TOT_LEN,
|
|
up->rx_ring, up->rx_ring_dma);
|
|
return -ENOMEM;
|
|
}
|
|
#else
|
|
up->tx_ring = pci_alloc_consistent(up->port.dev, TX_BUF_TOT_LEN,
|
|
&up->tx_ring_dma);
|
|
up->rx_ring = pci_alloc_consistent(up->port.dev, RX_BUF_TOT_LEN,
|
|
&up->rx_ring_dma);
|
|
if (up->tx_ring == NULL || up->rx_ring == NULL) {
|
|
if (up->tx_ring)
|
|
pci_free_consistent(up->port.dev, TX_BUF_TOT_LEN,
|
|
up->tx_ring, up->tx_ring_dma);
|
|
if (up->rx_ring)
|
|
pci_free_consistent(up->port.dev, RX_BUF_TOT_LEN,
|
|
up->rx_ring, up->rx_ring_dma);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Now, initialize the UART
|
|
*/
|
|
serial_outp(up, UART_LCR, UART_LCR_WLEN8);
|
|
#ifdef CONFIG_PREEMPT_RT
|
|
spin_lock(&up->port.lock);
|
|
if(up->port.flags & UPF_SHARE_IRQ)
|
|
disable_irq(up->port.irq);
|
|
#else
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
#endif
|
|
if (up->port.flags & UPF_FOURPORT) {
|
|
if (!is_real_interrupt(up->port.irq))
|
|
up->port.mctrl |= TIOCM_OUT1;
|
|
} else
|
|
/*
|
|
* Most PC uarts need OUT2 raised to enable interrupts.
|
|
*/
|
|
if (is_real_interrupt(up->port.irq))
|
|
up->port.mctrl |= TIOCM_OUT2;
|
|
|
|
serial8250_set_mctrl(&up->port, up->port.mctrl);
|
|
#ifdef CONFIG_PREEMPT_RT
|
|
if(up->port.flags & UPF_SHARE_IRQ)
|
|
enable_irq(up->port.irq);
|
|
spin_unlock(&up->port.lock);
|
|
#else
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
#endif
|
|
|
|
/*
|
|
* Finally, enable interrupts. Note: Modem status interrupts
|
|
* are set via set_termios(), which will be occurring imminently
|
|
* anyway, so we don't enable them here.
|
|
*/
|
|
up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_MSI;
|
|
serial_outp(up, UART_IER, up->ier);
|
|
|
|
if (up->port.flags & UPF_FOURPORT) {
|
|
unsigned int icp;
|
|
/*
|
|
* Enable interrupts on the AST Fourport board
|
|
*/
|
|
icp = (up->port.iobase & 0xfe0) | 0x01f;
|
|
outb_p(0x80, icp);
|
|
(void) inb_p(icp);
|
|
}
|
|
|
|
/*
|
|
* And clear the interrupt registers again for luck.
|
|
*/
|
|
(void) serial_inp(up, UART_LSR);
|
|
(void) serial_inp(up, UART_RX);
|
|
(void) serial_inp(up, UART_IIR);
|
|
(void) serial_inp(up, UART_MSR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void serial8250_shutdown(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
unsigned long flags;
|
|
|
|
/*
|
|
* Disable interrupts from this port
|
|
*/
|
|
up->ier = 0;
|
|
serial_outp(up, UART_IER, 0);
|
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
if (up->port.flags & UPF_FOURPORT) {
|
|
/* reset interrupts on the AST Fourport board */
|
|
inb((up->port.iobase & 0xfe0) | 0x1f);
|
|
up->port.mctrl |= TIOCM_OUT1;
|
|
} else
|
|
up->port.mctrl &= ~TIOCM_OUT2;
|
|
|
|
serial8250_set_mctrl(&up->port, up->port.mctrl);
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
|
|
/*
|
|
* Disable break condition and FIFOs
|
|
*/
|
|
serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
|
UART_FCR_CLEAR_RCVR |
|
|
UART_FCR_CLEAR_XMIT);
|
|
serial_outp(up, UART_FCR, 0);
|
|
|
|
#ifdef CONFIG_SERIAL_8250_RSA
|
|
/*
|
|
* Reset the RSA board back to 115kbps compat mode.
|
|
*/
|
|
disable_rsa(up);
|
|
#endif
|
|
|
|
/*
|
|
* Read data port to reset things, and then unlink from
|
|
* the IRQ chain.
|
|
*/
|
|
(void) serial_in(up, UART_RX);
|
|
|
|
if (!is_real_interrupt(up->port.irq))
|
|
del_timer_sync(&up->timer);
|
|
else
|
|
serial_unlink_irq_chain(up);
|
|
if(up->port.unused[1] & 0x02){
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 23)
|
|
dma_free_coherent(up->port.dev, RX_BUF_TOT_LEN,
|
|
up->rx_ring, up->rx_ring_dma);
|
|
dma_free_coherent(up->port.dev, TX_BUF_TOT_LEN,
|
|
up->tx_ring, up->tx_ring_dma);
|
|
#else
|
|
pci_free_consistent(up->port.dev, RX_BUF_TOT_LEN,
|
|
up->rx_ring, up->rx_ring_dma);
|
|
pci_free_consistent(up->port.dev, TX_BUF_TOT_LEN,
|
|
up->tx_ring, up->tx_ring_dma);
|
|
#endif
|
|
up->rx_ring = NULL;
|
|
up->tx_ring = NULL;
|
|
}
|
|
}
|
|
|
|
static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
|
|
{
|
|
unsigned int quot;
|
|
|
|
/*
|
|
* Handle magic divisors for baud rates above baud_base on
|
|
* SMSC SuperIO chips.
|
|
*/
|
|
if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
|
|
baud == (port->uartclk/4))
|
|
quot = 0x8001;
|
|
else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
|
|
baud == (port->uartclk/8))
|
|
quot = 0x8002;
|
|
else
|
|
{
|
|
//lipeng modify at 06/08/2006
|
|
//James.dai add UBUNTU_2_6_15 @V3.20 to support ubuntu6
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) || UBUNTU_2_6_15
|
|
quot = uart_get_divisor(port, baud);
|
|
#else
|
|
quot = adv_uart_get_divisor(port, baud);
|
|
#endif
|
|
//lipeng modify end
|
|
}
|
|
return quot;
|
|
}
|
|
|
|
static void
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
|
|
serial8250_set_termios(struct uart_port *port, struct termios *termios,
|
|
struct termios *old)
|
|
#else
|
|
serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
|
struct ktermios *old)
|
|
#endif
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
unsigned char cval, fcr = 0 ;
|
|
unsigned efr = 0;
|
|
unsigned long flags;
|
|
unsigned int baud, quot;
|
|
unsigned int dlldlm, cpr, tcr;
|
|
//unsigned int save_mcr, save_lcr, dll, dlm;
|
|
|
|
switch (termios->c_cflag & CSIZE) {
|
|
case CS5:
|
|
cval = 0x00;
|
|
break;
|
|
case CS6:
|
|
cval = 0x01;
|
|
break;
|
|
case CS7:
|
|
cval = 0x02;
|
|
break;
|
|
default:
|
|
case CS8:
|
|
cval = 0x03;
|
|
break;
|
|
}
|
|
|
|
if (termios->c_cflag & CSTOPB)
|
|
cval |= 0x04;
|
|
if (termios->c_cflag & PARENB)
|
|
cval |= UART_LCR_PARITY;
|
|
if (!(termios->c_cflag & PARODD))
|
|
cval |= UART_LCR_EPAR;
|
|
#ifdef CMSPAR
|
|
if (termios->c_cflag & CMSPAR)
|
|
cval |= UART_LCR_SPAR;
|
|
#endif
|
|
|
|
/*
|
|
* Ask the core to calculate the divisor for us.
|
|
*/
|
|
//lipeng modify at 06/08/2006
|
|
//James.dai add UBUNTU_2_6_15 @V3.20 to support ubuntu6
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) || UBUNTU_2_6_15
|
|
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
|
#else
|
|
baud = adv_uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
|
|
#endif
|
|
//lipeng modify end
|
|
quot = serial8250_get_divisor(port, baud);
|
|
|
|
/*
|
|
* Work around a bug in the Oxford Semiconductor 952 rev B
|
|
* chip which causes it to seriously miscalculate baud rates
|
|
* when DLL is 0.
|
|
*/
|
|
if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 &&
|
|
up->rev == 0x5201)
|
|
quot ++;
|
|
/* WangMao add the lines below @V3.20 */
|
|
/**** setup automatic flow control ****/
|
|
if(up->port.type == PORT_16C950)
|
|
{
|
|
//using DTR/DSR hardware control flow
|
|
if(termios->c_cflag & CDTRDSR)
|
|
{
|
|
//portdtr[up->port.line] = 1;
|
|
up->acr |= UART_ACR_DSRFC; //set ACR[2]=1
|
|
up->acr |= 0x08; //set ACR[3]=1
|
|
serial_icr_write(up, UART_ACR, up->acr);
|
|
//printk("set_termios:enable DTR/DSR:%X\n", up->acr);
|
|
}
|
|
|
|
//using CTR/CSR hardware control flow
|
|
efr = UART_EFR_ECB;
|
|
if (termios->c_cflag & CRTSCTS) //using hardware control flow
|
|
{
|
|
//enable CTS,RTS
|
|
efr |= UART_EFR_CTS | UART_EFR_RTS;
|
|
//printk("set_termios:enable RTS/CTS:%X\n", efr);
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
|
if(I_IXOFF(up->port.state->port.tty))
|
|
{
|
|
/* B=0x1011: enable signal charactor in-band transmit
|
|
XON = XON1 && XOFF = XOFF1 */
|
|
efr |= 0xB; // enable oxford 16c950 software flow control
|
|
// setup xon/xoff character
|
|
serial_out(up, UART_LCR, 0xBF);
|
|
serial_out(up, 0x4, START_CHAR(up->port.state->port.tty));
|
|
serial_out(up, 0x5, START_CHAR(up->port.state->port.tty));
|
|
serial_out(up, 0x6, STOP_CHAR(up->port.state->port.tty));
|
|
serial_out(up, 0x7, STOP_CHAR(up->port.state->port.tty));
|
|
serial_out(up, UART_LCR, 0x0);
|
|
//printk("set_termios:enable XOn/XOff:%X\n", efr);
|
|
}
|
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
|
|
if(I_IXOFF(up->port.info->port.tty))
|
|
{
|
|
/* B=0x1011: enable signal charactor in-band transmit
|
|
XON = XON1 && XOFF = XOFF1 */
|
|
efr |= 0xB; // enable oxford 16c950 software flow control
|
|
// setup xon/xoff character
|
|
serial_out(up, UART_LCR, 0xBF);
|
|
serial_out(up, 0x4, START_CHAR(up->port.info->port.tty));
|
|
serial_out(up, 0x5, START_CHAR(up->port.info->port.tty));
|
|
serial_out(up, 0x6, STOP_CHAR(up->port.info->port.tty));
|
|
serial_out(up, 0x7, STOP_CHAR(up->port.info->port.tty));
|
|
serial_out(up, UART_LCR, 0x0);
|
|
//printk("set_termios:enable XOn/XOff:%X\n", efr);
|
|
}
|
|
#else
|
|
|
|
if(I_IXOFF(up->port.info->tty))
|
|
{
|
|
/* B=0x1011: enable signal charactor in-band transmit
|
|
XON = XON1 && XOFF = XOFF1 */
|
|
efr |= 0xB; // enable oxford 16c950 software flow control
|
|
// setup xon/xoff character
|
|
serial_out(up, UART_LCR, 0xBF);
|
|
serial_out(up, 0x4, START_CHAR(up->port.info->tty));
|
|
serial_out(up, 0x5, START_CHAR(up->port.info->tty));
|
|
serial_out(up, 0x6, STOP_CHAR(up->port.info->tty));
|
|
serial_out(up, 0x7, STOP_CHAR(up->port.info->tty));
|
|
serial_out(up, UART_LCR, 0x0);
|
|
//printk("set_termios:enable XOn/XOff:%X\n", efr);
|
|
}
|
|
#endif
|
|
serial_out(up, UART_LCR, 0xBF);
|
|
serial_out(up, UART_EFR, efr);
|
|
//printk("set_termios: efr:%X\n", serial_inp(up, UART_EFR));
|
|
serial_out(up, UART_LCR, 0x0);
|
|
}
|
|
/* WangMao add end */
|
|
//printk("set_termios: will set TTL,RTL\n");
|
|
if (up->capabilities & UART_USE_FIFO) {
|
|
if (baud < 2400)
|
|
{
|
|
//printk("1 baud is %d\n",baud);
|
|
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
|
|
//WangMao add the lines below to enable trigger level
|
|
//the trigger level of 16C950 is set by TTL,and RTL
|
|
if(up->port.type == PORT_16C950)
|
|
{
|
|
serial_icr_write(up,UART_TTL,0);
|
|
RTL = 2;
|
|
serial_icr_write(up,UART_RTL,2);
|
|
}//WangMao add end
|
|
}
|
|
#ifdef CONFIG_SERIAL_8250_RSA
|
|
else if (up->port.type == PORT_RSA)
|
|
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
|
|
#endif
|
|
else
|
|
{
|
|
/* WangMao fixed the custom baud rate @V3_20*/
|
|
if((baud == 38400) &&
|
|
((port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)&&
|
|
(921600/quot<2400))
|
|
{
|
|
//printk("2 baud is %d\n",921600/quot);
|
|
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
|
|
if(up->port.type == PORT_16C950)
|
|
{
|
|
serial_icr_write(up,UART_TTL,0);
|
|
RTL = 2;
|
|
serial_icr_write(up,UART_RTL,2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//printk("3 baud is %d\n",921600/quot);
|
|
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
|
|
if(up->port.type == PORT_16C950)
|
|
{
|
|
serial_icr_write(up,UART_TTL,0);
|
|
RTL = 32;
|
|
serial_icr_write(up,UART_RTL,32);
|
|
}
|
|
} //WangMao add end
|
|
if((baud == 38400) &&
|
|
((port->flags & UPF_SPD_MASK) == UPF_SPD_CUST))
|
|
{
|
|
if(up->port.unused[1] & 0x01)
|
|
{
|
|
baud = 921600 / quot;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
/* WangMao add these @V3.20
|
|
no UART_USE_FIFO flag,it means the hardware depth is 1,so not enable FIFO mode,no need to set TTL,RTL */
|
|
else
|
|
fcr = UART_FCR_TRIGGER_1;
|
|
if(quick_response)
|
|
serial_icr_write(up,UART_RTL,2);
|
|
else{
|
|
if(up->port.unused[1] & 0x02)
|
|
RTL = 70;
|
|
else
|
|
RTL = 70;//previous value of RTL is 2,but the performance is weak
|
|
serial_icr_write(up,UART_RTL,RTL);
|
|
}
|
|
//WangMao add end
|
|
/*
|
|
* TI16C750: hardware flow control and 64 byte FIFOs. When AFE is
|
|
* enabled, RTS will be deasserted when the receive FIFO contains
|
|
* more characters than the trigger, or the MCR RTS bit is cleared.
|
|
*/
|
|
if (up->port.type == PORT_16750) {
|
|
/*
|
|
* modify by lipeng 06.02.28. sles 9( kernel 2.6.5-7.97-default) not define UART_MCR_AFE
|
|
*/
|
|
#ifdef UART_MCR_AFE
|
|
up->mcr &= ~UART_MCR_AFE;
|
|
if (termios->c_cflag & CRTSCTS)
|
|
up->mcr |= UART_MCR_AFE;
|
|
#endif
|
|
fcr |= UART_FCR7_64BYTE;
|
|
//WangMao add the line below @V3.21
|
|
serial_outp(up, UART_FCR, fcr); /* set fcr */
|
|
}
|
|
else //WangMao add these @V3.21
|
|
{
|
|
if (fcr & UART_FCR_ENABLE_FIFO)
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
|
serial_outp(up, UART_FCR, fcr); /* set fcr */
|
|
}
|
|
|
|
/*
|
|
* Ok, we're now changing the port state. Do it with
|
|
* interrupts disabled.
|
|
*/
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
|
|
/*
|
|
* Update the per-port timeout.
|
|
*/
|
|
//lipeng modify at 06/08/2006
|
|
//James.dai add UBUNTU_2_6_15 @V3.20 to support ubuntu6
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) || UBUNTU_2_6_15
|
|
uart_update_timeout(port, termios->c_cflag, baud);
|
|
#else
|
|
adv_uart_update_timeout(port, termios->c_cflag, baud);
|
|
#endif
|
|
//lipeng modify end
|
|
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
|
|
if (termios->c_iflag & INPCK)
|
|
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
|
|
if (termios->c_iflag & (BRKINT | PARMRK))
|
|
up->port.read_status_mask |= UART_LSR_BI;
|
|
|
|
/*
|
|
* Characteres to ignore
|
|
*/
|
|
up->port.ignore_status_mask = 0;
|
|
if (termios->c_iflag & IGNPAR)
|
|
up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
|
|
if (termios->c_iflag & IGNBRK) {
|
|
up->port.ignore_status_mask |= UART_LSR_BI;
|
|
/*
|
|
* If we're ignoring parity and break indicators,
|
|
* ignore overruns too (for real raw support).
|
|
*/
|
|
if (termios->c_iflag & IGNPAR)
|
|
up->port.ignore_status_mask |= UART_LSR_OE;
|
|
}
|
|
|
|
/*
|
|
* ignore all characters if CREAD is not set
|
|
*/
|
|
if ((termios->c_cflag & CREAD) == 0)
|
|
up->port.ignore_status_mask |= UART_LSR_DR;
|
|
|
|
/*
|
|
* CTS flow control flag and modem status interrupts
|
|
*/
|
|
up->ier &= ~UART_IER_MSI;
|
|
if (UART_ENABLE_MS(&up->port, termios->c_cflag))
|
|
up->ier |= UART_IER_MSI;
|
|
/*
|
|
*modify by lipeng, 06.02.28. sles 9(kernel 2.6.5-7.97-default) not define PORT_XSCALE
|
|
*/
|
|
#ifdef PORT_XSCALE
|
|
if (up->port.type == PORT_XSCALE)
|
|
up->ier |= UART_IER_UUE | UART_IER_RTOIE;
|
|
#endif
|
|
///lipeng
|
|
//lipeng mask this line and move to end of this function at 01/04/2008
|
|
//serial_out(up, UART_IER, up->ier);
|
|
|
|
if (up->capabilities & UART_STARTECH) {
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
serial_outp(up, UART_EFR,
|
|
termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0);
|
|
}
|
|
|
|
if (up->capabilities & UART_NATSEMI) {
|
|
/* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */
|
|
serial_outp(up, UART_LCR, 0xe0);
|
|
} else {
|
|
serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
|
|
}
|
|
|
|
if(up->port.unused[1] & 0x01){
|
|
//printk(KERN_INFO "quot=%x,", quot);
|
|
////////////////////
|
|
tcr = 4;
|
|
if ( quot >= 4)
|
|
{
|
|
cpr = 32;
|
|
}
|
|
else
|
|
{
|
|
cpr = 8;
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
|
dlldlm = (int)((10*(3906250*16)/(tcr*cpr*baud/8)+5)/10);
|
|
#else
|
|
dlldlm = (int)((10*(3906250*16)/(tcr*cpr*baud/8))/10);
|
|
#endif
|
|
if ( baud < 19200)
|
|
{
|
|
dlldlm = dlldlm - 1;
|
|
}
|
|
|
|
////////////////////////
|
|
//dlldlm = quot & 0xffff;
|
|
//quot = quot >> 16;
|
|
//cpr = quot & 0xff;
|
|
//tcr = quot >> 8 & 0x0f;
|
|
//cpr20 = cpr & 0x07;
|
|
//cpr73 = cpr >> 3 & 0x1f;
|
|
//printk(KERN_INFO "cpr=%x, cpr20=%x, cpr73=%x, after=%x,", cpr, cpr20,cpr73, (cpr73 << 3)|cpr20);
|
|
//set the registers
|
|
serial_outp(up, UART_LCR, up->lcr | UART_LCR_DLAB); /* set DLAB */
|
|
serial_outp(up, UART_DLL, dlldlm & 0xff); /* LS of divisor */
|
|
serial_outp(up, UART_DLM, dlldlm >> 8);
|
|
serial_icr_write(up, UART_TCR, (tcr & 0x0F)); /* TCR[3:0]=SC */
|
|
serial_icr_write(up, UART_CPR, cpr); /* CPR[7:3]=M */
|
|
}
|
|
else{
|
|
serial_outp(up, UART_LCR, up->lcr | UART_LCR_DLAB); /* set DLAB */
|
|
serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */
|
|
serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */
|
|
}
|
|
/*
|
|
* LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
|
|
* is written without DLAB set, this mode will be disabled.
|
|
*/
|
|
if (up->port.type == PORT_16750)
|
|
serial_outp(up, UART_FCR, fcr);
|
|
|
|
serial_outp(up, UART_LCR, cval); /* reset DLAB */
|
|
up->lcr = cval; /* Save LCR */
|
|
if (up->port.type != PORT_16750) {
|
|
if (fcr & UART_FCR_ENABLE_FIFO) {
|
|
/* emulated UARTs (Lucent Venus 167x) need two steps */
|
|
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
|
}
|
|
serial_outp(up, UART_FCR, fcr); /* set fcr */
|
|
}
|
|
serial8250_set_mctrl(&up->port, up->port.mctrl);
|
|
serial_out(up, UART_IER, up->ier);
|
|
/*
|
|
printk(KERN_INFO "ACR1=%x\n", serial_icr_read(up, UART_ACR));
|
|
//serial_icr_write(up, UART_ACR, 0x60);
|
|
serial_out(up, UART_SCR, UART_ACR);
|
|
serial_out(up, UART_ICR, 0x60);
|
|
printk(KERN_INFO "ACR2=%x\n", serial_icr_read(up, UART_ACR));
|
|
*/
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
|
|
/* for debug ...
|
|
printk("change_speed: MCR:%X\n", serial_in(up, UART_MCR));
|
|
printk("change_speed: ACR is %X\n",serial_icr_read(up,UART_ACR));
|
|
printk("change_speed: IER:%X\n", serial_in(up, UART_IER));
|
|
printk("change_speed: RTL is %X\n",serial_icr_read(up,UART_RTL));
|
|
serial_out(up, UART_LCR, 0xBF);
|
|
printk("change_speed: EFR:%X\n", serial_in(up, UART_EFR));
|
|
serial_out(up, UART_LCR, 0x0);
|
|
printk("change_speed: FCR:%X\n", serial_in(up, UART_FCR)); */
|
|
|
|
//jinxin added for debug...
|
|
/*
|
|
save_lcr = serial_inp(up, UART_LCR);
|
|
serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB);
|
|
dll = serial_inp(up, UART_DLL);
|
|
dlm = serial_inp(up, UART_DLM);
|
|
serial_outp(up, UART_LCR, save_lcr);
|
|
tcr = serial_icr_read(up, UART_TCR);
|
|
cpr = serial_icr_read(up, UART_CPR);
|
|
save_mcr = serial_inp(up, UART_MCR);
|
|
printk(KERN_INFO "tcr=0x%x, cpr=0x%x, dlm=0x%x, dll=0x%x, save_mcr=0x%x, uart_clk=%d\n", tcr, cpr, dlm, dll, save_mcr, up->port.uartclk);*/
|
|
}
|
|
|
|
static void
|
|
serial8250_pm(struct uart_port *port, unsigned int state,
|
|
unsigned int oldstate)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
if (state) {
|
|
/* sleep */
|
|
if (up->capabilities & UART_STARTECH) {
|
|
/* Arrange to enter sleep mode */
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
serial_outp(up, UART_EFR, UART_EFR_ECB);
|
|
serial_outp(up, UART_LCR, 0);
|
|
serial_outp(up, UART_IER, UART_IERX_SLEEP);
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
serial_outp(up, UART_EFR, 0);
|
|
serial_outp(up, UART_LCR, 0);
|
|
}
|
|
if (up->port.type == PORT_16750) {
|
|
/* Arrange to enter sleep mode */
|
|
serial_outp(up, UART_IER, UART_IERX_SLEEP);
|
|
}
|
|
|
|
if (up->pm)
|
|
up->pm(port, state, oldstate);
|
|
} else {
|
|
/* wake */
|
|
if (up->capabilities & UART_STARTECH) {
|
|
/* Wake up UART */
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
serial_outp(up, UART_EFR, UART_EFR_ECB);
|
|
/*
|
|
* Turn off LCR == 0xBF so we actually set the IER
|
|
* register on the XR16C850
|
|
*/
|
|
serial_outp(up, UART_LCR, 0);
|
|
serial_outp(up, UART_IER, 0);
|
|
/*
|
|
* Now reset LCR so we can turn off the ECB bit
|
|
*/
|
|
serial_outp(up, UART_LCR, 0xBF);
|
|
serial_outp(up, UART_EFR, 0);
|
|
/*
|
|
* For a XR16C850, we need to set the trigger levels
|
|
*/
|
|
if (up->port.type == PORT_16850) {
|
|
unsigned char fctr;
|
|
|
|
fctr = serial_inp(up, UART_FCTR) &
|
|
~(UART_FCTR_RX | UART_FCTR_TX);
|
|
serial_outp(up, UART_FCTR, fctr |
|
|
UART_FCTR_TRGD |
|
|
UART_FCTR_RX);
|
|
serial_outp(up, UART_TRG, UART_TRG_96);
|
|
serial_outp(up, UART_FCTR, fctr |
|
|
UART_FCTR_TRGD |
|
|
UART_FCTR_TX);
|
|
serial_outp(up, UART_TRG, UART_TRG_96);
|
|
}
|
|
serial_outp(up, UART_LCR, 0);
|
|
}
|
|
|
|
if (up->port.type == PORT_16750) {
|
|
/* Wake up UART */
|
|
serial_outp(up, UART_IER, 0);
|
|
}
|
|
|
|
if (up->pm)
|
|
up->pm(port, state, oldstate);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Resource handling. This is complicated by the fact that resources
|
|
* depend on the port type. Maybe we should be claiming the standard
|
|
* 8250 ports, and then trying to get other resources as necessary?
|
|
*/
|
|
static int
|
|
serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res)
|
|
{
|
|
unsigned int size = 8 << up->port.regshift;
|
|
int ret = 0;
|
|
|
|
switch (up->port.iotype) {
|
|
case UPIO_MEM:
|
|
if (up->port.mapbase) {
|
|
*res = request_mem_region(up->port.mapbase, size, "adv950");
|
|
if (!*res)
|
|
ret = -EBUSY;
|
|
}
|
|
break;
|
|
|
|
case UPIO_HUB6:
|
|
case UPIO_PORT:
|
|
*res = request_region(up->port.iobase, size, "adv950");
|
|
if (!*res)
|
|
ret = -EBUSY;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res)
|
|
{
|
|
unsigned int size = 8 << up->port.regshift;
|
|
unsigned long start;
|
|
int ret = 0;
|
|
|
|
switch (up->port.iotype) {
|
|
case UPIO_MEM:
|
|
if (up->port.mapbase) {
|
|
start = up->port.mapbase;
|
|
start += UART_RSA_BASE << up->port.regshift;
|
|
*res = request_mem_region(start, size, "serial-rsa");
|
|
if (!*res)
|
|
ret = -EBUSY;
|
|
}
|
|
break;
|
|
|
|
case UPIO_HUB6:
|
|
case UPIO_PORT:
|
|
start = up->port.iobase;
|
|
start += UART_RSA_BASE << up->port.regshift;
|
|
*res = request_region(start, size, "serial-rsa");
|
|
if (!*res)
|
|
ret = -EBUSY;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void serial8250_release_port(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
unsigned long start, offset = 0, size = 0;
|
|
|
|
//if (!(up->port.flags & UPF_RESOURCES))
|
|
// return;
|
|
if (up->port.type == PORT_RSA) {
|
|
offset = UART_RSA_BASE << up->port.regshift;
|
|
size = 8;
|
|
}
|
|
|
|
size <<= up->port.regshift;
|
|
|
|
switch (up->port.iotype) {
|
|
case UPIO_MEM:
|
|
if (up->port.mapbase) {
|
|
if (up->port.flags & UPF_IOREMAP) {
|
|
/*
|
|
* Unmap the area.
|
|
*/
|
|
iounmap(up->port.membase);
|
|
up->port.membase = NULL;
|
|
}
|
|
|
|
start = up->port.mapbase;
|
|
|
|
if (size)
|
|
release_mem_region(start + offset, size);
|
|
release_mem_region(start, 8 << up->port.regshift);
|
|
}
|
|
break;
|
|
|
|
case UPIO_HUB6:
|
|
case UPIO_PORT:
|
|
start = up->port.iobase;
|
|
|
|
if (size)
|
|
release_region(start + offset, size);
|
|
release_region(start + offset, 8 << up->port.regshift);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int serial8250_request_port(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
struct resource *res = NULL, *res_rsa = NULL;
|
|
int ret = 0;
|
|
printk("serial8250_request_port\n");
|
|
//if (up->port.flags & UPF_RESOURCES) {
|
|
if (up->port.type == PORT_RSA) {
|
|
ret = serial8250_request_rsa_resource(up, &res_rsa);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
ret = serial8250_request_std_resource(up, &res);
|
|
//}
|
|
|
|
/*
|
|
* If we have a mapbase, then request that as well.
|
|
*/
|
|
if (ret == 0 && up->port.flags & UPF_IOREMAP) {
|
|
int size = res->end - res->start + 1;
|
|
|
|
up->port.membase = ioremap(up->port.mapbase, size);
|
|
if (!up->port.membase)
|
|
ret = -ENOMEM;
|
|
}
|
|
|
|
if (ret < 0) {
|
|
if (res_rsa)
|
|
release_resource(res_rsa);
|
|
if (res)
|
|
release_resource(res);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void serial8250_config_port(struct uart_port *port, int flags)
|
|
{
|
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
|
struct resource *res_std = NULL, *res_rsa = NULL;
|
|
int probeflags = PROBE_ANY;
|
|
int ret;
|
|
|
|
#ifdef CONFIG_MCA
|
|
/*
|
|
* Don't probe for MCA ports on non-MCA machines.
|
|
*/
|
|
if (up->port.flags & UPF_BOOT_ONLYMCA && !MCA_bus)
|
|
return;
|
|
#endif
|
|
//printk("serial8250_config_port\n");
|
|
/*
|
|
* Find the region that we can probe for. This in turn
|
|
* tells us whether we can probe for the type of port.
|
|
*/
|
|
//if (up->port.flags & UPF_RESOURCES) {
|
|
ret = serial8250_request_std_resource(up, &res_std);
|
|
if (ret < 0)
|
|
return;
|
|
|
|
ret = serial8250_request_rsa_resource(up, &res_rsa);
|
|
if (ret < 0)
|
|
probeflags &= ~PROBE_RSA;
|
|
//} else {
|
|
// probeflags &= ~PROBE_RSA;
|
|
//}
|
|
|
|
if (flags & UART_CONFIG_TYPE)
|
|
autoconfig(up, probeflags);
|
|
if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
|
|
autoconfig_irq(up);
|
|
|
|
/*
|
|
* If the port wasn't an RSA port, release the resource.
|
|
*/
|
|
if (up->port.type != PORT_RSA && res_rsa)
|
|
release_resource(res_rsa);
|
|
|
|
if (up->port.type == PORT_UNKNOWN && res_std)
|
|
release_resource(res_std);
|
|
}
|
|
|
|
static int
|
|
serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
|
|
{
|
|
if (ser->irq >= NR_IRQS || ser->irq < 0 ||
|
|
ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
|
|
ser->type > PORT_MAX_8250 || ser->type == PORT_CIRRUS ||
|
|
ser->type == PORT_STARTECH)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
serial8250_type(struct uart_port *port)
|
|
{
|
|
int type = port->type;
|
|
|
|
if (type >= ARRAY_SIZE(uart_config))
|
|
type = 0;
|
|
return uart_config[type].name;
|
|
}
|
|
|
|
static struct uart_ops serial8250_pops = {
|
|
.tx_empty = serial8250_tx_empty,
|
|
.set_mctrl = serial8250_set_mctrl,
|
|
.get_mctrl = serial8250_get_mctrl,
|
|
.stop_tx = serial8250_stop_tx,
|
|
.start_tx = serial8250_start_tx,
|
|
.stop_rx = serial8250_stop_rx,
|
|
.enable_ms = serial8250_enable_ms,
|
|
.break_ctl = serial8250_break_ctl,
|
|
.startup = serial8250_startup,
|
|
.shutdown = serial8250_shutdown,
|
|
.set_termios = serial8250_set_termios,
|
|
.pm = serial8250_pm,
|
|
.type = serial8250_type,
|
|
.release_port = serial8250_release_port,
|
|
.request_port = serial8250_request_port,
|
|
.config_port = serial8250_config_port,
|
|
.verify_port = serial8250_verify_port,
|
|
};
|
|
|
|
struct uart_8250_port serial8250_ports[UART_NR];
|
|
|
|
static void __init serial8250_isa_init_ports(void)
|
|
{
|
|
struct uart_8250_port *up;
|
|
static int first = 1;
|
|
int i;
|
|
|
|
if (!first)
|
|
return;
|
|
first = 0;
|
|
|
|
for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port);
|
|
i++, up++) {
|
|
up->port.iobase = old_serial_port[i].port;
|
|
up->port.irq = irq_canonicalize(old_serial_port[i].irq);
|
|
up->port.uartclk = old_serial_port[i].baud_base * 16;
|
|
up->port.flags = old_serial_port[i].flags;// |
|
|
//UPF_RESOURCES;
|
|
up->port.hub6 = old_serial_port[i].hub6;
|
|
up->port.membase = old_serial_port[i].iomem_base;
|
|
up->port.iotype = old_serial_port[i].io_type;
|
|
up->port.regshift = old_serial_port[i].iomem_reg_shift;
|
|
up->port.ops = &serial8250_pops;
|
|
if (share_irqs)
|
|
up->port.flags |= UPF_SHARE_IRQ;
|
|
}
|
|
}
|
|
|
|
|
|
static void __init serial8250_register_ports(struct uart_driver *drv)
|
|
{
|
|
int i;
|
|
|
|
//serial8250_isa_init_ports();
|
|
|
|
for (i = 0; i < UART_NR; i++) {
|
|
struct uart_8250_port *up = &serial8250_ports[i];
|
|
|
|
up->port.line = i;
|
|
up->port.ops = &serial8250_pops;
|
|
init_timer(&up->timer);
|
|
up->timer.function = serial8250_timeout;
|
|
|
|
/*
|
|
* ALPHA_KLUDGE_MCR needs to be killed.
|
|
*/
|
|
up->mcr_mask = ~ALPHA_KLUDGE_MCR;
|
|
up->mcr_force = ALPHA_KLUDGE_MCR;
|
|
|
|
adv_uart_add_one_port(drv, &up->port);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
|
|
|
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
|
|
|
|
/*
|
|
* Wait for transmitter & holding register to empty
|
|
*/
|
|
static inline void wait_for_xmitr(struct uart_8250_port *up)
|
|
{
|
|
unsigned int status, tmout = 10000;
|
|
|
|
/* Wait up to 10ms for the character(s) to be sent. */
|
|
do {
|
|
status = serial_in(up, UART_LSR);
|
|
|
|
if (status & UART_LSR_BI)
|
|
up->lsr_break_flag = UART_LSR_BI;
|
|
|
|
if (--tmout == 0)
|
|
break;
|
|
udelay(1);
|
|
} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
|
|
|
|
/* Wait up to 1s for flow control if necessary */
|
|
if (up->port.flags & UPF_CONS_FLOW) {
|
|
tmout = 1000000;
|
|
while (--tmout &&
|
|
((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
|
|
udelay(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print a string to the serial port trying not to disturb
|
|
* any possible real use of the port...
|
|
*
|
|
* The console_lock must be held when we get here.
|
|
*/
|
|
static void
|
|
serial8250_console_write(struct console *co, const char *s, unsigned int count)
|
|
{
|
|
struct uart_8250_port *up = &serial8250_ports[co->index];
|
|
unsigned int ier;
|
|
int i;
|
|
|
|
/*
|
|
* First save the UER then disable the interrupts
|
|
*/
|
|
ier = serial_in(up, UART_IER);
|
|
|
|
/*
|
|
*modify by lipeng, 06.02.28. sles 9(kernel 2.6.5-7.97-default) not define PORT_XSCALE
|
|
*/
|
|
#ifdef PORT_XSCALE
|
|
if (up->port.type == PORT_XSCALE)
|
|
serial_out(up, UART_IER, UART_IER_UUE);
|
|
else
|
|
serial_out(up, UART_IER, 0);
|
|
#else
|
|
serial_out(up, UART_IER, 0);
|
|
#endif
|
|
////lipeng
|
|
/*
|
|
* Now, do each character
|
|
*/
|
|
for (i = 0; i < count; i++, s++) {
|
|
wait_for_xmitr(up);
|
|
|
|
/*
|
|
* Send the character out.
|
|
* If a LF, also do CR...
|
|
*/
|
|
serial_out(up, UART_TX, *s);
|
|
if (*s == 10) {
|
|
wait_for_xmitr(up);
|
|
serial_out(up, UART_TX, 13);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Finally, wait for transmitter to become empty
|
|
* and restore the IER
|
|
*/
|
|
wait_for_xmitr(up);
|
|
serial_out(up, UART_IER, ier);
|
|
}
|
|
|
|
static int __init serial8250_console_setup(struct console *co, char *options)
|
|
{
|
|
struct uart_port *port;
|
|
int baud = 9600;
|
|
int bits = 8;
|
|
int parity = 'n';
|
|
int flow = 'n';
|
|
|
|
/*
|
|
* Check whether an invalid uart number has been specified, and
|
|
* if so, search for the first available port that does have
|
|
* console support.
|
|
*/
|
|
if (co->index >= UART_NR)
|
|
co->index = 0;
|
|
port = &serial8250_ports[co->index].port;
|
|
if (!port->ops)
|
|
return -ENODEV;
|
|
|
|
/*
|
|
* Temporary fix.
|
|
*/
|
|
spin_lock_init(&port->lock);
|
|
|
|
if (options)
|
|
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
|
|
|
return uart_set_options(port, co, baud, parity, bits, flow);
|
|
}
|
|
|
|
extern struct uart_driver serial8250_reg;
|
|
static struct console serial8250_console = {
|
|
.name = SERIAL_NAME,
|
|
.write = serial8250_console_write,
|
|
.device = uart_console_device,
|
|
.setup = serial8250_console_setup,
|
|
.flags = CON_PRINTBUFFER,
|
|
.index = -1,
|
|
.data = &serial8250_reg,
|
|
};
|
|
|
|
static int __init serial8250_console_init(void)
|
|
{
|
|
serial8250_isa_init_ports();
|
|
register_console(&serial8250_console);
|
|
return 0;
|
|
}
|
|
console_initcall(serial8250_console_init);
|
|
|
|
static int __init serial8250_late_console_init(void)
|
|
{
|
|
if (!(serial8250_console.flags & CON_ENABLED))
|
|
register_console(&serial8250_console);
|
|
return 0;
|
|
}
|
|
late_initcall(serial8250_late_console_init);
|
|
|
|
#define SERIAL8250_CONSOLE &serial8250_console
|
|
#else
|
|
#define SERIAL8250_CONSOLE NULL
|
|
#endif
|
|
|
|
#ifndef ADV_TTY_MAJOR
|
|
#define ADV_TTY_MAJOR 0
|
|
#endif
|
|
#ifdef TTY_MAJOR
|
|
#undef TTY_MAJOR
|
|
#define TTY_MAJOR ADV_TTY_MAJOR
|
|
#endif
|
|
static struct uart_driver serial8250_reg = {
|
|
.owner = THIS_MODULE,
|
|
.driver_name = "adv950",
|
|
/* .devfs_name = "tts/",
|
|
.dev_name = "ttyS",*/
|
|
//.devfs_name = "ttp/",
|
|
.dev_name = SERIAL_NAME,
|
|
.major = TTY_MAJOR,
|
|
/*.minor = 64,*/
|
|
.minor = 0,
|
|
.nr = UART_NR,
|
|
.cons = SERIAL8250_CONSOLE,
|
|
};
|
|
|
|
/*
|
|
* register_serial and unregister_serial allows for 16x50 serial ports to be
|
|
* configured at run-time, to support PCMCIA modems.
|
|
*/
|
|
|
|
static int __register_serial(struct serial_struct *req, int line)
|
|
{
|
|
struct uart_port port;
|
|
|
|
port.iobase = req->port;
|
|
port.membase = req->iomem_base;
|
|
port.irq = req->irq;
|
|
port.uartclk = req->baud_base * 16;
|
|
port.fifosize = req->xmit_fifo_size;
|
|
port.regshift = req->iomem_reg_shift;
|
|
port.iotype = req->io_type;
|
|
port.flags = req->flags | UPF_BOOT_AUTOCONF;
|
|
port.mapbase = req->iomap_base;
|
|
port.unused[1] = (unsigned char)req->type;
|
|
port.line = line;
|
|
// Default ACR value for auto DTR RS485 or RS232
|
|
port.unused[0] = req->reserved_char[0];
|
|
|
|
if (share_irqs)
|
|
port.flags |= UPF_SHARE_IRQ;
|
|
|
|
if (HIGH_BITS_OFFSET)
|
|
port.iobase |= (long) req->port_high << HIGH_BITS_OFFSET;
|
|
|
|
/*
|
|
* If a clock rate wasn't specified by the low level
|
|
* driver, then default to the standard clock rate.
|
|
*/
|
|
if (port.uartclk == 0)
|
|
port.uartclk = BASE_BAUD * 16;
|
|
|
|
return adv_uart_register_port(&serial8250_reg, &port);
|
|
}
|
|
|
|
/**
|
|
* register_serial - configure a 16x50 serial port at runtime
|
|
* @req: request structure
|
|
*
|
|
* Configure the serial port specified by the request. If the
|
|
* port exists and is in use an error is returned. If the port
|
|
* is not currently in the table it is added.
|
|
*
|
|
* The port is then probed and if necessary the IRQ is autodetected
|
|
* If this fails an error is returned.
|
|
*
|
|
* On success the port is ready to use and the line number is returned.
|
|
*/
|
|
int adv_register_serial(struct serial_struct *req)
|
|
{
|
|
return __register_serial(req, -1);
|
|
}
|
|
|
|
int __init early_serial_setup(struct uart_port *port)
|
|
{
|
|
if (port->line >= ARRAY_SIZE(serial8250_ports))
|
|
return -ENODEV;
|
|
|
|
serial8250_isa_init_ports();
|
|
serial8250_ports[port->line].port = *port;
|
|
serial8250_ports[port->line].port.ops = &serial8250_pops;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* unregister_serial - remove a 16x50 serial port at runtime
|
|
* @line: serial line number
|
|
*
|
|
* Remove one serial port. This may be called from interrupt
|
|
* context.
|
|
*/
|
|
void adv_unregister_serial(int line)
|
|
{
|
|
adv_uart_unregister_port(&serial8250_reg, line);
|
|
}
|
|
|
|
/*
|
|
* This is for ISAPNP only.
|
|
*/
|
|
void serial8250_get_irq_map(unsigned int *map)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < UART_NR; i++) {
|
|
if (serial8250_ports[i].port.type != PORT_UNKNOWN &&
|
|
serial8250_ports[i].port.irq < 16)
|
|
*map |= 1 << serial8250_ports[i].port.irq;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* serial8250_suspend_port - suspend one serial port
|
|
* @line: serial line number
|
|
* @level: the level of port suspension, as per uart_suspend_port
|
|
*
|
|
* Suspend one serial port.
|
|
*/
|
|
void adv_serial8250_suspend_port(int line)
|
|
{
|
|
adv_uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port);
|
|
}
|
|
|
|
/**
|
|
* serial8250_resume_port - resume one serial port
|
|
* @line: serial line number
|
|
* @level: the level of port resumption, as per uart_resume_port
|
|
*
|
|
* Resume one serial port.
|
|
*/
|
|
void adv_serial8250_resume_port(int line)
|
|
{
|
|
adv_uart_resume_port(&serial8250_reg, &serial8250_ports[line].port);
|
|
}
|
|
|
|
|
|
//static int __init adv_serial8250_init(void)
|
|
int adv_serial8250_init(void)
|
|
{
|
|
int ret, i;
|
|
|
|
/*printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ "
|
|
"%d ports, IRQ sharing %sabled\n", (int) UART_NR,
|
|
share_irqs ? "en" : "dis");*/
|
|
|
|
for (i = 0; i < NR_IRQS; i++)
|
|
spin_lock_init(&irq_lists[i].lock);
|
|
|
|
ret = adv_uart_register_driver(&serial8250_reg);
|
|
if (ret >= 0)
|
|
serial8250_register_ports(&serial8250_reg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//static void __exit adv_serial8250_exit(void)
|
|
void adv_serial8250_exit(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < UART_NR; i++)
|
|
adv_uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port);
|
|
|
|
adv_uart_unregister_driver(&serial8250_reg);
|
|
}
|
|
|
|
/*
|
|
module_init(adv_serial8250_init);
|
|
module_exit(adv_serial8250_exit);
|
|
*/
|
|
EXPORT_SYMBOL(adv_serial8250_init);
|
|
EXPORT_SYMBOL(adv_serial8250_exit);
|
|
EXPORT_SYMBOL(adv_register_serial);
|
|
EXPORT_SYMBOL(adv_unregister_serial);
|
|
/*EXPORT_SYMBOL(serial8250_get_irq_map);*/
|
|
EXPORT_SYMBOL(adv_serial8250_suspend_port);
|
|
EXPORT_SYMBOL(adv_serial8250_resume_port);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Advantech IAG PCI-954/16C950 serial driver");
|
|
|
|
module_param(share_irqs, uint, 0644);
|
|
MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
|
|
" (unsafe)");
|
|
|
|
#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
|
|
MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
|
|
MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
|
|
MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
|
|
MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA");
|
|
#endif
|
|
MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
|