Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c
new file mode 100644
index 0000000..546a0bc
--- /dev/null
+++ b/drivers/serial/icom.c
@@ -0,0 +1,1691 @@
+/*
+  * icom.c
+  *
+  * Copyright (C) 2001 IBM Corporation. All rights reserved.
+  *
+  * Serial device driver.
+  *
+  * Based on code from serial.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  *
+  */
+#define SERIAL_DO_RESTART
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/fs.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/kobject.h>
+#include <linux/firmware.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "icom.h"
+
+/*#define ICOM_TRACE		 enable port trace capabilities */
+
+#define ICOM_DRIVER_NAME "icom"
+#define ICOM_VERSION_STR "1.3.1"
+#define NR_PORTS	       128
+#define ICOM_PORT ((struct icom_port *)port)
+#define to_icom_adapter(d) container_of(d, struct icom_adapter, kobj)
+
+static const struct pci_device_id icom_pci_table[] = {
+	{
+	      .vendor = PCI_VENDOR_ID_IBM,
+	      .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1,
+	      .subvendor = PCI_ANY_ID,
+	      .subdevice = PCI_ANY_ID,
+	      .driver_data = ADAPTER_V1,
+	 },
+	{
+	      .vendor = PCI_VENDOR_ID_IBM,
+	      .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
+	      .subvendor = PCI_VENDOR_ID_IBM,
+	      .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX,
+	      .driver_data = ADAPTER_V2,
+	 },
+	{
+	      .vendor = PCI_VENDOR_ID_IBM,
+	      .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
+	      .subvendor = PCI_VENDOR_ID_IBM,
+	      .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM,
+	      .driver_data = ADAPTER_V2,
+	 },
+	{
+	      .vendor = PCI_VENDOR_ID_IBM,
+	      .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
+	      .subvendor = PCI_VENDOR_ID_IBM,
+	      .subdevice = PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL,
+	      .driver_data = ADAPTER_V2,
+	 },
+	{}
+};
+
+struct lookup_proc_table start_proc[4] = {
+	{NULL, ICOM_CONTROL_START_A},
+	{NULL, ICOM_CONTROL_START_B},
+	{NULL, ICOM_CONTROL_START_C},
+	{NULL, ICOM_CONTROL_START_D}
+};
+
+
+struct lookup_proc_table stop_proc[4] = {
+	{NULL, ICOM_CONTROL_STOP_A},
+	{NULL, ICOM_CONTROL_STOP_B},
+	{NULL, ICOM_CONTROL_STOP_C},
+	{NULL, ICOM_CONTROL_STOP_D}
+};
+
+struct lookup_int_table int_mask_tbl[4] = {
+	{NULL, ICOM_INT_MASK_PRC_A},
+	{NULL, ICOM_INT_MASK_PRC_B},
+	{NULL, ICOM_INT_MASK_PRC_C},
+	{NULL, ICOM_INT_MASK_PRC_D},
+};
+
+
+MODULE_DEVICE_TABLE(pci, icom_pci_table);
+
+static LIST_HEAD(icom_adapter_head);
+
+/* spinlock for adapter initialization and changing adapter operations */
+static spinlock_t icom_lock;
+
+#ifdef ICOM_TRACE
+static inline void trace(struct icom_port *, char *, unsigned long) {};
+#else
+static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {};
+#endif
+
+static void free_port_memory(struct icom_port *icom_port)
+{
+	struct pci_dev *dev = icom_port->adapter->pci_dev;
+
+	trace(icom_port, "RET_PORT_MEM", 0);
+	if (icom_port->recv_buf) {
+		pci_free_consistent(dev, 4096, icom_port->recv_buf,
+				    icom_port->recv_buf_pci);
+		icom_port->recv_buf = NULL;
+	}
+	if (icom_port->xmit_buf) {
+		pci_free_consistent(dev, 4096, icom_port->xmit_buf,
+				    icom_port->xmit_buf_pci);
+		icom_port->xmit_buf = NULL;
+	}
+	if (icom_port->statStg) {
+		pci_free_consistent(dev, 4096, icom_port->statStg,
+				    icom_port->statStg_pci);
+		icom_port->statStg = NULL;
+	}
+
+	if (icom_port->xmitRestart) {
+		pci_free_consistent(dev, 4096, icom_port->xmitRestart,
+				    icom_port->xmitRestart_pci);
+		icom_port->xmitRestart = NULL;
+	}
+}
+
+static int __init get_port_memory(struct icom_port *icom_port)
+{
+	int index;
+	unsigned long stgAddr;
+	unsigned long startStgAddr;
+	unsigned long offset;
+	struct pci_dev *dev = icom_port->adapter->pci_dev;
+
+	icom_port->xmit_buf =
+	    pci_alloc_consistent(dev, 4096, &icom_port->xmit_buf_pci);
+	if (!icom_port->xmit_buf) {
+		dev_err(&dev->dev, "Can not allocate Transmit buffer\n");
+		return -ENOMEM;
+	}
+
+	trace(icom_port, "GET_PORT_MEM",
+	      (unsigned long) icom_port->xmit_buf);
+
+	icom_port->recv_buf =
+	    pci_alloc_consistent(dev, 4096, &icom_port->recv_buf_pci);
+	if (!icom_port->recv_buf) {
+		dev_err(&dev->dev, "Can not allocate Receive buffer\n");
+		free_port_memory(icom_port);
+		return -ENOMEM;
+	}
+	trace(icom_port, "GET_PORT_MEM",
+	      (unsigned long) icom_port->recv_buf);
+
+	icom_port->statStg =
+	    pci_alloc_consistent(dev, 4096, &icom_port->statStg_pci);
+	if (!icom_port->statStg) {
+		dev_err(&dev->dev, "Can not allocate Status buffer\n");
+		free_port_memory(icom_port);
+		return -ENOMEM;
+	}
+	trace(icom_port, "GET_PORT_MEM",
+	      (unsigned long) icom_port->statStg);
+
+	icom_port->xmitRestart =
+	    pci_alloc_consistent(dev, 4096, &icom_port->xmitRestart_pci);
+	if (!icom_port->xmitRestart) {
+		dev_err(&dev->dev,
+			"Can not allocate xmit Restart buffer\n");
+		free_port_memory(icom_port);
+		return -ENOMEM;
+	}
+
+	memset(icom_port->statStg, 0, 4096);
+
+	/* FODs: Frame Out Descriptor Queue, this is a FIFO queue that
+           indicates that frames are to be transmitted
+	*/
+
+	stgAddr = (unsigned long) icom_port->statStg;
+	for (index = 0; index < NUM_XBUFFS; index++) {
+		trace(icom_port, "FOD_ADDR", stgAddr);
+		stgAddr = stgAddr + sizeof(icom_port->statStg->xmit[0]);
+		if (index < (NUM_XBUFFS - 1)) {
+			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
+			icom_port->statStg->xmit[index].leLengthASD =
+			    (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ);
+			trace(icom_port, "FOD_ADDR", stgAddr);
+			trace(icom_port, "FOD_XBUFF",
+			      (unsigned long) icom_port->xmit_buf);
+			icom_port->statStg->xmit[index].leBuffer =
+			    cpu_to_le32(icom_port->xmit_buf_pci);
+		} else if (index == (NUM_XBUFFS - 1)) {
+			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
+			icom_port->statStg->xmit[index].leLengthASD =
+			    (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ);
+			trace(icom_port, "FOD_XBUFF",
+			      (unsigned long) icom_port->xmit_buf);
+			icom_port->statStg->xmit[index].leBuffer =
+			    cpu_to_le32(icom_port->xmit_buf_pci);
+		} else {
+			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
+		}
+	}
+	/* FIDs */
+	startStgAddr = stgAddr;
+
+	/* fill in every entry, even if no buffer */
+	for (index = 0; index <  NUM_RBUFFS; index++) {
+		trace(icom_port, "FID_ADDR", stgAddr);
+		stgAddr = stgAddr + sizeof(icom_port->statStg->rcv[0]);
+		icom_port->statStg->rcv[index].leLength = 0;
+		icom_port->statStg->rcv[index].WorkingLength =
+		    (unsigned short int) cpu_to_le16(RCV_BUFF_SZ);
+		if (index < (NUM_RBUFFS - 1) ) {
+			offset = stgAddr - (unsigned long) icom_port->statStg;
+			icom_port->statStg->rcv[index].leNext =
+			      cpu_to_le32(icom_port-> statStg_pci + offset);
+			trace(icom_port, "FID_RBUFF",
+			      (unsigned long) icom_port->recv_buf);
+			icom_port->statStg->rcv[index].leBuffer =
+			    cpu_to_le32(icom_port->recv_buf_pci);
+		} else if (index == (NUM_RBUFFS -1) ) {
+			offset = startStgAddr - (unsigned long) icom_port->statStg;
+			icom_port->statStg->rcv[index].leNext =
+			    cpu_to_le32(icom_port-> statStg_pci + offset);
+			trace(icom_port, "FID_RBUFF",
+			      (unsigned long) icom_port->recv_buf + 2048);
+			icom_port->statStg->rcv[index].leBuffer =
+			    cpu_to_le32(icom_port->recv_buf_pci + 2048);
+		} else {
+			icom_port->statStg->rcv[index].leNext = 0;
+			icom_port->statStg->rcv[index].leBuffer = 0;
+		}
+	}
+
+	return 0;
+}
+
+static void stop_processor(struct icom_port *icom_port)
+{
+	unsigned long temp;
+	unsigned long flags;
+	int port;
+
+	spin_lock_irqsave(&icom_lock, flags);
+
+	port = icom_port->port;
+	if (port == 0 || port == 1)
+		stop_proc[port].global_control_reg = &icom_port->global_reg->control;
+	else
+		stop_proc[port].global_control_reg = &icom_port->global_reg->control_2;
+
+
+	if (port < 4) {
+		temp = readl(stop_proc[port].global_control_reg);
+		temp =
+	    		(temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id;
+		writel(temp, stop_proc[port].global_control_reg);
+
+		/* write flush */
+		readl(stop_proc[port].global_control_reg);
+	} else {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+                        "Invalid port assignment\n");
+	}
+
+	spin_unlock_irqrestore(&icom_lock, flags);
+}
+
+static void start_processor(struct icom_port *icom_port)
+{
+	unsigned long temp;
+	unsigned long flags;
+	int port;
+
+	spin_lock_irqsave(&icom_lock, flags);
+
+	port = icom_port->port;
+	if (port == 0 || port == 1)
+		start_proc[port].global_control_reg = &icom_port->global_reg->control;
+	else
+		start_proc[port].global_control_reg = &icom_port->global_reg->control_2;
+	if (port < 4) {
+		temp = readl(start_proc[port].global_control_reg);
+		temp =
+	    		(temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id;
+		writel(temp, start_proc[port].global_control_reg);
+
+		/* write flush */
+		readl(start_proc[port].global_control_reg);
+	} else {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+                        "Invalid port assignment\n");
+	}
+
+	spin_unlock_irqrestore(&icom_lock, flags);
+}
+
+static void load_code(struct icom_port *icom_port)
+{
+	const struct firmware *fw;
+	char __iomem *iram_ptr;
+	int index;
+	int status = 0;
+	void __iomem *dram_ptr = icom_port->dram;
+	dma_addr_t temp_pci;
+	unsigned char *new_page = NULL;
+	unsigned char cable_id = NO_CABLE;
+	struct pci_dev *dev = icom_port->adapter->pci_dev;
+
+	/* Clear out any pending interrupts */
+	writew(0x3FFF, icom_port->int_reg);
+
+	trace(icom_port, "CLEAR_INTERRUPTS", 0);
+
+	/* Stop processor */
+	stop_processor(icom_port);
+
+	/* Zero out DRAM */
+	memset_io(dram_ptr, 0, 512);
+
+	/* Load Call Setup into Adapter */
+	if (request_firmware(&fw, "icom_call_setup.bin", &dev->dev) < 0) {
+		dev_err(&dev->dev,"Unable to load icom_call_setup.bin firmware image\n");
+		status = -1;
+		goto load_code_exit;
+	}
+
+	if (fw->size > ICOM_DCE_IRAM_OFFSET) {
+		dev_err(&dev->dev, "Invalid firmware image for icom_call_setup.bin found.\n");
+		release_firmware(fw);
+		status = -1;
+		goto load_code_exit;
+	}
+
+	iram_ptr = (char __iomem *)icom_port->dram + ICOM_IRAM_OFFSET;
+	for (index = 0; index < fw->size; index++)
+		writeb(fw->data[index], &iram_ptr[index]);
+
+	release_firmware(fw);
+
+	/* Load Resident DCE portion of Adapter */
+	if (request_firmware(&fw, "icom_res_dce.bin", &dev->dev) < 0) {
+		dev_err(&dev->dev,"Unable to load icom_res_dce.bin firmware image\n");
+		status = -1;
+		goto load_code_exit;
+	}
+
+	if (fw->size > ICOM_IRAM_SIZE) {
+		dev_err(&dev->dev, "Invalid firmware image for icom_res_dce.bin found.\n");
+		release_firmware(fw);
+		status = -1;
+		goto load_code_exit;
+	}
+
+	iram_ptr = (char __iomem *) icom_port->dram + ICOM_IRAM_OFFSET;
+	for (index = ICOM_DCE_IRAM_OFFSET; index < fw->size; index++)
+		writeb(fw->data[index], &iram_ptr[index]);
+
+	release_firmware(fw);
+
+	/* Set Hardware level */
+	if ((icom_port->adapter->version | ADAPTER_V2) == ADAPTER_V2)
+		writeb(V2_HARDWARE, &(icom_port->dram->misc_flags));
+
+	/* Start the processor in Adapter */
+	start_processor(icom_port);
+
+	writeb((HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL),
+	       &(icom_port->dram->HDLCConfigReg));
+	writeb(0x04, &(icom_port->dram->FlagFillIdleTimer));	/* 0.5 seconds */
+	writeb(0x00, &(icom_port->dram->CmdReg));
+	writeb(0x10, &(icom_port->dram->async_config3));
+	writeb((ICOM_ACFG_DRIVE1 | ICOM_ACFG_NO_PARITY | ICOM_ACFG_8BPC |
+		ICOM_ACFG_1STOP_BIT), &(icom_port->dram->async_config2));
+
+	/*Set up data in icom DRAM to indicate where personality
+	 *code is located and its length.
+	 */
+	new_page = pci_alloc_consistent(dev, 4096, &temp_pci);
+
+	if (!new_page) {
+		dev_err(&dev->dev, "Can not allocate DMA buffer\n");
+		status = -1;
+		goto load_code_exit;
+	}
+
+	if (request_firmware(&fw, "icom_asc.bin", &dev->dev) < 0) {
+		dev_err(&dev->dev,"Unable to load icom_asc.bin firmware image\n");
+		status = -1;
+		goto load_code_exit;
+	}
+
+	if (fw->size > ICOM_DCE_IRAM_OFFSET) {
+		dev_err(&dev->dev, "Invalid firmware image for icom_asc.bin found.\n");
+		release_firmware(fw);
+		status = -1;
+		goto load_code_exit;
+	}
+
+	for (index = 0; index < fw->size; index++)
+		new_page[index] = fw->data[index];
+
+	release_firmware(fw);
+
+	writeb((char) ((fw->size + 16)/16), &icom_port->dram->mac_length);
+	writel(temp_pci, &icom_port->dram->mac_load_addr);
+
+	/*Setting the syncReg to 0x80 causes adapter to start downloading
+	   the personality code into adapter instruction RAM.
+	   Once code is loaded, it will begin executing and, based on
+	   information provided above, will start DMAing data from
+	   shared memory to adapter DRAM.
+	 */
+	/* the wait loop below verifies this write operation has been done
+	   and processed
+	*/
+	writeb(START_DOWNLOAD, &icom_port->dram->sync);
+
+	/* Wait max 1 Sec for data download and processor to start */
+	for (index = 0; index < 10; index++) {
+		msleep(100);
+		if (readb(&icom_port->dram->misc_flags) & ICOM_HDW_ACTIVE)
+			break;
+	}
+
+	if (index == 10)
+		status = -1;
+
+	/*
+	 * check Cable ID
+	 */
+	cable_id = readb(&icom_port->dram->cable_id);
+
+	if (cable_id & ICOM_CABLE_ID_VALID) {
+		/* Get cable ID into the lower 4 bits (standard form) */
+		cable_id = (cable_id & ICOM_CABLE_ID_MASK) >> 4;
+		icom_port->cable_id = cable_id;
+	} else {
+		dev_err(&dev->dev,"Invalid or no cable attached\n");
+		icom_port->cable_id = NO_CABLE;
+	}
+
+      load_code_exit:
+
+	if (status != 0) {
+		/* Clear out any pending interrupts */
+		writew(0x3FFF, icom_port->int_reg);
+
+		/* Turn off port */
+		writeb(ICOM_DISABLE, &(icom_port->dram->disable));
+
+		/* Stop processor */
+		stop_processor(icom_port);
+
+		dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n");
+	}
+
+      if (new_page != NULL)
+	      pci_free_consistent(dev, 4096, new_page, temp_pci);
+}
+
+static int startup(struct icom_port *icom_port)
+{
+	unsigned long temp;
+	unsigned char cable_id, raw_cable_id;
+	unsigned long flags;
+	int port;
+
+	trace(icom_port, "STARTUP", 0);
+
+	if (!icom_port->dram) {
+		/* should NEVER be NULL */
+		dev_err(&icom_port->adapter->pci_dev->dev,
+			"Unusable Port, port configuration missing\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * check Cable ID
+	 */
+	raw_cable_id = readb(&icom_port->dram->cable_id);
+	trace(icom_port, "CABLE_ID", raw_cable_id);
+
+	/* Get cable ID into the lower 4 bits (standard form) */
+	cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4;
+
+	/* Check for valid Cable ID */
+	if (!(raw_cable_id & ICOM_CABLE_ID_VALID) ||
+	    (cable_id != icom_port->cable_id)) {
+
+		/* reload adapter code, pick up any potential changes in cable id */
+		load_code(icom_port);
+
+		/* still no sign of cable, error out */
+		raw_cable_id = readb(&icom_port->dram->cable_id);
+		cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4;
+		if (!(raw_cable_id & ICOM_CABLE_ID_VALID) ||
+		    (icom_port->cable_id == NO_CABLE))
+			return -EIO;
+	}
+
+	/*
+	 * Finally, clear and  enable interrupts
+	 */
+	spin_lock_irqsave(&icom_lock, flags);
+	port = icom_port->port;
+	if (port == 0 || port == 1)
+		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
+	else
+		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
+
+	if (port == 0 || port == 2)
+		writew(0x00FF, icom_port->int_reg);
+	else
+		writew(0x3F00, icom_port->int_reg);
+	if (port < 4) {
+		temp = readl(int_mask_tbl[port].global_int_mask);
+		writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
+
+		/* write flush */
+		readl(int_mask_tbl[port].global_int_mask);
+	} else {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+                        "Invalid port assignment\n");
+	}
+
+	spin_unlock_irqrestore(&icom_lock, flags);
+	return 0;
+}
+
+static void shutdown(struct icom_port *icom_port)
+{
+	unsigned long temp;
+	unsigned char cmdReg;
+	unsigned long flags;
+	int port;
+
+	spin_lock_irqsave(&icom_lock, flags);
+	trace(icom_port, "SHUTDOWN", 0);
+
+	/*
+	 * disable all interrupts
+	 */
+	port = icom_port->port;
+	if (port == 0 || port == 1)
+		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
+	else
+		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
+
+	if (port < 4) {
+		temp = readl(int_mask_tbl[port].global_int_mask);
+		writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
+
+		/* write flush */
+		readl(int_mask_tbl[port].global_int_mask);
+	} else {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+                        "Invalid port assignment\n");
+	}
+	spin_unlock_irqrestore(&icom_lock, flags);
+
+	/*
+	 * disable break condition
+	 */
+	cmdReg = readb(&icom_port->dram->CmdReg);
+	if ((cmdReg | CMD_SND_BREAK) == CMD_SND_BREAK) {
+		writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg);
+	}
+}
+
+static int icom_write(struct uart_port *port)
+{
+	unsigned long data_count;
+	unsigned char cmdReg;
+	unsigned long offset;
+	int temp_tail = port->info->xmit.tail;
+
+	trace(ICOM_PORT, "WRITE", 0);
+
+	if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) &
+	    SA_FLAGS_READY_TO_XMIT) {
+		trace(ICOM_PORT, "WRITE_FULL", 0);
+		return 0;
+	}
+
+	data_count = 0;
+	while ((port->info->xmit.head != temp_tail) &&
+	       (data_count <= XMIT_BUFF_SZ)) {
+
+		ICOM_PORT->xmit_buf[data_count++] =
+		    port->info->xmit.buf[temp_tail];
+
+		temp_tail++;
+		temp_tail &= (UART_XMIT_SIZE - 1);
+	}
+
+	if (data_count) {
+		ICOM_PORT->statStg->xmit[0].flags =
+		    cpu_to_le16(SA_FLAGS_READY_TO_XMIT);
+		ICOM_PORT->statStg->xmit[0].leLength =
+		    cpu_to_le16(data_count);
+		offset =
+		    (unsigned long) &ICOM_PORT->statStg->xmit[0] -
+		    (unsigned long) ICOM_PORT->statStg;
+		*ICOM_PORT->xmitRestart =
+		    cpu_to_le32(ICOM_PORT->statStg_pci + offset);
+		cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+		writeb(cmdReg | CMD_XMIT_RCV_ENABLE,
+		       &ICOM_PORT->dram->CmdReg);
+		writeb(START_XMIT, &ICOM_PORT->dram->StartXmitCmd);
+		trace(ICOM_PORT, "WRITE_START", data_count);
+		/* write flush */
+		readb(&ICOM_PORT->dram->StartXmitCmd);
+	}
+
+	return data_count;
+}
+
+static inline void check_modem_status(struct icom_port *icom_port)
+{
+	static char old_status = 0;
+	char delta_status;
+	unsigned char status;
+
+	spin_lock(&icom_port->uart_port.lock);
+
+	/*modem input register */
+	status = readb(&icom_port->dram->isr);
+	trace(icom_port, "CHECK_MODEM", status);
+	delta_status = status ^ old_status;
+	if (delta_status) {
+		if (delta_status & ICOM_RI)
+			icom_port->uart_port.icount.rng++;
+		if (delta_status & ICOM_DSR)
+			icom_port->uart_port.icount.dsr++;
+		if (delta_status & ICOM_DCD)
+			uart_handle_dcd_change(&icom_port->uart_port,
+					       delta_status & ICOM_DCD);
+		if (delta_status & ICOM_CTS)
+			uart_handle_cts_change(&icom_port->uart_port,
+					       delta_status & ICOM_CTS);
+
+		wake_up_interruptible(&icom_port->uart_port.info->
+				      delta_msr_wait);
+		old_status = status;
+	}
+	spin_unlock(&icom_port->uart_port.lock);
+}
+
+static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
+{
+	unsigned short int count;
+	int i;
+
+	if (port_int_reg & (INT_XMIT_COMPLETED)) {
+		trace(icom_port, "XMIT_COMPLETE", 0);
+
+		/* clear buffer in use bit */
+		icom_port->statStg->xmit[0].flags &=
+			cpu_to_le16(~SA_FLAGS_READY_TO_XMIT);
+
+		count = (unsigned short int)
+			cpu_to_le16(icom_port->statStg->xmit[0].leLength);
+		icom_port->uart_port.icount.tx += count;
+
+		for (i=0; i<count &&
+			!uart_circ_empty(&icom_port->uart_port.info->xmit); i++) {
+
+			icom_port->uart_port.info->xmit.tail++;
+			icom_port->uart_port.info->xmit.tail &=
+				(UART_XMIT_SIZE - 1);
+		}
+
+		if (!icom_write(&icom_port->uart_port))
+			/* activate write queue */
+			uart_write_wakeup(&icom_port->uart_port);
+	} else
+		trace(icom_port, "XMIT_DISABLED", 0);
+}
+
+static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port)
+{
+	short int count, rcv_buff;
+	struct tty_struct *tty = icom_port->uart_port.info->tty;
+	unsigned short int status;
+	struct uart_icount *icount;
+	unsigned long offset;
+
+	trace(icom_port, "RCV_COMPLETE", 0);
+	rcv_buff = icom_port->next_rcv;
+
+	status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags);
+	while (status & SA_FL_RCV_DONE) {
+
+		trace(icom_port, "FID_STATUS", status);
+		count = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].leLength);
+
+		trace(icom_port, "RCV_COUNT", count);
+		if (count > (TTY_FLIPBUF_SIZE - tty->flip.count))
+			count = TTY_FLIPBUF_SIZE - tty->flip.count;
+
+		trace(icom_port, "REAL_COUNT", count);
+
+		offset =
+			cpu_to_le32(icom_port->statStg->rcv[rcv_buff].leBuffer) -
+			icom_port->recv_buf_pci;
+
+		memcpy(tty->flip.char_buf_ptr,(unsigned char *)
+		       ((unsigned long)icom_port->recv_buf + offset), count);
+
+		if (count > 0) {
+			tty->flip.count += count - 1;
+			tty->flip.char_buf_ptr += count - 1;
+
+			memset(tty->flip.flag_buf_ptr, 0, count);
+			tty->flip.flag_buf_ptr += count - 1;
+		}
+
+		icount = &icom_port->uart_port.icount;
+		icount->rx += count;
+
+		/* Break detect logic */
+		if ((status & SA_FLAGS_FRAME_ERROR)
+		    && (tty->flip.char_buf_ptr[0] == 0x00)) {
+			status &= ~SA_FLAGS_FRAME_ERROR;
+			status |= SA_FLAGS_BREAK_DET;
+			trace(icom_port, "BREAK_DET", 0);
+		}
+
+		if (status &
+		    (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR |
+		     SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) {
+
+			if (status & SA_FLAGS_BREAK_DET)
+				icount->brk++;
+			if (status & SA_FLAGS_PARITY_ERROR)
+				icount->parity++;
+			if (status & SA_FLAGS_FRAME_ERROR)
+				icount->frame++;
+			if (status & SA_FLAGS_OVERRUN)
+				icount->overrun++;
+
+			/*
+			 * Now check to see if character should be
+			 * ignored, and mask off conditions which
+			 * should be ignored.
+			 */
+			if (status & icom_port->ignore_status_mask) {
+				trace(icom_port, "IGNORE_CHAR", 0);
+				goto ignore_char;
+			}
+
+			status &= icom_port->read_status_mask;
+
+			if (status & SA_FLAGS_BREAK_DET) {
+				*tty->flip.flag_buf_ptr = TTY_BREAK;
+			} else if (status & SA_FLAGS_PARITY_ERROR) {
+				trace(icom_port, "PARITY_ERROR", 0);
+				*tty->flip.flag_buf_ptr = TTY_PARITY;
+			} else if (status & SA_FLAGS_FRAME_ERROR)
+				*tty->flip.flag_buf_ptr = TTY_FRAME;
+
+			if (status & SA_FLAGS_OVERRUN) {
+				/*
+				 * Overrun is special, since it's
+				 * reported immediately, and doesn't
+				 * affect the current character
+				 */
+				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+					tty->flip.count++;
+					tty->flip.flag_buf_ptr++;
+					tty->flip.char_buf_ptr++;
+					*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+				}
+			}
+		}
+
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+		ignore_char:
+			icom_port->statStg->rcv[rcv_buff].flags = 0;
+		icom_port->statStg->rcv[rcv_buff].leLength = 0;
+		icom_port->statStg->rcv[rcv_buff].WorkingLength =
+			(unsigned short int) cpu_to_le16(RCV_BUFF_SZ);
+
+		rcv_buff++;
+		if (rcv_buff == NUM_RBUFFS)
+			rcv_buff = 0;
+
+		status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags);
+	}
+	icom_port->next_rcv = rcv_buff;
+	tty_flip_buffer_push(tty);
+}
+
+static void process_interrupt(u16 port_int_reg,
+			      struct icom_port *icom_port)
+{
+
+	spin_lock(&icom_port->uart_port.lock);
+	trace(icom_port, "INTERRUPT", port_int_reg);
+
+	if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED))
+		xmit_interrupt(port_int_reg, icom_port);
+
+	if (port_int_reg & INT_RCV_COMPLETED)
+		recv_interrupt(port_int_reg, icom_port);
+
+	spin_unlock(&icom_port->uart_port.lock);
+}
+
+static irqreturn_t icom_interrupt(int irq, void *dev_id,
+				  struct pt_regs *regs)
+{
+	void __iomem * int_reg;
+	u32 adapter_interrupts;
+	u16 port_int_reg;
+	struct icom_adapter *icom_adapter;
+	struct icom_port *icom_port;
+
+	/* find icom_port for this interrupt */
+	icom_adapter = (struct icom_adapter *) dev_id;
+
+	if ((icom_adapter->version | ADAPTER_V2) == ADAPTER_V2) {
+		int_reg = icom_adapter->base_addr + 0x8024;
+
+		adapter_interrupts = readl(int_reg);
+
+		if (adapter_interrupts & 0x00003FFF) {
+			/* port 2 interrupt,  NOTE:  for all ADAPTER_V2, port 2 will be active */
+			icom_port = &icom_adapter->port_info[2];
+			port_int_reg = (u16) adapter_interrupts;
+			process_interrupt(port_int_reg, icom_port);
+			check_modem_status(icom_port);
+		}
+		if (adapter_interrupts & 0x3FFF0000) {
+			/* port 3 interrupt */
+			icom_port = &icom_adapter->port_info[3];
+			if (icom_port->status == ICOM_PORT_ACTIVE) {
+				port_int_reg =
+				    (u16) (adapter_interrupts >> 16);
+				process_interrupt(port_int_reg, icom_port);
+				check_modem_status(icom_port);
+			}
+		}
+
+		/* Clear out any pending interrupts */
+		writel(adapter_interrupts, int_reg);
+
+		int_reg = icom_adapter->base_addr + 0x8004;
+	} else {
+		int_reg = icom_adapter->base_addr + 0x4004;
+	}
+
+	adapter_interrupts = readl(int_reg);
+
+	if (adapter_interrupts & 0x00003FFF) {
+		/* port 0 interrupt, NOTE:  for all adapters, port 0 will be active */
+		icom_port = &icom_adapter->port_info[0];
+		port_int_reg = (u16) adapter_interrupts;
+		process_interrupt(port_int_reg, icom_port);
+		check_modem_status(icom_port);
+	}
+	if (adapter_interrupts & 0x3FFF0000) {
+		/* port 1 interrupt */
+		icom_port = &icom_adapter->port_info[1];
+		if (icom_port->status == ICOM_PORT_ACTIVE) {
+			port_int_reg = (u16) (adapter_interrupts >> 16);
+			process_interrupt(port_int_reg, icom_port);
+			check_modem_status(icom_port);
+		}
+	}
+
+	/* Clear out any pending interrupts */
+	writel(adapter_interrupts, int_reg);
+
+	/* flush the write */
+	adapter_interrupts = readl(int_reg);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * ------------------------------------------------------------------
+ * Begin serial-core API
+ * ------------------------------------------------------------------
+ */
+static unsigned int icom_tx_empty(struct uart_port *port)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) &
+	    SA_FLAGS_READY_TO_XMIT)
+		ret = TIOCSER_TEMT;
+	else
+		ret = 0;
+
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+static void icom_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	unsigned char local_osr;
+
+	trace(ICOM_PORT, "SET_MODEM", 0);
+	local_osr = readb(&ICOM_PORT->dram->osr);
+
+	if (mctrl & TIOCM_RTS) {
+		trace(ICOM_PORT, "RAISE_RTS", 0);
+		local_osr |= ICOM_RTS;
+	} else {
+		trace(ICOM_PORT, "LOWER_RTS", 0);
+		local_osr &= ~ICOM_RTS;
+	}
+
+	if (mctrl & TIOCM_DTR) {
+		trace(ICOM_PORT, "RAISE_DTR", 0);
+		local_osr |= ICOM_DTR;
+	} else {
+		trace(ICOM_PORT, "LOWER_DTR", 0);
+		local_osr &= ~ICOM_DTR;
+	}
+
+	writeb(local_osr, &ICOM_PORT->dram->osr);
+}
+
+static unsigned int icom_get_mctrl(struct uart_port *port)
+{
+	unsigned char status;
+	unsigned int result;
+
+	trace(ICOM_PORT, "GET_MODEM", 0);
+
+	status = readb(&ICOM_PORT->dram->isr);
+
+	result = ((status & ICOM_DCD) ? TIOCM_CAR : 0)
+	    | ((status & ICOM_RI) ? TIOCM_RNG : 0)
+	    | ((status & ICOM_DSR) ? TIOCM_DSR : 0)
+	    | ((status & ICOM_CTS) ? TIOCM_CTS : 0);
+	return result;
+}
+
+static void icom_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+	unsigned char cmdReg;
+
+	if (tty_stop) {
+		trace(ICOM_PORT, "STOP", 0);
+		cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+		writeb(cmdReg | CMD_HOLD_XMIT, &ICOM_PORT->dram->CmdReg);
+	}
+}
+
+static void icom_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+	unsigned char cmdReg;
+
+	trace(ICOM_PORT, "START", 0);
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	if ((cmdReg & CMD_HOLD_XMIT) == CMD_HOLD_XMIT)
+		writeb(cmdReg & ~CMD_HOLD_XMIT,
+		       &ICOM_PORT->dram->CmdReg);
+
+	icom_write(port);
+}
+
+static void icom_send_xchar(struct uart_port *port, char ch)
+{
+	unsigned char xdata;
+	int index;
+	unsigned long flags;
+
+	trace(ICOM_PORT, "SEND_XCHAR", ch);
+
+	/* wait .1 sec to send char */
+	for (index = 0; index < 10; index++) {
+		spin_lock_irqsave(&port->lock, flags);
+		xdata = readb(&ICOM_PORT->dram->xchar);
+		if (xdata == 0x00) {
+			trace(ICOM_PORT, "QUICK_WRITE", 0);
+			writeb(ch, &ICOM_PORT->dram->xchar);
+
+			/* flush write operation */
+			xdata = readb(&ICOM_PORT->dram->xchar);
+			spin_unlock_irqrestore(&port->lock, flags);
+			break;
+		}
+		spin_unlock_irqrestore(&port->lock, flags);
+		msleep(10);
+	}
+}
+
+static void icom_stop_rx(struct uart_port *port)
+{
+	unsigned char cmdReg;
+
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg);
+}
+
+static void icom_enable_ms(struct uart_port *port)
+{
+	/* no-op */
+}
+
+static void icom_break(struct uart_port *port, int break_state)
+{
+	unsigned char cmdReg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	trace(ICOM_PORT, "BREAK", 0);
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	if (break_state == -1) {
+		writeb(cmdReg | CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg);
+	} else {
+		writeb(cmdReg & ~CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int icom_open(struct uart_port *port)
+{
+	int retval;
+
+	kobject_get(&ICOM_PORT->adapter->kobj);
+	retval = startup(ICOM_PORT);
+
+	if (retval) {
+		kobject_put(&ICOM_PORT->adapter->kobj);
+		trace(ICOM_PORT, "STARTUP_ERROR", 0);
+		return retval;
+	}
+
+	return 0;
+}
+
+static void icom_close(struct uart_port *port)
+{
+	unsigned char cmdReg;
+
+	trace(ICOM_PORT, "CLOSE", 0);
+
+	/* stop receiver */
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE,
+	       &ICOM_PORT->dram->CmdReg);
+
+	shutdown(ICOM_PORT);
+
+	kobject_put(&ICOM_PORT->adapter->kobj);
+}
+
+static void icom_set_termios(struct uart_port *port,
+			     struct termios *termios,
+			     struct termios *old_termios)
+{
+	int baud;
+	unsigned cflag, iflag;
+	int bits;
+	char new_config2;
+	char new_config3 = 0;
+	char tmp_byte;
+	int index;
+	int rcv_buff, xmit_buff;
+	unsigned long offset;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	trace(ICOM_PORT, "CHANGE_SPEED", 0);
+
+	cflag = termios->c_cflag;
+	iflag = termios->c_iflag;
+
+	new_config2 = ICOM_ACFG_DRIVE1;
+
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	case CS5:		/* 5 bits/char */
+		new_config2 |= ICOM_ACFG_5BPC;
+		bits = 7;
+		break;
+	case CS6:		/* 6 bits/char */
+		new_config2 |= ICOM_ACFG_6BPC;
+		bits = 8;
+		break;
+	case CS7:		/* 7 bits/char */
+		new_config2 |= ICOM_ACFG_7BPC;
+		bits = 9;
+		break;
+	case CS8:		/* 8 bits/char */
+		new_config2 |= ICOM_ACFG_8BPC;
+		bits = 10;
+		break;
+	default:
+		bits = 10;
+		break;
+	}
+	if (cflag & CSTOPB) {
+		/* 2 stop bits */
+		new_config2 |= ICOM_ACFG_2STOP_BIT;
+		bits++;
+	}
+	if (cflag & PARENB) {
+		/* parity bit enabled */
+		new_config2 |= ICOM_ACFG_PARITY_ENAB;
+		trace(ICOM_PORT, "PARENB", 0);
+		bits++;
+	}
+	if (cflag & PARODD) {
+		/* odd parity */
+		new_config2 |= ICOM_ACFG_PARITY_ODD;
+		trace(ICOM_PORT, "PARODD", 0);
+	}
+
+	/* Determine divisor based on baud rate */
+	baud = uart_get_baud_rate(port, termios, old_termios,
+				  icom_acfg_baud[0],
+				  icom_acfg_baud[BAUD_TABLE_LIMIT]);
+	if (!baud)
+		baud = 9600;	/* B0 transition handled in rs_set_termios */
+
+	for (index = 0; index < BAUD_TABLE_LIMIT; index++) {
+		if (icom_acfg_baud[index] == baud) {
+			new_config3 = index;
+			break;
+		}
+	}
+
+	uart_update_timeout(port, cflag, baud);
+
+	/* CTS flow control flag and modem status interrupts */
+	tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg));
+	if (cflag & CRTSCTS)
+		tmp_byte |= HDLC_HDW_FLOW;
+	else
+		tmp_byte &= ~HDLC_HDW_FLOW;
+	writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg));
+
+	/*
+	 * Set up parity check flag
+	 */
+	ICOM_PORT->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE;
+	if (iflag & INPCK)
+		ICOM_PORT->read_status_mask |=
+		    SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR;
+
+	if ((iflag & BRKINT) || (iflag & PARMRK))
+		ICOM_PORT->read_status_mask |= SA_FLAGS_BREAK_DET;
+
+	/*
+	 * Characters to ignore
+	 */
+	ICOM_PORT->ignore_status_mask = 0;
+	if (iflag & IGNPAR)
+		ICOM_PORT->ignore_status_mask |=
+		    SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR;
+	if (iflag & IGNBRK) {
+		ICOM_PORT->ignore_status_mask |= SA_FLAGS_BREAK_DET;
+		/*
+		 * If we're ignore parity and break indicators, ignore
+		 * overruns too.  (For real raw support).
+		 */
+		if (iflag & IGNPAR)
+			ICOM_PORT->ignore_status_mask |= SA_FLAGS_OVERRUN;
+	}
+
+	/*
+	 * !!! ignore all characters if CREAD is not set
+	 */
+	if ((cflag & CREAD) == 0)
+		ICOM_PORT->ignore_status_mask |= SA_FL_RCV_DONE;
+
+	/* Turn off Receiver to prepare for reset */
+	writeb(CMD_RCV_DISABLE, &ICOM_PORT->dram->CmdReg);
+
+	for (index = 0; index < 10; index++) {
+		if (readb(&ICOM_PORT->dram->PrevCmdReg) == 0x00) {
+			break;
+		}
+	}
+
+	/* clear all current buffers of data */
+	for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) {
+		ICOM_PORT->statStg->rcv[rcv_buff].flags = 0;
+		ICOM_PORT->statStg->rcv[rcv_buff].leLength = 0;
+		ICOM_PORT->statStg->rcv[rcv_buff].WorkingLength =
+		    (unsigned short int) cpu_to_le16(RCV_BUFF_SZ);
+	}
+
+	for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) {
+		ICOM_PORT->statStg->xmit[xmit_buff].flags = 0;
+	}
+
+	/* activate changes and start xmit and receiver here */
+	/* Enable the receiver */
+	writeb(new_config3, &(ICOM_PORT->dram->async_config3));
+	writeb(new_config2, &(ICOM_PORT->dram->async_config2));
+	tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg));
+	tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL;
+	writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg));
+	writeb(0x04, &(ICOM_PORT->dram->FlagFillIdleTimer));	/* 0.5 seconds */
+	writeb(0xFF, &(ICOM_PORT->dram->ier));	/* enable modem signal interrupts */
+
+	/* reset processor */
+	writeb(CMD_RESTART, &ICOM_PORT->dram->CmdReg);
+
+	for (index = 0; index < 10; index++) {
+		if (readb(&ICOM_PORT->dram->CmdReg) == 0x00) {
+			break;
+		}
+	}
+
+	/* Enable Transmitter and Reciever */
+	offset =
+	    (unsigned long) &ICOM_PORT->statStg->rcv[0] -
+	    (unsigned long) ICOM_PORT->statStg;
+	writel(ICOM_PORT->statStg_pci + offset,
+	       &ICOM_PORT->dram->RcvStatusAddr);
+	ICOM_PORT->next_rcv = 0;
+	ICOM_PORT->put_length = 0;
+	*ICOM_PORT->xmitRestart = 0;
+	writel(ICOM_PORT->xmitRestart_pci,
+	       &ICOM_PORT->dram->XmitStatusAddr);
+	trace(ICOM_PORT, "XR_ENAB", 0);
+	writeb(CMD_XMIT_RCV_ENABLE, &ICOM_PORT->dram->CmdReg);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *icom_type(struct uart_port *port)
+{
+	return "icom";
+}
+
+static void icom_release_port(struct uart_port *port)
+{
+}
+
+static int icom_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void icom_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_ICOM;
+}
+
+static struct uart_ops icom_ops = {
+	.tx_empty = icom_tx_empty,
+	.set_mctrl = icom_set_mctrl,
+	.get_mctrl = icom_get_mctrl,
+	.stop_tx = icom_stop_tx,
+	.start_tx = icom_start_tx,
+	.send_xchar = icom_send_xchar,
+	.stop_rx = icom_stop_rx,
+	.enable_ms = icom_enable_ms,
+	.break_ctl = icom_break,
+	.startup = icom_open,
+	.shutdown = icom_close,
+	.set_termios = icom_set_termios,
+	.type = icom_type,
+	.release_port = icom_release_port,
+	.request_port = icom_request_port,
+	.config_port = icom_config_port,
+};
+
+#define ICOM_CONSOLE NULL
+
+static struct uart_driver icom_uart_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = ICOM_DRIVER_NAME,
+	.dev_name = "ttyA",
+	.major = ICOM_MAJOR,
+	.minor = ICOM_MINOR_START,
+	.nr = NR_PORTS,
+	.cons = ICOM_CONSOLE,
+};
+
+static int __devinit icom_init_ports(struct icom_adapter *icom_adapter)
+{
+	u32 subsystem_id = icom_adapter->subsystem_id;
+	int retval = 0;
+	int i;
+	struct icom_port *icom_port;
+
+	if (icom_adapter->version == ADAPTER_V1) {
+		icom_adapter->numb_ports = 2;
+
+		for (i = 0; i < 2; i++) {
+			icom_port = &icom_adapter->port_info[i];
+			icom_port->port = i;
+			icom_port->status = ICOM_PORT_ACTIVE;
+			icom_port->imbed_modem = ICOM_UNKNOWN;
+		}
+	} else {
+		if (subsystem_id == PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL) {
+			icom_adapter->numb_ports = 4;
+
+			for (i = 0; i < 4; i++) {
+				icom_port = &icom_adapter->port_info[i];
+
+				icom_port->port = i;
+				icom_port->status = ICOM_PORT_ACTIVE;
+				icom_port->imbed_modem = ICOM_IMBED_MODEM;
+			}
+		} else {
+			icom_adapter->numb_ports = 4;
+
+			icom_adapter->port_info[0].port = 0;
+			icom_adapter->port_info[0].status = ICOM_PORT_ACTIVE;
+
+			if (subsystem_id ==
+			    PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM) {
+				icom_adapter->port_info[0].imbed_modem = ICOM_IMBED_MODEM;
+			} else {
+				icom_adapter->port_info[0].imbed_modem = ICOM_RVX;
+			}
+
+			icom_adapter->port_info[1].status = ICOM_PORT_OFF;
+
+			icom_adapter->port_info[2].port = 2;
+			icom_adapter->port_info[2].status = ICOM_PORT_ACTIVE;
+			icom_adapter->port_info[2].imbed_modem = ICOM_RVX;
+			icom_adapter->port_info[3].status = ICOM_PORT_OFF;
+		}
+	}
+
+	return retval;
+}
+
+static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *icom_adapter, int port_num)
+{
+	if (icom_adapter->version == ADAPTER_V1) {
+		icom_port->global_reg = icom_adapter->base_addr + 0x4000;
+		icom_port->int_reg = icom_adapter->base_addr +
+		    0x4004 + 2 - 2 * port_num;
+	} else {
+		icom_port->global_reg = icom_adapter->base_addr + 0x8000;
+		if (icom_port->port < 2)
+			icom_port->int_reg = icom_adapter->base_addr +
+			    0x8004 + 2 - 2 * icom_port->port;
+		else
+			icom_port->int_reg = icom_adapter->base_addr +
+			    0x8024 + 2 - 2 * (icom_port->port - 2);
+	}
+}
+static int __init icom_load_ports(struct icom_adapter *icom_adapter)
+{
+	struct icom_port *icom_port;
+	int port_num;
+	int retval;
+
+	for (port_num = 0; port_num < icom_adapter->numb_ports; port_num++) {
+
+		icom_port = &icom_adapter->port_info[port_num];
+
+		if (icom_port->status == ICOM_PORT_ACTIVE) {
+			icom_port_active(icom_port, icom_adapter, port_num);
+			icom_port->dram = icom_adapter->base_addr +
+					0x2000 * icom_port->port;
+
+			icom_port->adapter = icom_adapter;
+
+			/* get port memory */
+			if ((retval = get_port_memory(icom_port)) != 0) {
+				dev_err(&icom_port->adapter->pci_dev->dev,
+					"Memory allocation for port FAILED\n");
+			}
+		}
+	}
+	return 0;
+}
+
+static int __devinit icom_alloc_adapter(struct icom_adapter
+					**icom_adapter_ref)
+{
+	int adapter_count = 0;
+	struct icom_adapter *icom_adapter;
+	struct icom_adapter *cur_adapter_entry;
+	struct list_head *tmp;
+
+	icom_adapter = (struct icom_adapter *)
+	    kmalloc(sizeof(struct icom_adapter), GFP_KERNEL);
+
+	if (!icom_adapter) {
+		return -ENOMEM;
+	}
+
+	memset(icom_adapter, 0, sizeof(struct icom_adapter));
+
+	list_for_each(tmp, &icom_adapter_head) {
+		cur_adapter_entry =
+		    list_entry(tmp, struct icom_adapter,
+			       icom_adapter_entry);
+		if (cur_adapter_entry->index != adapter_count) {
+			break;
+		}
+		adapter_count++;
+	}
+
+	icom_adapter->index = adapter_count;
+	list_add_tail(&icom_adapter->icom_adapter_entry, tmp);
+
+	*icom_adapter_ref = icom_adapter;
+	return 0;
+}
+
+static void icom_free_adapter(struct icom_adapter *icom_adapter)
+{
+	list_del(&icom_adapter->icom_adapter_entry);
+	kfree(icom_adapter);
+}
+
+static void icom_remove_adapter(struct icom_adapter *icom_adapter)
+{
+	struct icom_port *icom_port;
+	int index;
+
+	for (index = 0; index < icom_adapter->numb_ports; index++) {
+		icom_port = &icom_adapter->port_info[index];
+
+		if (icom_port->status == ICOM_PORT_ACTIVE) {
+			dev_info(&icom_adapter->pci_dev->dev,
+				 "Device removed\n");
+
+			uart_remove_one_port(&icom_uart_driver,
+					     &icom_port->uart_port);
+
+			/* be sure that DTR and RTS are dropped */
+			writeb(0x00, &icom_port->dram->osr);
+
+			/* Wait 0.1 Sec for simple Init to complete */
+			msleep(100);
+
+			/* Stop proccessor */
+			stop_processor(icom_port);
+
+			free_port_memory(icom_port);
+		}
+	}
+
+	free_irq(icom_adapter->irq_number, (void *) icom_adapter);
+	iounmap(icom_adapter->base_addr);
+	icom_free_adapter(icom_adapter);
+	pci_release_regions(icom_adapter->pci_dev);
+}
+
+static void icom_kobj_release(struct kobject *kobj)
+{
+	struct icom_adapter *icom_adapter;
+
+	icom_adapter = to_icom_adapter(kobj);
+	icom_remove_adapter(icom_adapter);
+}
+
+static struct kobj_type icom_kobj_type = {
+	.release = icom_kobj_release,
+};
+
+static int __devinit icom_probe(struct pci_dev *dev,
+				const struct pci_device_id *ent)
+{
+	int index;
+        unsigned int command_reg;
+        int retval;
+        struct icom_adapter *icom_adapter;
+        struct icom_port *icom_port;
+
+        retval = pci_enable_device(dev);
+        if (retval) {
+		dev_err(&dev->dev, "Device enable FAILED\n");
+                return retval;
+	}
+
+	if ( (retval = pci_request_regions(dev, "icom"))) {
+		 dev_err(&dev->dev, "pci_request_region FAILED\n");
+		 pci_disable_device(dev);
+		 return retval;
+	 }
+
+        pci_set_master(dev);
+
+        if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) {
+		dev_err(&dev->dev, "PCI Config read FAILED\n");
+                return retval;
+        }
+
+	pci_write_config_dword(dev, PCI_COMMAND,
+		command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
+ 		| PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+
+        if (ent->driver_data == ADAPTER_V1) {
+		pci_write_config_dword(dev, 0x44, 0x8300830A);
+	 } else {
+		pci_write_config_dword(dev, 0x44, 0x42004200);
+		pci_write_config_dword(dev, 0x48, 0x42004200);
+         }
+
+
+	retval = icom_alloc_adapter(&icom_adapter);
+	if (retval) {
+		 dev_err(&dev->dev, "icom_alloc_adapter FAILED\n");
+		 retval = -EIO;
+		 goto probe_exit0;
+	}
+
+	 icom_adapter->base_addr_pci = pci_resource_start(dev, 0);
+	 icom_adapter->irq_number = dev->irq;
+	 icom_adapter->pci_dev = dev;
+	 icom_adapter->version = ent->driver_data;
+	 icom_adapter->subsystem_id = ent->subdevice;
+
+
+	retval = icom_init_ports(icom_adapter);
+	if (retval) {
+		dev_err(&dev->dev, "Port configuration failed\n");
+		goto probe_exit1;
+	}
+
+	 icom_adapter->base_addr = ioremap(icom_adapter->base_addr_pci,
+						pci_resource_len(dev, 0));
+
+	if (!icom_adapter->base_addr)
+		goto probe_exit1;
+
+	 /* save off irq and request irq line */
+	 if ( (retval = request_irq(dev->irq, icom_interrupt,
+				   SA_INTERRUPT | SA_SHIRQ, ICOM_DRIVER_NAME,
+				   (void *) icom_adapter))) {
+		  goto probe_exit2;
+	 }
+
+	retval = icom_load_ports(icom_adapter);
+
+        for (index = 0; index < icom_adapter->numb_ports; index++) {
+		icom_port = &icom_adapter->port_info[index];
+
+		if (icom_port->status == ICOM_PORT_ACTIVE) {
+			icom_port->uart_port.irq = icom_port->adapter->irq_number;
+			icom_port->uart_port.type = PORT_ICOM;
+			icom_port->uart_port.iotype = UPIO_MEM;
+			icom_port->uart_port.membase =
+					       (char *) icom_adapter->base_addr_pci;
+			icom_port->uart_port.fifosize = 16;
+			icom_port->uart_port.ops = &icom_ops;
+			icom_port->uart_port.line =
+		        icom_port->port + icom_adapter->index * 4;
+			if (uart_add_one_port (&icom_uart_driver, &icom_port->uart_port)) {
+				icom_port->status = ICOM_PORT_OFF;
+				dev_err(&dev->dev, "Device add failed\n");
+			 } else
+			        dev_info(&dev->dev, "Device added\n");
+		}
+	}
+
+	kobject_init(&icom_adapter->kobj);
+	icom_adapter->kobj.ktype = &icom_kobj_type;
+	return 0;
+
+probe_exit2:
+	iounmap(icom_adapter->base_addr);
+probe_exit1:
+	icom_free_adapter(icom_adapter);
+
+probe_exit0:
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+
+        return retval;
+
+
+}
+
+static void __devexit icom_remove(struct pci_dev *dev)
+{
+	struct icom_adapter *icom_adapter;
+	struct list_head *tmp;
+
+	list_for_each(tmp, &icom_adapter_head) {
+		icom_adapter = list_entry(tmp, struct icom_adapter,
+					  icom_adapter_entry);
+		if (icom_adapter->pci_dev == dev) {
+			kobject_put(&icom_adapter->kobj);
+			return;
+		}
+	}
+
+	dev_err(&dev->dev, "Unable to find device to remove\n");
+}
+
+static struct pci_driver icom_pci_driver = {
+	.name = ICOM_DRIVER_NAME,
+	.id_table = icom_pci_table,
+	.probe = icom_probe,
+	.remove = __devexit_p(icom_remove),
+};
+
+static int __init icom_init(void)
+{
+	int ret;
+
+	spin_lock_init(&icom_lock);
+
+	ret = uart_register_driver(&icom_uart_driver);
+	if (ret)
+		return ret;
+
+	ret = pci_register_driver(&icom_pci_driver);
+
+	if (ret < 0)
+		uart_unregister_driver(&icom_uart_driver);
+
+	return ret;
+}
+
+static void __exit icom_exit(void)
+{
+	pci_unregister_driver(&icom_pci_driver);
+	uart_unregister_driver(&icom_uart_driver);
+}
+
+module_init(icom_init);
+module_exit(icom_exit);
+
+#ifdef ICOM_TRACE
+static inline void trace(struct icom_port *icom_port, char *trace_pt,
+		  unsigned long trace_data)
+{
+	dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n",
+		 icom_port->port, trace_pt, trace_data);
+}
+#endif
+
+MODULE_AUTHOR("Michael Anderson <mjanders@us.ibm.com>");
+MODULE_DESCRIPTION("IBM iSeries Serial IOA driver");
+MODULE_SUPPORTED_DEVICE
+    ("IBM iSeries 2745, 2771, 2772, 2742, 2793 and 2805 Communications adapters");
+MODULE_LICENSE("GPL");
+