杨振 3 سال پیش
کامیت
4b0212b7b7
3فایلهای تغییر یافته به همراه2734 افزوده شده و 0 حذف شده
  1. 19 0
      Makefile
  2. 175 0
      readme
  3. 2540 0
      xr17v35x.c

+ 19 - 0
Makefile

@@ -0,0 +1,19 @@
+#makefile for XR17v35x PCIe UARTs for Linux 2.6.32 and newer
+KERNEL_SRC = /lib/modules/`uname -r`/build
+
+all: build
+
+obj-m += xr17v35x.o
+
+xrpci-objs :=	xr17v35x.o
+
+EXTRA_CFLAGS +=  -DDEBUG=1
+
+build:
+	$(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules
+
+install:
+	cp xr17v35x.ko /lib/modules/$(shell uname -r)/kernel/drivers/char
+clean:
+	rm -f *~ *.o
+	rm -f *~ *.ko

+ 175 - 0
readme

@@ -0,0 +1,175 @@
+=============================================================================
+
+        MaxLinear PCIe Multiport Device Driver Ver 2.6 Installation Guide
+         for Linux Kernel 2.6.32 and newer (Tested on various kernels from 2.6.32 to 4.15.x)
+			Copyright (C) 2019, MaxLinear.
+=============================================================================
+
+Note: Exar is now part of MaxLinear. All description below will be referred as MaxLinear/Exar.
+
+Revision History
+----------------
+Changes from Ver 2.5 to 2.6
+- Ensure LCR bit-7 = 0 when accessing RHR/THR/IER/ISR to avoid incorrect register write.
+- Remove warning messages when building with gcc 7.4.0 (Ubuntu 18.04).
+
+Changes from Ver 2.4 to 2.5
+- Improve baud the accuracy on 12Mbps.
+- Add option to  select for RS485 control between DTR# and RTS# pin. 
+- Add timer function patch for Kernel 4.15 and later support
+
+Changes from Ver 2.3 to 2.4
+- Add INT0 read for "nobody cares" interrupt issue
+
+Releasing 2.2c as version 2.3 (no changes from 2.2c)
+
+Changes from Ver 2.2b to 2.2c
+- Macro XR_17v35x_UART_RHR was not defined. Issue fixed.
+
+Changes from Ver 2.2a to 2.2b
+- RS-485 was not getting disabled correctly. Issue Fixed. 
+ 
+Changes from Ver 2.2 to 2.2a
+- For receiving data, replaced memcpy_fromio() routine with serial_in(). This was done because the former was dropping data on receive. 
+
+Changes from Ver 2.1 to 2.2
+- Added support for non standard baud rates
+- Configure MPIO's as inputs.
+- Added a parameter to enable disable internal loopback
+- Fixed bug related to Fractional buad rate generator.
+- RS485 disabled by default. 
+- To enable RS485 configure the parameter ENABLE_RS485
+
+Changes from Ver 2.0 to 2.1
+- Added support for Software flow control and Auto Hardware flow control
+- Enabled Auto RS 485
+- Modified logic for  Fractional baud rate generator
+- Added Support for XR17D15x devices
+
+Changes from Ver 1.5 to 2.0 (version 1.6 - 1.9 are unofficial versions, never posted on website):
+- Fixed issue when un-registering ports (rmmod) that prevented re-registering all of the ports (insmod)
+- Added logic in interrupt service to ensure that LCR bit-7 = 0 before reading ISR/IIR
+- Added support for 4X Sampling Mode, 8X Sampling Mode, 9-bit (Multidrop) Mode, RS-485 Mode
+- Added support for 2.6.32 to 3.17.x (and possibly newer)
+
+-----------------------------------------------------------------------------
+1. Introduction
+
+   The PCIe Multiport serial driver Ver 2.2, supports the following multiport
+   boards.
+
+      - XR17V258 PCI Multiport (8 Channels) Boards
+      - XR17V254 PCI Multiport (4 Channels) Boards
+      - XR17V252 PCI Multiport (2 Channels) Boards
+	- XR17V358 & XR17V4354 PCIe Multiport (8 Channels) Boards
+      - XR17V354 PCIe Multiport (4 Channels) Boards
+      - XR17V352 PCIe Multiport (2 Channels) Boards
+      - XR17V4358 & XR17V8354 PCIe Multiport (12 Channels) Boards
+      - XR17V8358 PCIe Multiport (16 Channels) Boards
+
+   This driver and installation procedure have been tested on various
+   kernel versions from 2.6.32 to 3.17.x.
+
+   All the drivers and utilities are published in form of source code under
+   GNU General Public License in this version. Please refer to GNU General
+   Public License announcement in each source code file for more detail.
+
+   This version of driver can be installed as Loadable Module (Module driver)
+   or built-in into kernel (Static driver). You may refer to following
+   installation procedure for suitable one. Before you install the driver,
+   please refer to hardware installation procedure in the User's Manual.
+
+   We assume the user should be familiar with following documents.
+   - Serial-HOWTO
+   - Kernel-HOWTO
+
+-----------------------------------------------------------------------------
+2. System Requirement
+   - Hardware platform: Intel x86
+   - Kernel version: 2.6.32 or newer
+   - Maximum of 256 ports can be installed in any PCIe board combination
+   - This driver has been tested on various kernel versions from 2.6.32 to 3.17.x.  
+     It is strongly recommended that the user do a complete install of all components
+     to ensure that this driver works correctly.
+
+-----------------------------------------------------------------------------
+3. Installation
+
+   3.1 Hardware installation
+      
+      You may need to adjust IRQ usage in BIOS to avoid IRQ conflict with legacy ISA devices. 
+
+   3.2 Driver Installation
+	  For all the newer kernels the support for MaxLinear/Exar PCI/PCIe UARTs is already integrated in the generic 
+	  serial drivers that comes with the kernel source. If you want to use the XR17V35x/XR17D15x PCIe/PCI 
+	  UARTs to only send/receive data then you may use the generic serial driver available in the linux 
+	  kernel. When using generic driver the XR17V35x device will be listed as "ttyS" instead of "ttyXR" 
+	  under /dev/ folder. In order to determine which "ttyS" are assigned to XR17V35x you can open file 
+	  "/proc/tty/driver/serial" the PCI/PCIe UARTs will be listed as MMIO devices.
+	  However, if you intend to use the Enhanced features of the device(i.e. Auto RTS/CTS, Auto RS485 etc)
+	  or enable disable MPIO, in that case you will need the custom driver. Follow the steps below for using
+	  the custom driver, in this example we will be using XR17V358: 
+	  (MaxLinear/Exar AN-225 Application Note can be referred for installing custom driver)
+
+	  A.) You will need to unbind the serial driver. Follow the steps below to unbind the serial driver. 
+
+		root$ lspci -vd 13a8:*
+		03:00.0 Serial controller: Exar Corp. Device 0358 (rev 03) (prog-if 02 [16550])
+		Flags: fast devsel, IRQ 30
+		Memory at f3ffc000 (32-bit, non-prefetchable) [size=16K]
+		Capabilities: [50] MSI: Enable- Count=1/1 Maskable- 64bit+
+		Capabilities: [78] Power Management version 3
+		Capabilities: [80] Express Endpoint, MSI 01
+		Capabilities: [100] Virtual Channel
+		Kernel driver in use: serial
+
+		As you can see in the last line above that the kernel is using generic "serial" module. 
+		Note: For newer kernel (i.e.: 4.11 and later), "exar_serial" may be seen. 
+		      If so, use "exar_serial" instead of "serial" for the following unbind commands
+
+		root$ ls /sys/bus/pci/drivers/serial/
+		0000:03:00.0 bind new_id remove_id uevent unbind
+		root$ echo -n "0000:03:00.0" > /sys/bus/pci/drivers/serial/unbind
+
+		This will unbind the generic serial driver from XR17V35x device. Now you can compile and install 
+		the customer driver. After successful installation the device should be listed in /dev folder as 
+		"ttyXR" as mentioned in the README.
+	
+	  B.) Module driver configuration
+       
+		1. Find "Makefile" in driver source file path, then run
+			  # make
+		  
+		   After the driver file "xr17v35x.ko" is properly compiled, then run
+			  # insmod xr17v35x.ko
+				or				
+			  # insmod ./xr17v35x.ko
+
+		   This will activate the module driver. You may run "lsmod" to check if "xr17v35x.ko" is activated.
+
+		2. This module registers MaxLinear/Exar ports as "/dev/ttyXR0", "/dev/ttyXR1", etc. You may use "minicom" or
+		   its custom serial application to test the ports
+   
+		3. You may refer to /var/log/messages or /var/log/kern.log to check the latest status log reported 
+		   by this driver whenever activated to verify driver installation.
+
+	  C.) Driver files and device naming convention
+
+		This driver creates traditional serial device names, "/dev/ttyXR0" onwards.
+
+        Device naming when multiple boards are installed
+        -----------------------------------------------
+        Naming convention for each multiport board is pre-defined as below, assuming 2 XR17V358 boards, 
+		1 XR17V354 board and 1 XR17V352 board.
+
+        Board Num.	 Port Name
+        1st board (358)	ttyXR0  - ttyXR7
+        2nd board (358)	ttyXR8  - ttyXR15
+        3rd board (354)	ttyXR16 - ttyXR19
+        4th board (352)	ttyXR24 - ttyXR25
+ 
+4. Troubleshooting
+
+   The boot time error mesages and solutions are stated as clearly as
+   possible. If all the possible solutions fail, please contact our technical
+   support team (uarttechsupport@exar.com) to get more help.

+ 2540 - 0
xr17v35x.c

@@ -0,0 +1,2540 @@
+/*****************************************************************************/
+/*
+*      xr17v35x.c  -- MaxLinear multiport serial driver.
+*
+*      
+*****************************************************************************
+*                                        Copyright (c) 2010, MaxLinear, Inc.
+*****************************************************************************
+*
+*      Based on Linux 2.6.37 Kernel's  drivers/serial/8250.c and /8250_pci.c
+*
+*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*
+*	   Multiport Serial Driver for MaxLinear's PCI Family of UARTs (XR17V258/254/252/358/354/352/8358/4358/8354)
+*								  (XR17D158/154/152)
+*       ChangeLog:
+*	   for    	: LINUX 2.6.32 and newer (Tested on various kernel versions from 2.6.32 to 4.15)
+*	   date   	: July 2019
+*	   version	: 2.6 
+*	 Note: XR_17v35x_UART_RHR was not defined. Fixed  
+*	Check Release Notes for information on what has changed in the new version.
+*
+*/
+
+#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/delay.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/export.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+
+
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+#include <asm/serial.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+#include "linux/version.h"
+
+#define _INLINE_ inline
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 8, 0)
+#define __devinitdata
+#define __devinit
+#define __devexit
+#define __devexit_p
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 8, 0)
+struct serial_uart_config {
+	char	*name;
+	int	dfl_xmit_fifo_size;
+	int	flags;
+};
+#endif
+
+//void tty_flip_buffer_push(struct tty_port *port);
+
+/*
+ * Definitions for PCI support.
+ */
+#define FL_BASE_MASK		0x0007
+#define FL_BASE0		0x0000
+#define FL_GET_BASE(x)		(x & FL_BASE_MASK)
+
+#define NR_PORTS	256
+
+#define XR_MAJOR       30
+#define XR_MINOR       0
+/*	
+   Set this parameter to 1 to enable Debug mode
+   The Driver enables the internal loopback under debug mode
+   To disable internal loopback go to serialxr_set_termios
+*/
+#ifndef DEBUG
+#define DEBUG 		0
+#endif
+
+/*
+ * The special register set for XR17V35x UARTs.
+ */
+ 
+#define XR_17v35x_UART_RHR			0 
+#define XR_17v35x_UART_THR			0
+#define	XR_17V35X_UART_DLD	        2
+#define	XR_17V35X_UART_MSR	        6
+#define XR_17V35X_EXTENDED_FCTR		8
+#define XR_17V35X_EXTENDED_EFR		9
+#define XR_17V35X_TXFIFO_CNT		10
+#define XR_17V35X_EXTENDED_TXTRG	10
+#define XR_17V35X_RXFIFO_CNT		11
+#define XR_17V35X_EXTENDED_RXTRG	11
+#define XR_17V35X_UART_XOFF2      	13 
+#define XR_17V35X_UART_XOFF1 		0xC0
+#define XR_17V35X_UART_XON1			0xE0
+#define XR_17V35X_FCTR_RTS_8DELAY	0x03
+#define XR_17V35X_FCTR_TRGD			192
+#define XR_17V35x_FCTR_RS485	    0x20
+
+#define XR_17V35x_MPIOLVL_7_0       0x90
+#define XR_17V35x_MPIO3T_7_0        0x91
+#define XR_17V35x_MPIOSEL_7_0       0x93
+#define XR_17V35x_MPIOLVL_15_8       0x96
+#define XR_17V35x_MPIO3T_15_8        0x97
+#define XR_17V35x_MPIOSEL_15_8       0x99
+
+
+// Set this parameter to 1 to enable RS485 mode
+#define ENABLE_RS485		        0
+//Set this parameter to 1 to enable DTR RS-485 half duplex direction control
+#define USE_DTR_RS485                    0
+// Set this parameter to 1 to enabled internal loopback
+#define ENABLE_INTERNAL_LOOPBACK      0
+
+#define UART_17V35X_RX_OFFSET		0x100
+#define UART_17V35X_TX_OFFSET 		0x100
+
+#define	XR_17V35X_IER_RTSDTR	        0x40
+#define XR_17V35X_IER_CTSDSR	        0x80
+
+#define XR_17V35X_8XMODE	        0x88
+#define XR_17V35X_4XMODE	        0x89
+
+#define DIVISOR_CHANGED   0
+
+#define PCI_NUM_BAR_RESOURCES	6
+#define CAPTURE_SERIAL_INDEX    1
+
+struct serial_private {
+	struct pci_dev		*dev;
+	unsigned int		nr;
+	void __iomem		*remapped_bar[PCI_NUM_BAR_RESOURCES];
+	struct pci_serial_quirk	*quirk;
+	int    uart_index[NR_PORTS];
+	int			line[0];
+};
+
+struct pciserial_board {
+	unsigned int flags;
+	unsigned int num_ports;
+	unsigned int base_baud;
+	unsigned int uart_offset;
+	unsigned int reg_shift;
+	unsigned int first_offset;
+};
+
+/*
+ * init function returns:
+ *  > 0 - number of ports
+ *  = 0 - use board->num_ports
+ *  < 0 - error
+ */
+struct pci_serial_quirk {
+	u32	vendor;
+	u32	device;
+	u32	subvendor;
+	u32	subdevice;
+	int	(*init)(struct pci_dev *dev);
+	int	(*setup)(struct serial_private *, 
+			 const struct pciserial_board *,
+			 struct uart_port *, int);
+	void	(*exit)(struct pci_dev *dev);
+};
+
+/*
+ * This is the configuration table for all of the PCI serial boards
+ * which we support.  It is directly indexed by the xrpci_board_num_t enum
+ * value, which is encoded in the pci_device_id PCI probe table's
+ * driver_data member.
+ *
+ * The makeup of these names are:
+ *  pbn_bn{_bt}_n_baud
+ *
+ *  bn   = PCI BAR number
+ *  bt   = Index using PCI BARs
+ *  n    = number of serial ports
+ *  baud = baud rate
+ */
+enum xrpci_board_num_t {
+	xr_8port = 0,
+	xr_4port,
+	xr_2port,
+	xr_4354port,
+	xr_8354port,
+	xr_4358port,
+	xr_8358port,
+	xr_258port,
+	xr_254port,
+	xr_252port,
+	xr_158port,
+	xr_154port,
+	xr_152port,
+};
+
+static struct pciserial_board xrpciserial_boards[] __devinitdata = {
+	[xr_8port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 7812500*4,
+		.uart_offset	= 0x400,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	
+	[xr_4port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 7812500*4,
+		.uart_offset	= 0x400,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_2port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 7812500*4,
+		.uart_offset	= 0x400,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_4354port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 7812500*4,
+		.uart_offset	= 0x400,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_8354port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 12,
+		.base_baud	= 7812500*4,
+		.uart_offset	= 0x400,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_4358port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 12,
+		.base_baud	= 7812500*4,
+		.uart_offset	= 0x400,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_8358port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 16,
+		.base_baud	= 7812500*4,
+		.uart_offset	= 0x400,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_258port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 1500000,
+		.uart_offset	= 0x200,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_254port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 1500000,
+		.uart_offset	= 0x200,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_252port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 1500000,
+		.uart_offset	= 0x200,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+    	[xr_158port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 921600,
+		.uart_offset	= 0x200,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_154port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 0x200,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+	[xr_152port] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 0x200,
+		.reg_shift	= 0,
+		.first_offset	= 0,
+	},
+
+};
+
+/*
+ * Configuration:
+ *   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
+ *                is unsafe when used on edge-triggered interrupts.
+ */
+#define SERIALEXAR_SHARE_IRQS 1 
+unsigned int share_irqs = SERIALEXAR_SHARE_IRQS;
+
+/*
+ * Debugging.
+ */
+#if 0
+#define DEBUG_AUTOCONF(fmt...)	printk(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...)	do { } while (0)
+#endif
+
+#if DEBUG
+#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)
+#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
+
+#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
+struct uart_xr_port {
+	struct uart_port	port;
+	struct timer_list	timer;		/* "no irq" timer */
+	struct list_head	list;		/* ports on this IRQ */
+	unsigned short		capabilities;	/* port capabilities */
+	unsigned short		bugs;		/* port bugs */
+	unsigned int		tx_loadsz;	/* transmit fifo load size */
+	unsigned char		acr;
+	unsigned char		ier;
+	unsigned char		lcr;
+	unsigned char		mcr;
+	unsigned char		mcr_mask;	/* mask of user bits */
+	unsigned char		mcr_force;	/* mask of forced bits */
+
+	unsigned char		lsr_saved_flags;
+	unsigned char		msr_saved_flags;
+	
+	unsigned short 		deviceid;
+	unsigned char		channelnum;
+	unsigned char       multidrop_address;
+    unsigned char       multidrop_mode;
+    unsigned char       is_match_address;
+	/*
+	 * We provide a per-port pm hook.
+	 */
+	void			(*pm)(struct uart_port *port,
+					  unsigned int state, unsigned int old);
+};
+
+struct irq_info {
+	struct			hlist_node node;
+	int			irq;
+	spinlock_t		lock;	/* Protects list not the hash */
+	struct list_head	*head;
+};
+
+#define NR_IRQ_HASH		32	/* Can be adjusted later */
+static struct hlist_head irq_lists[NR_IRQ_HASH];
+static DEFINE_MUTEX(hash_mutex);	/* Used to walk the hash */
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+#define PORT_MAX_XR 2 
+#define XRPCIe_TYPE 1 // the second entry that is [1] in the array
+#define XRPCI25x_TYPE 2 // the third entry that is [2] in the array
+
+static const struct serial_uart_config uart_config[PORT_MAX_XR+1] = {
+	{ "Unknown",	1,	0 },
+	{ "XR17v35x",	256,	0 },
+	{ "XR17v25x",	64,	0 },
+};
+
+static int
+setup_port(struct serial_private *priv, struct uart_port *port,
+	   int bar, int offset, int regshift)
+{
+	struct pci_dev *dev = priv->dev;
+	unsigned long base, len;
+
+	if (bar >= PCI_NUM_BAR_RESOURCES)
+		return -EINVAL;
+
+	base = pci_resource_start(dev, bar);
+
+	if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
+		len =  pci_resource_len(dev, bar);
+
+		if (!priv->remapped_bar[bar])
+			priv->remapped_bar[bar] = ioremap(base, len);
+		if (!priv->remapped_bar[bar])
+			return -ENOMEM;
+
+		port->iotype = UPIO_MEM;
+		port->iobase = 0;
+		port->mapbase = base + offset;
+		port->membase = priv->remapped_bar[bar] + offset;
+		port->regshift = regshift;
+	} else {
+		//Exar's got to be memory mapped. some hardware reading error?
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+static int
+pci_default_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_port *port, int idx)
+{
+	unsigned int bar, offset = board->first_offset;
+
+	bar = FL_GET_BASE(board->flags);	
+	offset += idx * board->uart_offset;
+	//printk(KERN_INFO "Exar PCIe device 0x%x\n", priv->dev->device);
+	if((priv->dev->device == 0x4354 || priv->dev->device == 0x8354) && (idx >= 4))
+	{
+	    offset += 0x1000; // the ports on expansion device for 0x(4/8)354 sit at bar0+0x2000 offset. 
+			      // So we need to add 0x1000 here as 4*0x400
+	}
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+/*
+ * Master list of serial port init/setup/exit quirks.
+ * This does not describe the general nature of the port.
+ * (ie, baud base, number and location of ports, etc)
+ *
+ * This list is ordered alphabetically by vendor then device.
+ * Specific entries must come before more generic entries.
+ */
+static struct pci_serial_quirk pci_serial_quirks[] = {	
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x358,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+	
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x354,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x352,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x4354,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x8354,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x4358,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x8358,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x258,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+	
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x254,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x252,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+		{
+		.vendor		= 0x13a8,
+		.device		= 0x158,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+	
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x154,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+	{
+		.vendor		= 0x13a8,
+		.device		= 0x152,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,	
+	},
+
+};
+
+static inline int quirk_id_matches(u32 quirk_id, u32 dev_id)
+{
+	return quirk_id == PCI_ANY_ID || quirk_id == dev_id;
+}
+
+static struct pci_serial_quirk *find_quirk(struct pci_dev *dev)
+{
+	struct pci_serial_quirk *quirk;
+
+	for (quirk = pci_serial_quirks; ; quirk++)
+		if (quirk_id_matches(quirk->vendor, dev->vendor) &&
+		    quirk_id_matches(quirk->device, dev->device))		    
+		 	break;
+	return quirk;
+}
+
+static _INLINE_ unsigned int serial_in(struct uart_xr_port *up, int offset)
+{
+	return readb(up->port.membase + offset);
+}
+
+static _INLINE_ void
+serial_out(struct uart_xr_port *up, int offset, int value)
+{
+	writeb(value, up->port.membase + offset);
+}
+
+static void serialxr_stop_tx(struct uart_port *port)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	int lcr;
+
+	if (up->ier & UART_IER_THRI) {
+		up->ier &= ~UART_IER_THRI;
+		lcr = serial_in(up, UART_LCR);
+		if (lcr & 0x80) {			
+			printk(KERN_INFO "channelnum %d: serialxr stop tx - LCR = 0x%x", up->channelnum, lcr);
+			serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+		}
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static void serialxr_start_tx(struct uart_port *port)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	int lcr;
+
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		lcr = serial_in(up, UART_LCR);
+		if (lcr & 0x80) {
+			printk(KERN_INFO"channelnum %d: serialxr start tx - LCR = 0x%x", up->channelnum, lcr);
+			serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+		}
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static void serialxr_stop_rx(struct uart_port *port)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	int lcr;
+
+	up->ier &= ~UART_IER_RLSI;
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		printk(KERN_INFO"channelnum %d: serialxr stop rx - LCR = 0x%x", up->channelnum, lcr);
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void serialxr_enable_ms(struct uart_port *port)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	int lcr;
+
+	up->ier |= UART_IER_MSI;
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		printk(KERN_INFO"channelnum %d: serialxr enable ms - LCR = 0x%x", up->channelnum, lcr);
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void
+receive_chars(struct uart_xr_port *up, unsigned int *status)
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0)	
+	struct uart_port *port = &up->port;
+#else
+	struct tty_struct *tty = up->port.state->port.tty;	
+#endif	
+	unsigned char ch[256], lsr = *status;
+	char flag;
+	int i, lcr, datasize_in_fifo, port_index;
+	unsigned char tmp;		
+	datasize_in_fifo = serial_in(up, XR_17V35X_RXFIFO_CNT);
+	while(datasize_in_fifo!=serial_in(up, XR_17V35X_RXFIFO_CNT))
+	/*Read Receive Fifo count until we get read same value twice*/
+		datasize_in_fifo=serial_in(up, XR_17V35X_RXFIFO_CNT);
+
+  	port_index = up->port.line;
+	flag = TTY_NORMAL;
+  
+	if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE))) 
+	{
+		/*
+		* Mask off conditions which should be ignored.
+		*/
+		lsr &= up->port.read_status_mask;
+        if (lsr & UART_LSR_OE) 
+		{
+			printk("OverRun Happen....");
+		} 
+		if (lsr & UART_LSR_BI) 
+		{
+			DEBUG_INTR("handling break....");
+			flag = TTY_BREAK;
+		} 
+		else if (lsr & UART_LSR_PE)
+		{
+			flag = TTY_PARITY;
+			
+			if(up->multidrop_mode == 1)
+			{
+				  //memcpy_fromio(ch, up->port.membase + UART_17V35X_RX_OFFSET, datasize_in_fifo);
+					for(i=0;i<datasize_in_fifo;i++)
+					{
+						lcr = serial_in(up, UART_LCR);
+						if (lcr & 0x80) {
+							printk(KERN_INFO"channelnum %d: receive chars (multidrop mode) - LCR = 0x%x", up->channelnum, lcr);
+							serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+						}
+					  ch[i]= serial_in(up, XR_17v35x_UART_RHR);
+					}
+					up->port.icount.rx+=datasize_in_fifo;
+		          //up->port.icount.rx+=datasize_in_fifo;
+				  DEBUG_INTR("Receive address byte:%02x\n",ch[0]);
+				  if(up->is_match_address == 0)
+				  {
+     				  if(ch[0] == up->multidrop_address)
+     				  {
+     				    DEBUG_INTR(" Enable the receiver\n");
+     					//set EFR[4] = 1; enable the shaded bits 
+     				    tmp = serial_in(up, XR_17V35X_EXTENDED_EFR);
+     				    tmp |=0x10;
+     				    serial_out(up, XR_17V35X_EXTENDED_EFR, tmp);
+     				    serial_out(up,XR_17V35X_UART_MSR, 0);//Enable the receiver 
+     				    //set EFR[4] = 0; disable the shaded bits 
+     				    tmp = serial_in(up, XR_17V35X_EXTENDED_EFR);
+     				    tmp &=~0x10;
+     				    serial_out(up, XR_17V35X_EXTENDED_EFR, tmp); 
+						up->is_match_address = 1;
+     				  }
+					  else
+					  {
+					    //do nothing
+					  }
+				  }
+				  else
+				  {
+     				  if(ch[0] == up->multidrop_address)
+     				  {
+     				    //do dothing
+     				  }
+					  else
+     				  {
+     				    DEBUG_INTR(" Disable the receiver\n");
+     					//set EFR[4] = 1; enable the shaded bits 
+     				    tmp = serial_in(up, XR_17V35X_EXTENDED_EFR);
+     				    tmp |=0x10;
+     				    serial_out(up, XR_17V35X_EXTENDED_EFR, tmp);
+     				    serial_out(up,XR_17V35X_UART_MSR, 0x04);//Disable the receiver 
+     				     //set EFR[4] = 0; disable the shaded bits 
+     				    tmp = serial_in(up, XR_17V35X_EXTENDED_EFR);
+     				    tmp &=~0x10;
+     				    serial_out(up, XR_17V35X_EXTENDED_EFR, tmp);
+						up->is_match_address = 0;
+     				  }
+				  }
+				  return;
+			}
+			if(up->multidrop_mode == 0)
+			{
+			    printk("handling port<%d> Parity error....(%d)\n",port_index,up->multidrop_mode);
+			}
+			
+		}
+		else if (lsr & UART_LSR_FE)
+		{
+		    DEBUG_INTR("handling Frame error....\n");	
+			flag = TTY_FRAME;
+		}
+	}
+	
+    //memcpy_fromio(ch, up->port.membase + UART_17V35X_RX_OFFSET, datasize_in_fifo);
+			
+	//print_hex_dump(KERN_DEBUG,"R:",DUMP_PREFIX_NONE,16,1,ch,datasize_in_fifo,1);
+    for(i=0;i<datasize_in_fifo;i++)
+    {
+		lcr = serial_in(up, UART_LCR);
+		if (lcr & 0x80) {
+			printk(KERN_INFO"channelnum %d: receive chars - LCR = 0x%x", up->channelnum, lcr);
+			serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+		}
+      ch[i]= serial_in(up, XR_17v35x_UART_RHR);
+    }
+	up->port.icount.rx+=datasize_in_fifo;
+	
+	for(i = 0; i < datasize_in_fifo; i++)
+	{
+		if (uart_handle_sysrq_char(&up->port, ch[i]))
+			continue;
+		uart_insert_char(&up->port, lsr, UART_LSR_OE, ch[i], flag);
+	}
+    spin_unlock(&up->port.lock);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0)
+	tty_flip_buffer_push(&port->state->port);
+#else
+	tty_flip_buffer_push(tty);
+#endif
+	spin_lock(&up->port.lock);
+
+	DEBUG_INTR(" LSR_DR...");
+}
+
+static void transmit_chars(struct uart_xr_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count, bytes_in_fifo, tmp;
+	int i, lcr;
+	unsigned char *ch;
+	if (up->port.x_char) {
+		serial_out(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_tx_stopped(&up->port)) {
+		serialxr_stop_tx(&up->port);
+		return;
+	}
+	if (uart_circ_empty(xmit)) {
+		serialxr_stop_tx(&up->port);
+		return;
+	}
+
+	bytes_in_fifo = serial_in(up, XR_17V35X_TXFIFO_CNT);
+	// read the fifo count untill we get the same value twice
+	while (bytes_in_fifo != serial_in(up, XR_17V35X_TXFIFO_CNT))
+		bytes_in_fifo = serial_in(up, XR_17V35X_TXFIFO_CNT);
+
+	// how much buffer is availabe now to write?	
+	count = up->port.fifosize - bytes_in_fifo;
+	
+	if (uart_circ_chars_pending(xmit) < count)
+		count = uart_circ_chars_pending(xmit);
+		
+	do
+	{	
+		// if the count is more than (tail to end of the buffer), transmit only the rest here.
+		// tail+tmp&(UART_XMIT_SIZE-1) will reset the tail to the starting of the circular buffer
+		if( ((xmit->tail + count) & (UART_XMIT_SIZE-1)) < xmit->tail)
+		{			
+			tmp = UART_XMIT_SIZE - xmit->tail;
+			//memcpy_toio(up->port.membase + UART_17V35X_TX_OFFSET, &(xmit->buf[xmit->tail]), tmp);
+			ch = (unsigned char *)&(xmit->buf[xmit->tail]);
+			//print_hex_dump(KERN_DEBUG,"T:",DUMP_PREFIX_NONE,16,1,&(xmit->buf[xmit->tail]),tmp,1);
+			for(i=0;i<tmp;i++)
+			{
+				lcr = serial_in(up, UART_LCR);
+				if (lcr & 0x80) {
+					printk(KERN_INFO"channelnum %d: transmit_chars1 - LCR = 0x%x", up->channelnum, lcr);
+					serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+				}				
+			  serial_out(up, XR_17v35x_UART_THR, ch[i]);
+			}
+			xmit->tail += tmp;
+			xmit->tail &= (UART_XMIT_SIZE-1);
+			up->port.icount.tx += tmp;
+			count	-= tmp;
+		}
+		else
+		{	
+		    ch = (unsigned char *)&(xmit->buf[xmit->tail]);
+			//memcpy_toio(up->port.membase + UART_17V35X_TX_OFFSET, &(xmit->buf[xmit->tail]), count);	
+			for(i=0;i < count;i++)
+			{
+				lcr = serial_in(up, UART_LCR);
+				if (lcr & 0x80) {
+					printk(KERN_INFO"channelnum %d: transmit_chars2 - LCR = 0x%x", up->channelnum, lcr);
+					serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+				}
+			  serial_out(up, XR_17v35x_UART_THR, ch[i]);
+			}				    
+			xmit->tail += count;
+			xmit->tail &= UART_XMIT_SIZE - 1;
+			up->port.icount.tx += count;
+			count = 0;
+		}
+
+	}while (count > 0);
+   	
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	DEBUG_INTR("THRE...");
+
+	if (uart_circ_empty(xmit))
+		serialxr_stop_tx(&up->port);
+}
+
+static unsigned int check_modem_status(struct uart_xr_port *up)
+{
+	unsigned int status = serial_in(up, UART_MSR);
+
+	status |= up->msr_saved_flags;
+	up->msr_saved_flags = 0;
+	if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
+	    up->port.state != NULL) {
+		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);
+
+		wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+	}
+
+	return status;
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static void serialxr_handle_port(struct uart_xr_port *up)
+{
+	unsigned int status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	status = serial_in(up, UART_LSR);
+
+	DEBUG_INTR("status = %x...", status);
+
+	if (status & (UART_LSR_DR | UART_LSR_BI))
+		receive_chars(up, &status);
+	check_modem_status(up);
+	if (status & UART_LSR_THRE)
+		transmit_chars(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * 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.
+ */
+static irqreturn_t serialxr_interrupt(int irq, void *dev_id)
+{
+	struct irq_info *i = dev_id;
+	struct list_head *l, *end = NULL;
+	int pass_counter = 0, handled = 0;
+
+	DEBUG_INTR("serialxr_interrupt(%d)...", irq);
+
+	spin_lock(&i->lock);
+
+	l = i->head;
+	do {
+		struct uart_xr_port *up;
+		unsigned int iir, lcr;
+
+		up = list_entry(l, struct uart_xr_port, list);
+
+		lcr = serial_in(up, UART_LCR);  // store value of LCR
+		if (lcr & 0x80) {
+			printk(KERN_INFO"channelnum %d: serialxr interrupt - LCR = 0x%x", up->channelnum, lcr);
+			serial_out(up, UART_LCR, lcr & 0x7F); // ensure LCR bit-7=0 before reading UART_IIR
+		}		
+		iir = serial_in(up, UART_IIR);
+		if (!(iir & UART_IIR_NO_INT)) {
+			serialxr_handle_port(up);
+
+			handled = 1;
+
+			end = NULL;
+		} else if (end == NULL)
+			end = l;
+
+		serial_out(up, UART_LCR, lcr); // restore LCR
+		l = l->next;
+                /* add INT0 clear */
+                 serial_in(up,0x80);
+		if (l == i->head && pass_counter++ > 256) {
+			/* If we hit this, we're dead. */
+			printk(KERN_ERR "serialxr: too much work for "
+				"irq%d\n", irq);
+			break;
+		}
+	} while (l != end);
+
+	spin_unlock(&i->lock);
+
+	DEBUG_INTR("end.\n");
+	return IRQ_RETVAL(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_xr_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);
+
+	/* List empty so throw away the hash node */
+	if (i->head == NULL) {
+		hlist_del(&i->node);
+		kfree(i);
+	}
+}
+
+static int serial_link_irq_chain(struct uart_xr_port *up)
+{
+	struct hlist_head *h;
+	struct hlist_node *n;
+	struct irq_info *i;
+	int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+
+	mutex_lock(&hash_mutex);
+
+	h = &irq_lists[up->port.irq % NR_IRQ_HASH];
+
+	hlist_for_each(n, h) {
+		i = hlist_entry(n, struct irq_info, node);
+		if (i->irq == up->port.irq)
+			break;
+	}
+
+	if (n == NULL) {
+		i = kzalloc(sizeof(struct irq_info), GFP_KERNEL);
+		if (i == NULL) {
+			mutex_unlock(&hash_mutex);
+			return -ENOMEM;
+		}
+		spin_lock_init(&i->lock);
+		i->irq = up->port.irq;
+		hlist_add_head(&i->node, h);
+	}
+	mutex_unlock(&hash_mutex);
+
+	spin_lock_irq(&i->lock);
+
+	if (i->head) {
+		list_add_tail(&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);
+		irq_flags |= up->port.irqflags;
+		ret = request_irq(up->port.irq, serialxr_interrupt,
+				  irq_flags, "xrserial", i);
+		if (ret < 0)
+			serial_do_unlink(i, up);
+	}
+
+	return ret;
+}
+
+static void serial_unlink_irq_chain(struct uart_xr_port *up)
+{
+	struct irq_info *i;
+	struct hlist_node *n;
+	struct hlist_head *h;
+
+	mutex_lock(&hash_mutex);
+
+	h = &irq_lists[up->port.irq % NR_IRQ_HASH];
+
+	hlist_for_each(n, h) {
+		i = hlist_entry(n, struct irq_info, node);
+		if (i->irq == up->port.irq)
+			break;
+	}
+
+	BUG_ON(n == NULL);
+	BUG_ON(i->head == NULL);
+
+	if (list_empty(i->head))
+		free_irq(up->port.irq, i);
+
+	serial_do_unlink(i, up);
+	mutex_unlock(&hash_mutex);
+}
+
+static inline int poll_timeout(int timeout)
+{
+	return timeout > 6 ? (timeout / 2 - 2) : 1;
+}
+
+/*
+ * 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).
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void serialxr_timeout(struct timer_list *p_tl)
+#else
+static void serialxr_timeout(unsigned long data)
+#endif
+{
+	#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+	struct uart_xr_port *up = from_timer(up, p_tl, timer);
+	#else
+	struct uart_xr_port *up = (struct uart_xr_port *)data;
+	#endif
+
+	unsigned int iir;
+	int lcr;
+
+	lcr = serial_in(up, UART_LCR);  // check value of LCR
+	if (lcr & 0x80) {
+		serial_out(up, UART_LCR, lcr & 0x7F);	// ensure LCR bit-7=0 before reading UART_IIR
+	}
+	iir = serial_in(up, UART_IIR);
+	if (!(iir & UART_IIR_NO_INT))
+		serialxr_handle_port(up);
+	mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout));
+}
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+static unsigned int serialxr_tx_empty(struct uart_port *port)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	unsigned long flags;
+	unsigned int lsr;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	lsr = serial_in(up, UART_LSR);
+	up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int serialxr_get_mctrl(struct uart_port *port)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	unsigned int status;
+	unsigned int ret;
+
+	status = check_modem_status(up);
+
+	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 serialxr_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	unsigned char mcr = 0, efr;
+
+	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;
+	
+	efr = serial_in(up, UART_EFR); 
+	efr = efr & 0xEF;	// clear access to shaded registers so that write to MCR does not change from using DTR to RTS for RS-485 control
+#if   USE_DTR_RS485
+       mcr |= 0x04;
+	printk(KERN_INFO "serialxr_set_mctrl mcr=%02x\n",mcr);
+#endif
+	serial_out(up, UART_EFR, efr);
+	serial_out(up, UART_MCR, mcr);
+}
+
+static void serialxr_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_xr_port *up = (struct uart_xr_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 serialxr_startup(struct uart_port *port)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	unsigned long flags;
+	unsigned int fctr_reg=0;
+	int retval, lcr;
+		
+	up->capabilities = uart_config[up->port.type].flags;
+ 	serial_out(up, XR_17V35X_EXTENDED_EFR, UART_EFR_ECB);
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_IER, 0);
+
+	/* Set the RX/TX trigger levels */
+	/* These are some default values, the OEMs can change these values
+		* according to their best case scenarios */
+	
+	if(up->deviceid > 0x258) // PCIe device
+	{
+		serial_out(up, XR_17V35X_EXTENDED_RXTRG, 32);
+		serial_out(up, XR_17V35X_EXTENDED_TXTRG, 64);
+	}
+	else // for 25x
+	{
+		serial_out(up, XR_17V35X_EXTENDED_RXTRG, 32); // 25x
+		serial_out(up, XR_17V35X_EXTENDED_TXTRG, 32);
+	}
+
+	/* Hysteresis level of 8, Enable Auto RS-485 Mode */
+	fctr_reg=serial_in(up,XR_17V35X_EXTENDED_FCTR);
+	DEBUG_INTR(KERN_INFO "serialxr_startup: FCTR=0x%x",fctr_reg);
+#if ENABLE_RS485
+	serial_out(up, XR_17V35X_EXTENDED_FCTR, fctr_reg|XR_17V35X_FCTR_TRGD | XR_17V35X_FCTR_RTS_8DELAY | XR_17V35x_FCTR_RS485);
+#if USE_DTR_RS485	
+	serial_out(up, UART_MCR, 0x04);  //use DTR for Auto RS-485 Control
+#endif	
+#else
+	serial_out(up, XR_17V35X_EXTENDED_FCTR, (fctr_reg|XR_17V35X_FCTR_TRGD | XR_17V35X_FCTR_RTS_8DELAY)&0xDF);
+#endif
+	
+
+	serial_out(up, UART_LCR, 0);
+
+	/* Wake up and initialize UART */
+	serial_out(up, XR_17V35X_EXTENDED_EFR, UART_EFR_ECB | 0x10/*Enable Shaded bits access*/);
+	serial_out(up,XR_17V35X_UART_MSR, 0);
+	serial_out(up, UART_LCR, 0);	// Do LCR first to avoid LCR bit-7=1 before writing to IER
+	serial_out(up, UART_IER, 0);
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reeanbled in set_termios())
+	 */
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_FCR, 0);
+	
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+          /* add INT0 clear */
+        serial_in(up,0x80);
+if(port->irq) {
+	retval = serial_link_irq_chain(up);
+	if(retval)
+		return retval;
+}
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+		
+	/*
+	* Most PC uarts need OUT2 raised to enable interrupts.
+	*/
+	if (is_real_interrupt(up->port.irq))
+		up->port.mctrl |= TIOCM_OUT2;
+	//to enable intenal loop, uncomment the line below
+	//up->port.mctrl |= TIOCM_LOOP;
+
+	serialxr_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * 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;
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		printk(KERN_INFO"channelnum %d: serialxr startup - LCR = 0x%x", up->channelnum, lcr);
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_IER, up->ier);
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+         /* add INT0 clear */
+        serial_in(up,0x80); 
+	return 0;
+}
+
+static void serialxr_shutdown(struct uart_port *port)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	unsigned long flags;
+	unsigned char lsr;
+	int i = 0;
+	int lcr;
+	
+	while(1)
+	{
+	   i++;
+	   lsr = serial_in(up, UART_LSR);
+	   if((lsr&0x60) != 0x60)
+	      printk("serialxr_shutdown wait TXFIFO Empty %02x",lsr);
+	   else
+	   	  break;
+	   msleep(1);
+	   if(i>1000) break;
+	   
+	}  
+	
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		printk(KERN_INFO"channelnum %d: serialxr_shutdown1 - LCR = 0x%x",	up->channelnum, lcr);
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	
+	up->port.mctrl &= ~TIOCM_OUT2;
+
+	serialxr_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_in(up, UART_LCR) & (~UART_LCR_SBC) & 0x7f);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+				  UART_FCR_CLEAR_RCVR |
+				  UART_FCR_CLEAR_XMIT);
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		printk(KERN_INFO"channelnum %d: serialxr_shutdown2 - LCR = 0x%x",	up->channelnum, lcr);
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_FCR, 0);
+
+	/*
+	 * Read data port to reset things, and then unlink from
+	 * the IRQ chain.
+	 */
+	(void) serial_in(up, UART_RX);
+#if 1
+if (port->irq)
+	serial_unlink_irq_chain(up);
+#endif
+}
+
+static int quot_coeff = 16 ;
+static unsigned char low_baudrate_mode = 0;
+static unsigned int uart_get_divisor_exar(struct uart_port *port, unsigned int baud)
+{
+	unsigned int quot;
+
+	/*
+	 * Old custom speed handling.
+	 */
+	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
+		quot = port->custom_divisor;
+	else
+		{
+			if(low_baudrate_mode)
+			   quot = DIV_ROUND_CLOSEST(port->uartclk/4, quot_coeff * baud);
+			else
+			   quot = DIV_ROUND_CLOSEST(port->uartclk, quot_coeff * baud);	
+		}
+	
+	
+
+//	DEBUG_INTR(KERN_INFO "uart_get_divisor_exar:UartClk=%d QuotCoeff=0x%x",port->uartclk,quot_coeff);
+	return quot;
+}
+
+
+static unsigned int serialxr_get_divisor(struct uart_port *port, unsigned int baud)
+{
+	unsigned int quot;
+
+	quot = uart_get_divisor_exar(port, baud);
+
+	return quot;
+}
+
+static void
+serialxr_set_special_baudrate(struct uart_port *port,unsigned int special_baudrate)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	signed int baud, quot;
+    signed int quot_fraction = 0;
+	unsigned char val_4xmode;
+	unsigned char val_8xmode;
+	unsigned char lcr_bak;
+	unsigned int reg_read;
+	int port_index = up->channelnum;
+	printk(KERN_INFO "Enter in serialxr_set non-standard baudrate:%d channelnum:%d\n",special_baudrate,up->channelnum);
+	
+	baud = special_baudrate/*uart_get_baud_rate(port, termios, old, 0, port->uartclk/4)*/;
+    lcr_bak = serial_in(up, UART_LCR);
+	val_4xmode = serial_in(up, XR_17V35X_4XMODE);
+	val_8xmode = serial_in(up, XR_17V35X_8XMODE);
+	    	
+	if((port_index > 15)||(port_index < 0))
+	{
+	   return;
+	}
+
+	switch(up->deviceid)
+	{
+	  case 0x4354:
+	  case 0x8354:
+	  	if(port_index >= 4) port_index = port_index - 4;
+	  	break;
+	  case 0x4358:
+	  case 0x8358:
+	  	if(port_index >= 8) port_index = port_index - 8;
+	  	break;
+	 default:
+	 	//Do nothing
+	 	break;
+	}
+	
+		
+	if(baud < 12500000/16)
+	{//using the 16x mode
+	      val_4xmode &=~(1 << port_index);
+	      val_8xmode &=~(1 << port_index);	
+	      quot_coeff = 16;
+	      printk(KERN_INFO "Using the 16x Mode\n");
+	}
+	else if((baud >= 12500000/16)&&(baud < 12500000/4))
+	{//using the 8x mode
+	      val_4xmode &=~(1 << port_index);
+		  val_8xmode |=(1 << port_index);
+		  quot_coeff = 8;
+		  printk(KERN_INFO "Using the 8x Mode\n");
+	}
+	else 
+	{//using the 4x mode
+	   val_4xmode |=(1 << port_index);
+	   val_8xmode &=~(1 << port_index);
+	   quot_coeff = 4;
+	   printk(KERN_INFO "Using the 4x Mode\n");
+	}
+
+	serial_out(up, XR_17V35X_8XMODE, val_8xmode);
+	serial_out(up, XR_17V35X_4XMODE, val_4xmode);
+	
+	quot = serialxr_get_divisor(port, baud);
+	if(!((up->deviceid == 0x152)||(up->deviceid == 0x154)||(up->deviceid == 0x158)))
+	{
+	    unsigned int quot_16;
+	    DEBUG_INTR(KERN_INFO "XR_17V35X uartclk:%d Quot=0x%x\n",port->uartclk,quot);
+	    if(quot_coeff == 16)
+	    {
+	        quot_16 = DIV_ROUND_CLOSEST(port->uartclk, baud);
+		 quot_fraction = quot_16 & 0x0f;	
+		 quot = (quot_16 >>4);
+		
+	    }
+	    else if(quot_coeff == 8)
+	    {
+	         quot_16 = DIV_ROUND_CLOSEST(port->uartclk*2, baud);
+		  quot_fraction = quot_16 & 0x0f;	
+		  quot = (quot_16 >>4);
+	    }
+	    else if(quot_coeff == 4)
+	    {
+	         quot_16 = DIV_ROUND_CLOSEST(port->uartclk*4, baud);
+		  quot_fraction = quot_16 & 0x0f;	
+		  quot = (quot_16 >>4);
+	    }
+	    else
+	    {
+	       
+	    }
+       }
+    serial_out(up, UART_LCR, lcr_bak | UART_LCR_DLAB);/* set DLAB */
+    serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+    serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
+	//Fractional baud rate support
+	if((up->deviceid == 0x152)||(up->deviceid == 0x154)||(up->deviceid == 0x158))
+	{
+	   //nothing to do , because these devices do not have support for the DLD register.
+	}
+	else
+	{
+	    reg_read=(serial_in(up, XR_17V35X_UART_DLD)&0xF0);
+	    DEBUG_INTR(KERN_INFO "serialxr_set_special_baudrate: quot =0x%x quot_fraction=0x%x DLD_reg=0x%x\n",quot,quot_fraction,reg_read);		
+	    serial_out(up, XR_17V35X_UART_DLD, quot_fraction | reg_read);
+	    reg_read=serial_in(up, XR_17V35X_UART_DLD);
+	 }
+	 serial_out(up, UART_LCR, lcr_bak);		/* reset DLAB */
+
+}
+
+static void
+serialxr_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+struct uart_xr_port *up = (struct uart_xr_port *)port;
+unsigned char cval;
+unsigned long flags;
+signed int baud, quot;
+signed int quot_fraction = 0;
+unsigned char val_4xmode;
+unsigned char val_8xmode;
+unsigned int reg_read;
+unsigned char efr,mcr;
+int lcr;
+
+int port_index = up->channelnum;
+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.
+	 */
+baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4);
+
+printk(KERN_INFO "\nserialxr_set_termios: Port Index:%d c_ispeed:%d c_ospeed:%d baud=%d",port_index,termios->c_ispeed,termios->c_ospeed,baud);
+
+val_4xmode = serial_in(up, XR_17V35X_4XMODE);
+val_8xmode = serial_in(up, XR_17V35X_8XMODE);
+    	
+if((port_index > 15)||(port_index < 0))
+{
+   return;
+}
+
+switch(up->deviceid)
+{
+  case 0x4354:
+  case 0x8354:
+	if(port_index >= 4) port_index = port_index - 4;
+	break;
+  case 0x4358:
+  case 0x8358:
+	if(port_index >= 8) port_index = port_index - 8;
+	break;
+ default:
+	//Do nothing
+	break;
+}
+
+
+if(baud < 120)
+{
+   //set EFR[4] = 1; enable the shaded bits 
+   efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+   serial_out(up, XR_17V35X_EXTENDED_EFR, efr | 0x10);
+   mcr=serial_in(up, UART_MCR);
+   serial_out(up, UART_MCR, mcr | 0x80 );//set the prescaler (MCR bit-7 = 1, requires EFR bit-4 = 1) to divide the clock by 4.  
+   //Restore the EFR Value
+   serial_out(up, XR_17V35X_EXTENDED_EFR, efr);
+   low_baudrate_mode = 1;
+}
+else
+{
+   //set EFR[4] = 1; enable the shaded bits 
+   efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+   serial_out(up, XR_17V35X_EXTENDED_EFR, efr | 0x10);
+   mcr=serial_in(up, UART_MCR);
+   serial_out(up, UART_MCR, mcr & 0x7f );//clr the prescaler (MCR bit-7 = 1, requires EFR bit-4 = 1) to divide the clock by 1.  
+   //Restore the EFR Value
+   serial_out(up, XR_17V35X_EXTENDED_EFR, efr);
+   low_baudrate_mode = 0;
+}
+
+	
+if(baud < 12500000/16)
+{//using the 16x mode
+      val_4xmode &=~(1 << port_index);
+      val_8xmode &=~(1 << port_index);	
+      quot_coeff = 16;
+      printk(KERN_INFO "Using the 16x Mode\n");
+}
+else if((baud >= 12500000/16)&&(baud < 12500000/4))
+{//using the 8x mode
+      val_4xmode &=~(1 << port_index);
+	  val_8xmode |=(1 << port_index);
+	  quot_coeff = 8;
+	  printk(KERN_INFO "Using the 8x Mode\n");
+}
+else 
+{//using the 4x mode
+   val_4xmode |=(1 << port_index);
+   val_8xmode &=~(1 << port_index);
+   quot_coeff = 4;
+   printk(KERN_INFO "Using the 4x Mode\n");
+}
+serial_out(up, XR_17V35X_8XMODE, val_8xmode);
+serial_out(up, XR_17V35X_4XMODE, val_4xmode);
+DEBUG_INTR(KERN_INFO "XR_17V35X_4XMODE:%d \n",serial_in(up, XR_17V35X_4XMODE));
+DEBUG_INTR(KERN_INFO "XR_17V35X_8XMODE:%d \n",serial_in(up, XR_17V35X_8XMODE));
+	
+	quot = serialxr_get_divisor(port, baud);
+	if(!((up->deviceid == 0x152)||(up->deviceid == 0x154)||(up->deviceid == 0x158)))
+	 {
+	    DEBUG_INTR(KERN_INFO "XR_17V35X uartclk:%d Quot=0x%x\n",port->uartclk,quot);
+	    //#ifdef DIVISOR_CHANGED
+	    if((port->uartclk/baud) > (quot_coeff*quot))
+	    {	
+		if(quot_coeff==16)  quot_fraction = ( (port->uartclk/baud) - (quot_coeff*quot));
+		else if(quot_coeff==8) quot_fraction = ( (port->uartclk/baud) - (quot_coeff*quot))*2;
+		else if(quot_coeff==4) quot_fraction = ( (port->uartclk/baud) - (quot_coeff*quot))*4;
+	    }
+	    else if(quot > 1)
+	    {	
+	       quot--;
+	       if(quot_coeff==16)  quot_fraction = ( (port->uartclk/baud) - (quot_coeff*quot));
+	       else if(quot_coeff==8) quot_fraction = ( (port->uartclk/baud) - (quot_coeff*quot))*2;
+	       else if(quot_coeff==4) quot_fraction = ( (port->uartclk/baud) - (quot_coeff*quot))*4;
+
+	    }
+	    else
+	    {
+		quot_fraction = 0;
+	    }
+
+	    if(quot_fraction>=0x10) quot_fraction=0x0f;
+	 }
+//#endif	
+	/*
+	 * 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.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_PE | 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;
+
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		printk(KERN_INFO"channelnum %d: serialxr_set_termios1 - LCR = 0x%x",	up->channelnum, lcr);
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_IER, up->ier);
+	reg_read=serial_in(up, XR_17V35X_EXTENDED_EFR);
+
+	if(termios->c_cflag & CRTSCTS)
+	{
+	    serial_out(up, XR_17V35X_EXTENDED_EFR, reg_read|0xC0);
+	    printk(KERN_INFO "Hardware Flow Control Enabled");	    
+	}
+	 else
+	 {
+	       serial_out(up, XR_17V35X_EXTENDED_EFR, reg_read & 0x3F);
+	       printk(KERN_INFO "Hardware Flow Control Disabled\n");	    
+	 }
+	
+	/*
+	*	Auto XON/XOFF software flow control flags
+	*/
+	
+	serial_out(up, XR_17V35X_UART_XON1,0x11); //Initializing XON1
+	serial_out(up, XR_17V35X_UART_XOFF1,0x13); //Initializing XOFF1
+
+	if(((termios->c_iflag) & IXOFF)&&((termios->c_iflag) & IXON))
+	{
+		serial_out(up, XR_17V35X_EXTENDED_EFR, (reg_read) | 0x0A );
+		printk(KERN_INFO "Software Flow Control Enabled\n");
+	}
+	else 
+	{
+		serial_out(up, XR_17V35X_EXTENDED_EFR, (reg_read) & 0xF0 );
+		printk(KERN_INFO "No Software Flow Control\n");
+	}
+	
+	reg_read=serial_in(up, XR_17V35X_EXTENDED_EFR);
+	
+	if((termios->c_iflag) & IXANY)
+	{
+		serial_out(up, XR_17V35X_EXTENDED_EFR, ((termios->c_iflag) & IXOFF)||((termios->c_iflag) & IXON)?((reg_read) | 0x1A):((reg_read) | 0x10));
+		reg_read=serial_in(up, UART_MCR);
+		serial_out(up, UART_MCR, (reg_read) | 0x20 );
+		serial_out(up, XR_17V35X_EXTENDED_EFR, (reg_read) & 0xEF );
+		printk(KERN_INFO "AUTO XANY Enabled\n");
+	}
+	else 
+	{
+		serial_out(up, XR_17V35X_EXTENDED_EFR, (reg_read) | 0x10 );
+		reg_read=serial_in(up, UART_MCR);
+		serial_out(up, UART_MCR, (reg_read) & 0xDF );
+		reg_read=serial_in(up,XR_17V35X_EXTENDED_EFR);
+		serial_out(up, XR_17V35X_EXTENDED_EFR, (reg_read) & 0xEF );
+		printk(KERN_INFO "AUTO XANY NOT Enabled\n");
+	}
+	
+//---------------------------------------------------------------------------//
+	
+	serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+	
+	serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
+	//Fractional baud rate support
+	if((up->deviceid == 0x152)||(up->deviceid == 0x154)||(up->deviceid == 0x158))
+	{
+		//nothing to do , because these devices do not have support for the DLD register.
+	}
+	else
+	{
+	  reg_read=(serial_in(up, XR_17V35X_UART_DLD)&0xF0);
+	  DEBUG_INTR(KERN_INFO "serialxr_set_termios: quot =0x%x quot_fraction=0x%x DLD_reg=0x%x\n",quot,quot_fraction,reg_read);		
+	  serial_out(up, XR_17V35X_UART_DLD, quot_fraction | reg_read);
+	  reg_read=serial_in(up, XR_17V35X_UART_DLD);
+	}
+	serial_out(up, UART_LCR, cval);		/* reset DLAB */
+	up->lcr = cval;						/* Save LCR */
+	
+	lcr = serial_in(up, UART_LCR);
+	if (lcr & 0x80) {
+		printk(KERN_INFO"channelnum %d: serialxr_set_termios2 - LCR = 0x%x", up->channelnum, lcr);
+		serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+	}
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);/* set fcr */
+	/*
+		Configuring MPIO as inputs
+	*/
+	if((up->deviceid == 0x354)||(up->deviceid == 0x4354)||(up->deviceid == 0x8354))
+	{
+		serial_out(up, XR_17V35x_MPIOSEL_7_0,0x0FF); //0x0ff= ALL INPUTS	
+	}
+	else if((up->deviceid == 0x358)||(up->deviceid == 0x4358)||(up->deviceid == 0x8358))
+	{
+		serial_out(up, XR_17V35x_MPIOSEL_7_0,0x0FF); //0x0ff= ALL INPUTS
+		serial_out(up, XR_17V35x_MPIOSEL_15_8,0x0FF); //0x0ff= ALL INPUTS
+	}
+	
+	serialxr_set_mctrl(&up->port, up->port.mctrl);
+#if ENABLE_INTERNAL_LOOPBACK
+	reg_read=serial_in(up, UART_MCR);
+	serial_out(up, UART_MCR, (reg_read) | 0x10);
+	printk(KERN_INFO "Enabling Internal Loopback\n");
+#endif
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ *      EXAR ioctls
+ */
+//#define 	FIOQSIZE		0x5460 
+#define		EXAR_READ_REG      	(FIOQSIZE + 1)
+#define 	EXAR_WRITE_REG     	(FIOQSIZE + 2)
+
+#define 	EXAR_SET_MULTIDROP_MODE_NORMAL   (FIOQSIZE + 3)
+#define 	EXAR_SET_MULTIDROP_MODE_AUTO     (FIOQSIZE + 4)
+#define 	EXAR_SET_REMOVE_MULTIDROP_MODE   (FIOQSIZE + 5)
+#define 	EXAR_SET_NON_STANDARD_BAUDRATE   (FIOQSIZE + 6)
+
+
+
+struct xrioctl_rw_reg {
+	unsigned char reg;
+	unsigned char regvalue;
+};
+/*
+ * This function is used to handle Exar Device specific ioctl calls
+ * The user level application should have defined the above ioctl
+ * commands with the above values to access these ioctls and the 
+ * input parameters for these ioctls should be struct xrioctl_rw_reg
+ * The Ioctl functioning is pretty much self explanatory here in the code,
+ * and the register values should be between 0 to XR_17V35X_EXTENDED_RXTRG
+ */
+
+static int
+serialxr_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	int ret = -ENOIOCTLCMD;
+	struct xrioctl_rw_reg ioctlrwarg;
+    unsigned char address;
+	unsigned char tmp,lcr_bak,dld,efr;
+	unsigned int any_baudrate = 0;
+	switch (cmd)
+	{
+		case EXAR_READ_REG:
+		if (copy_from_user(&ioctlrwarg, (void *)arg, sizeof(ioctlrwarg)))
+			return -EFAULT;
+		ioctlrwarg.regvalue = serial_in(up, ioctlrwarg.reg);
+		if (copy_to_user((void *)arg, &ioctlrwarg, sizeof(ioctlrwarg)))
+			return -EFAULT;
+		DEBUG_INTR(KERN_INFO "serialxr_ioctl read reg[0x%02x]=0x%02x \n",ioctlrwarg.reg,ioctlrwarg.regvalue);
+		ret = 0;
+		break;
+		
+		case EXAR_WRITE_REG:
+		if (copy_from_user(&ioctlrwarg, (void *)arg, sizeof(ioctlrwarg)))
+			return -EFAULT;
+		serial_out(up, ioctlrwarg.reg, ioctlrwarg.regvalue);
+		DEBUG_INTR(KERN_INFO "serialxr_ioctl write reg[0x%02x]=0x%02x \n",ioctlrwarg.reg,ioctlrwarg.regvalue);
+		ret = 0;
+		break;
+		case EXAR_SET_MULTIDROP_MODE_NORMAL:
+		if (copy_from_user(&address, (void *)arg, 1))
+			return -EFAULT;
+		   
+		    up->multidrop_address = address;
+			//set EFR[4] = 1; enable the shaded bits 
+			efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+			efr |=0x10;
+			serial_out(up, XR_17V35X_EXTENDED_EFR, efr);
+			
+			serial_out(up,XR_17V35X_UART_MSR, 0x04);//Disable the receiver with mode=0
+            //set EFR[4] =0; disable the shaded bits 
+			efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+			efr &=~0x10;
+			serial_out(up, XR_17V35X_EXTENDED_EFR, efr); 
+			
+			
+			lcr_bak = serial_in(up, UART_LCR);
+			tmp = 0x80 | lcr_bak | 0x38; //LCR[7]=1 for access DLD  LCR[5:3] = '111'  for Forced parity to space "0"
+			serial_out(up, UART_LCR, tmp);
+			//set the DLD[6] = 1 enable Multidrop mode
+			dld = serial_in(up, XR_17V35X_UART_DLD);
+			dld |= 0x40;
+			serial_out(up, XR_17V35X_UART_DLD, dld);
+			
+			//set EFR[5] = 0; disable the special char Select
+			efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+			efr &=~0x20;
+			serial_out(up, XR_17V35X_EXTENDED_EFR, efr);
+			
+		    lcr_bak = serial_in(up, UART_LCR);//set LCR[7]=0
+			lcr_bak &=~0x80;
+			serial_out(up, UART_LCR, lcr_bak);
+			
+			ret = 0;
+			up->multidrop_mode = 1;//for enable multidrop normal mode 
+			up->is_match_address = 0;
+			DEBUG_INTR(KERN_INFO "User request EXAR_SET_MULTIDROP_MODE_NORMAL addr:%d \n",up->multidrop_address);
+		    break;
+		
+		case EXAR_SET_MULTIDROP_MODE_AUTO:
+		if (copy_from_user(&address, (void *)arg, 1))
+			return -EFAULT;
+		    up->multidrop_address = address;
+			
+			serial_out(up, XR_17V35X_UART_XOFF2,address);
+			
+            //set EFR[4] = 1; enable the shaded bits 
+			efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+			efr |=0x10;
+			serial_out(up, XR_17V35X_EXTENDED_EFR, efr);
+			
+			serial_out(up,XR_17V35X_UART_MSR, 0x04);//Disable the receiver with mode=0
+			 //set EFR[4] =0; disable the shaded bits 
+			efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+			efr &=~0x10;
+			serial_out(up, XR_17V35X_EXTENDED_EFR, efr); 
+			
+		    lcr_bak = serial_in(up, UART_LCR);
+			tmp = 0x80 | lcr_bak | 0x38; //LCR[7]=1 for access DLD  LCR[5:3] = '111'  for Forced parity to space "0"
+			serial_out(up, UART_LCR, tmp);
+			
+			//set the DLD[6] = 1 enable Multidrop mode
+			dld = serial_in(up, XR_17V35X_UART_DLD);
+			dld |= 0x40;
+			serial_out(up, XR_17V35X_UART_DLD, dld);
+			
+			//set EFR[5] = 1; enable the special char Select
+			efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+			efr |=0x20;
+			serial_out(up, XR_17V35X_EXTENDED_EFR, efr);
+			//printk(KERN_INFO "UART_EFR=0x%02x\n",serial_in(up, XR_17V35X_EXTENDED_EFR));
+						
+			up->multidrop_mode = 2;//for enable multidrop auto mode 
+
+			lcr_bak = serial_in(up, UART_LCR);//set LCR[7]=0
+			lcr_bak &=~0x80;
+			serial_out(up, UART_LCR, lcr_bak);
+			
+			DEBUG_INTR(KERN_INFO "User request EXAR_SET_MULTIDROP_MODE_AUTO addr:%d \n",up->multidrop_address);
+			ret = 0;	
+			break;
+		case EXAR_SET_REMOVE_MULTIDROP_MODE:
+			//set the DLD[6] = 0 disable Multidrop mode
+			lcr_bak = serial_in(up, UART_LCR);
+			tmp = 0x80 | lcr_bak; //LCR[7]=1 for access DLD
+			
+			dld = serial_in(up, XR_17V35X_UART_DLD);
+			dld &=~0x40;//Disable Multidrop mode
+			serial_out(up, XR_17V35X_UART_DLD, tmp);
+					
+			efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+			efr &=~0x20;//disable the special char select
+			efr |= 0x10; //enable the shaded bits 
+			serial_out(up, XR_17V35X_EXTENDED_EFR, efr);
+
+			
+			serial_out(up,XR_17V35X_UART_MSR, 0x00);//Enable the receiver with mode=0
+			
+             //set EFR[4] =0; disable the shaded bits 
+			efr = serial_in(up, XR_17V35X_EXTENDED_EFR);
+			efr &=~0x10;
+			serial_out(up, XR_17V35X_EXTENDED_EFR, efr);  
+			
+            lcr_bak &=~0x38;//LCR[5:3] = '000'  
+            lcr_bak &=~0x80;//Set LCR[7] = 0 
+			serial_out(up, UART_LCR, lcr_bak);
+			up->multidrop_mode = 0;
+			up->is_match_address = 0;
+		    ret = 0;	
+		   
+		break;
+		case EXAR_SET_NON_STANDARD_BAUDRATE:
+		if (copy_from_user(&any_baudrate, (void *)arg, sizeof(unsigned int)))
+		{
+		   return -EFAULT;	
+		}
+		serialxr_set_special_baudrate(port,any_baudrate);
+		break;
+		
+	}
+	
+	return ret;
+}
+	      
+static void
+serialxr_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+	int lcr;	
+	struct uart_xr_port *up = (struct uart_xr_port *)port;
+	if (state) {
+		/* sleep */
+		serial_out(up, XR_17V35X_EXTENDED_EFR, UART_EFR_ECB);
+		lcr = serial_in(up, UART_LCR);
+		if (lcr & 0x80) {
+			printk(KERN_INFO"channelnum %d: serialxr_pm sleep - LCR = 0x%x", up->channelnum, lcr);
+			serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+		}
+		serial_out(up, UART_IER, UART_IERX_SLEEP);
+		serial_out(up, XR_17V35X_EXTENDED_EFR, 0);
+	} else {
+		/* wake */
+		serial_out(up, XR_17V35X_EXTENDED_EFR, UART_EFR_ECB);
+		lcr = serial_in(up, UART_LCR);
+		if (lcr & 0x80) {
+			printk(KERN_INFO"channelnum %d: serialxr_pm wake - LCR = 0x%x", up->channelnum, lcr);
+			serial_out(up, UART_LCR, lcr & 0x7f);	// Set LCR bit-7=0 when accessing RHR/THR/IER/ISR to avoid incorrect register access
+		}
+		serial_out(up, UART_IER, 0);
+		serial_out(up, XR_17V35X_EXTENDED_EFR, 0);
+	}
+
+	if (up->pm)
+		up->pm(port, state, oldstate);
+}
+
+static void serialxr_release_port(struct uart_port *port)
+{	
+}
+
+static int serialxr_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serialxr_config_port(struct uart_port *port, int flags)
+{
+	struct uart_xr_port *up = (struct uart_xr_port *)port;	
+
+	if (flags & UART_CONFIG_TYPE)
+	{	
+		if(up->deviceid > 0x258) // PCIe device
+		{
+			up->port.type = XRPCIe_TYPE;
+		}
+		else
+		{
+			up->port.type = XRPCI25x_TYPE;
+		}
+		up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
+		up->capabilities = uart_config[up->port.type].flags;	
+	}
+}
+
+static const char *
+serialxr_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 serialxr_pops = {
+	.tx_empty	= serialxr_tx_empty,
+	.set_mctrl	= serialxr_set_mctrl,
+	.get_mctrl	= serialxr_get_mctrl,
+	.stop_tx	= serialxr_stop_tx,
+	.start_tx	= serialxr_start_tx,
+	.stop_rx	= serialxr_stop_rx,
+	.enable_ms	= serialxr_enable_ms,
+	.break_ctl	= serialxr_break_ctl,
+	.startup	= serialxr_startup,
+	.shutdown	= serialxr_shutdown,
+	.set_termios	= serialxr_set_termios,
+	.pm		= serialxr_pm,
+	.type		= serialxr_type,
+	.release_port	= serialxr_release_port,
+	.request_port	= serialxr_request_port,
+	.config_port	= serialxr_config_port,
+	.ioctl		= serialxr_ioctl,
+};
+
+static DEFINE_MUTEX(serial_mutex);
+
+static struct uart_xr_port serialxr_ports[NR_PORTS];
+
+#define SERIALXR_CONSOLE	NULL
+
+static struct uart_driver xr_uart_driver = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "xrserial",
+	.dev_name		= "ttyXR",
+	.major			= XR_MAJOR,
+	.minor			= XR_MINOR,
+	.nr			= NR_PORTS,
+	.cons			= SERIALXR_CONSOLE,
+};
+
+static struct uart_xr_port *serialxr_find_match_or_unused(struct uart_port *port)
+{
+	int i;
+
+	/*
+	 * First, find a port entry which matches.
+	 */
+	for (i = 0; i < NR_PORTS; i++)
+		if (uart_match_port(&serialxr_ports[i].port, port))
+			return &serialxr_ports[i];
+
+	/*
+	 * We didn't find a matching entry, so look for the first
+	 * free entry.  We look for one which hasn't been previously
+	 * used (indicated by zero iobase).
+	 */
+	for (i = 0; i < NR_PORTS; i++)
+		if (serialxr_ports[i].port.type == PORT_UNKNOWN &&
+		    serialxr_ports[i].port.iobase == 0)
+		{
+			port->line = i;
+			return &serialxr_ports[i];
+		}
+
+	/*
+	 * That also failed.  Last resort is to find any entry which
+	 * doesn't have a real port associated with it.
+	 */
+	for (i = 0; i < NR_PORTS; i++)
+		if (serialxr_ports[i].port.type == PORT_UNKNOWN)
+			return &serialxr_ports[i];
+
+	return NULL;
+}
+
+
+/*
+ *	serialxr_register_port - register a serial port
+ *	@port: serial port template
+ *
+ *	Configure the serial port specified by the request. If the
+ *	port exists and is in use, it is hung up and unregistered
+ *	first.
+ *
+ *	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 serialxr_register_port(struct uart_port *port, unsigned short deviceid, unsigned char channelnum)
+{
+	struct uart_xr_port *uart;
+	int ret = -ENOSPC;
+
+	if (port->uartclk == 0)
+		return -EINVAL;
+
+	mutex_lock(&serial_mutex);
+	uart = serialxr_find_match_or_unused(port);
+	if (uart) {
+		uart->port.iobase   = port->iobase;
+		uart->port.membase  = port->membase;
+		uart->port.irq      = port->irq;
+		uart->port.uartclk  = port->uartclk;
+		uart->port.fifosize = port->fifosize;
+		uart->port.regshift = port->regshift;
+		uart->port.iotype   = port->iotype;
+		uart->port.flags    = port->flags | UPF_BOOT_AUTOCONF;
+		uart->port.mapbase  = port->mapbase;
+		if (port->dev)
+			uart->port.dev = port->dev;
+
+		uart->deviceid = deviceid;
+		uart->channelnum = channelnum;
+		uart->port.line = port->line;
+		spin_lock_init(&uart->port.lock);
+
+		#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+		timer_setup(&uart->timer, serialxr_timeout, 0);
+		#else
+		init_timer(&uart->timer);
+		uart->timer.function = serialxr_timeout;
+		#endif
+
+		/*
+		 * ALPHA_KLUDGE_MCR needs to be killed.
+		 */
+		uart->mcr_mask = ~(0x0); //~ALPHA_KLUDGE_MCR;
+		uart->mcr_force = 0; // ALPHA_KLUDGE_MCR;
+
+		uart->port.ops = &serialxr_pops;		
+		
+		ret = uart_add_one_port(&xr_uart_driver, &uart->port);
+#if 0
+		if (ret == 0)
+		{
+			ret = uart->port.line;
+
+			if (is_real_interrupt(uart->port.irq)) {
+				serial_link_irq_chain(uart);
+			}
+		}
+#endif
+	}
+	mutex_unlock(&serial_mutex);
+
+	return ret;
+}
+
+/*
+ * Probe one serial board.  Unfortunately, there is no rhyme nor reason
+ * to the arrangement of serial ports on a PCI card.
+ */
+static int __devinit
+init_one_xrpciserialcard(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+	struct serial_private *priv;
+	struct pciserial_board *board;
+	struct pci_serial_quirk *quirk;
+	struct uart_port serial_port;
+	int rc, nr_ports, i;
+			
+	if (ent->driver_data >= ARRAY_SIZE(xrpciserial_boards)) {
+		printk(KERN_INFO "pci_init_one: invalid driver_data: %ld\n",
+			ent->driver_data);
+		return -EINVAL;
+	}
+
+	board = &xrpciserial_boards[ent->driver_data];
+
+	rc = pci_enable_device(dev);
+	if (rc)
+		return rc;
+	
+	nr_ports = board->num_ports;
+
+	/*
+	 * Find an init and setup quirks.
+	 */
+	quirk = find_quirk(dev);
+
+	/*
+	 * Run the new-style initialization function.
+	 * The initialization function returns:
+	 *  <0  - error
+	 *   0  - use board->num_ports
+	 *  >0  - number of ports
+	 */
+	if (quirk->init) {
+		rc = quirk->init(dev);
+		if (rc < 0)
+			goto disable;
+		if (rc)
+			nr_ports = rc;
+	}
+
+	priv = kmalloc(sizeof(struct serial_private) +
+		       sizeof(unsigned int) * nr_ports,
+		       GFP_KERNEL);
+	if (!priv) {
+		rc = -ENOMEM;
+		goto deinit;
+	}
+
+	memset(priv, 0, sizeof(struct serial_private) +
+			sizeof(unsigned int) * nr_ports);
+
+	priv->dev = dev;
+	priv->quirk = quirk;
+
+	memset(&serial_port, 0, sizeof(struct uart_port));
+	serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+
+	if((priv->dev->device == 0x152)	||(priv->dev->device == 0x154)||(priv->dev->device == 0x158))
+		serial_port.uartclk = board->base_baud * 16;
+	else
+		serial_port.uartclk = board->base_baud * 4;
+	serial_port.irq = dev->irq;
+	serial_port.dev = &dev->dev;
+	for (i = 0; i < nr_ports; i++) {
+		if (quirk->setup(priv, board, &serial_port, i))
+			break;
+
+		// setup the uartclock for the devices on expansion slot
+		switch(priv->dev->device)
+		{
+		    case 0x4354:	      
+		    case 0x8354:
+		      if(i >= 4)
+			serial_port.uartclk = 62500000; // half the clock speed of the main chip (which is 125MHz)
+		      break;
+
+		    case 0x4358:	      
+		    case 0x8358:
+		      if(i >= 8) // epansions slot ports
+			serial_port.uartclk = 62500000; // half the clock speed of the main chip (which is 125MHz)
+		      break;
+
+		    default: //0x358/354/352/258/254/252
+		    break;
+		}
+
+		rc = serialxr_register_port(&serial_port, dev->device,i);
+		if (rc < 0) {
+			printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), i);
+			break;
+		}
+				
+		printk(KERN_WARNING "init_one_xrpciserialcard line:%d\n",serial_port.line);
+		priv->uart_index[i] = serial_port.line;
+		priv->line[i] = rc;
+		
+		
+	}
+
+	priv->nr = i;
+
+	if (!IS_ERR(priv)) {
+		pci_set_drvdata(dev, priv);
+		return 0;
+	}
+
+ deinit:
+	if (quirk->exit)
+		quirk->exit(dev);
+ disable:
+	pci_disable_device(dev);
+	return rc;
+}
+
+/*
+ *	serialxr_unregister_port - remove a serial port at runtime
+ *	@line: serial line number
+ *
+ *	Remove one serial port.  This may not be called from interrupt
+ *	context.  We hand the port back to the our control.
+ */
+void serialxr_unregister_port(int line)
+{
+	struct uart_xr_port *uart = &serialxr_ports[line];
+
+	mutex_lock(&serial_mutex);
+#if 0
+	if (is_real_interrupt(uart->port.irq))
+	  serial_unlink_irq_chain(uart);
+#endif
+	uart_remove_one_port(&xr_uart_driver, &uart->port);
+	uart->port.dev = NULL;	
+	mutex_unlock(&serial_mutex);
+}
+
+void pciserial_remove_ports(struct serial_private *priv)
+{
+	struct pci_serial_quirk *quirk;
+	int i;
+
+	for (i = 0; i < priv->nr; i++)
+	{
+	  	printk(KERN_WARNING "pciserial_remove_ports dev:%p port_num:%d\n",priv->dev,priv->uart_index[i]);
+		//serialxr_unregister_port(priv->line[i]);
+		serialxr_unregister_port(priv->uart_index[i]);
+		
+	}	
+
+	for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
+		if (priv->remapped_bar[i])
+			iounmap(priv->remapped_bar[i]);
+		priv->remapped_bar[i] = NULL;
+	}
+
+	/*
+	 * Find the exit quirks.
+	 */
+	quirk = find_quirk(priv->dev);
+	if (quirk->exit)
+		quirk->exit(priv->dev);
+
+	kfree(priv);
+}
+
+static void __devexit remove_one_xrpciserialcard(struct pci_dev *dev)
+{
+	struct serial_private *priv = pci_get_drvdata(dev);
+
+	pci_set_drvdata(dev, NULL);
+
+	pciserial_remove_ports(priv);
+
+	pci_disable_device(dev);
+}
+
+
+static struct pci_device_id xrserial_pci_tbl[] = {
+	{	0x13a8, 0x358,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_8port },
+	{	0x13a8, 0x354,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_4port },
+	{	0x13a8, 0x352,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_2port },
+	{	0x13a8, 0x4354,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_4354port },
+	{	0x13a8, 0x8354,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_8354port },
+	{	0x13a8, 0x4358,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_4358port },
+	{	0x13a8, 0x8358,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_8358port },
+	{	0x13a8, 0x258,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_258port },
+	{	0x13a8, 0x254,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_254port },
+	{	0x13a8, 0x252,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_252port },
+	{	0x13a8, 0x158,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_158port },
+	{	0x13a8, 0x154,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_154port },
+	{	0x13a8, 0x152,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, xr_152port },
+
+	{ 0, }
+};
+
+static struct pci_driver xrserial_pci_driver = {
+	.name		= "xrserial",
+	.probe		= init_one_xrpciserialcard,
+	.remove		= __devexit_p(remove_one_xrpciserialcard),
+	.id_table	= xrserial_pci_tbl,
+};
+
+static int __init serialxr_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Exar PCIe (XR17V35x) serial driver Revision: 2.6\n");
+
+	ret = uart_register_driver(&xr_uart_driver);
+	if (ret)
+		return ret;
+
+	ret = pci_register_driver(&xrserial_pci_driver);
+
+	if (ret < 0)
+		uart_unregister_driver(&xr_uart_driver);
+
+	return ret;	
+}
+
+static void __exit serialxr_exit(void)
+{
+	pci_unregister_driver(&xrserial_pci_driver);
+	uart_unregister_driver(&xr_uart_driver);
+}
+
+module_init(serialxr_init);
+module_exit(serialxr_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Exar PCIe specific serial driver for XR17V35x- Revision: 2.6");