public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Jeff Garzik <jgarzik@pobox.com>
To: Janice M Girouard <janiceg@us.ibm.com>
Cc: linux-kernel@vger.kernel.org, wenxiong@us.ibm.com,
	linux-serial@vger.kernel.org,
	Russell King <rmk+lkml@arm.linux.org.uk>
Subject: Re: new device driver to enable the IBM Multiport Serial Adapter in Linux
Date: Tue, 27 Jul 2004 11:22:35 -0400	[thread overview]
Message-ID: <4106733B.3080108@pobox.com> (raw)
In-Reply-To: <41055722.2070104@us.ibm.com>


> +#define ICOM_DRIVER_NAME "icom"
> +#define ICOM_VERSION_STR "1.3.1"
> +#define ICOM_DEV_ID_1    0x0031
> +#define ICOM_DEV_ID_2    0x0219
> +#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 = ICOM_DEV_ID_1,
> +	      .subvendor = PCI_ANY_ID,
> +	      .subdevice = PCI_ANY_ID,
> +	      .driver_data = ADAPTER_V1,
> +	 },
> +	{
> +	      .vendor = PCI_VENDOR_ID_IBM,
> +	      .device = ICOM_DEV_ID_2,
> +	      .subvendor = PCI_VENDOR_ID_IBM,
> +	      .subdevice = V2_TWO_PORTS_RVX,
> +	      .driver_data = ADAPTER_V2,
> +	 },
> +	{
> +	      .vendor = PCI_VENDOR_ID_IBM,
> +	      .device = ICOM_DEV_ID_2,
> +	      .subvendor = PCI_VENDOR_ID_IBM,
> +	      .subdevice = V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM,
> +	      .driver_data = ADAPTER_V2,
> +	 },
> +	{
> +	      .vendor = PCI_VENDOR_ID_IBM,
> +	      .device = ICOM_DEV_ID_2,
> +	      .subvendor = PCI_VENDOR_ID_IBM,
> +	      .subdevice = FOUR_PORT_MODEL,
> +	      .driver_data = ADAPTER_V2,
> +	 },
> +	{}
> +};

Either use (or create) standard PCI_DEVICE_ID_IBM_xxx constants for the 
->device and ->subdevice ids, or do not use constants at all.


> +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;

initialize with SPIN_LOCK_UNLOCKED perhaps?


> +#ifdef ICOM_TRACE
> +static void trace(struct icom_port *, char *, unsigned long);
> +#else
> +static void trace(struct icom_port *port, char *trace_pt, unsigned long data)
> +{
> +};

inline the no-op


> +#endif
> +
> +static void return_port_memory(struct icom_port *icom_port)

nit:  use of the word "free" is __far__ more common than "return".


> +{
> +	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 = 0;
> +	}
> +	if (icom_port->xmit_buf) {
> +		pci_free_consistent(dev, 4096, icom_port->xmit_buf,
> +				    icom_port->xmit_buf_pci);
> +		icom_port->xmit_buf = 0;
> +	}
> +	if (icom_port->statStg) {
> +		pci_free_consistent(dev, 4096, icom_port->statStg,
> +				    icom_port->statStg_pci);
> +		icom_port->statStg = 0;
> +	}
> +
> +	if (icom_port->xmitRestart) {
> +		pci_free_consistent(dev, 4096, icom_port->xmitRestart,
> +				    icom_port->xmitRestart_pci);
> +		icom_port->xmitRestart = 0;
> +	}
> +}
> +
> +static int __init get_port_memory(struct icom_port *icom_port)
> +{
> +	int index;
> +	int number_of_buffs;
> +	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");
> +		return_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");
> +		return_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");
> +		return_port_memory(icom_port);
> +		return -ENOMEM;
> +	}
> +
> +	memset(icom_port->statStg, 0, 4096);
> +
> +	/* FODs */

and who except the author knows what an 'FOD' is?


> +	number_of_buffs = NUM_XBUFFS;

pointless variable.  just use the constant.


> +	stgAddr = (unsigned long) icom_port->statStg;
> +	startStgAddr = stgAddr;

dead store


> +	for (index = 0; index < number_of_buffs; index++) {
> +		trace(icom_port, "FOD_ADDR", stgAddr);
> +		stgAddr = stgAddr + sizeof(icom_port->statStg->xmit[0]);
> +		if (index < (number_of_buffs - 1)) {
> +			icom_port->statStg->xmit[index].flags = 0;
> +			icom_port->statStg->xmit[index].leNext = 0;
> +			icom_port->statStg->xmit[index].leNextASD = 0;
> +			icom_port->statStg->xmit[index].leLengthASD =
> +			    (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ);
> +			icom_port->statStg->xmit[index].leOffsetASD = 0;
> +			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 == (number_of_buffs - 1)) {
> +			icom_port->statStg->xmit[index].flags = 0;
> +			icom_port->statStg->xmit[index].leNext = 0;
> +			icom_port->statStg->xmit[index].leNextASD = 0;
> +			icom_port->statStg->xmit[index].leLengthASD =
> +			    (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ);
> +			icom_port->statStg->xmit[index].leOffsetASD = 0;
> +			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 {
> +			icom_port->statStg->xmit[index].flags = 0;
> +			icom_port->statStg->xmit[index].leNext = 0;
> +			icom_port->statStg->xmit[index].leNextASD = 0;
> +			icom_port->statStg->xmit[index].leLengthASD = 0;
> +			icom_port->statStg->xmit[index].leOffsetASD = 0;
> +			icom_port->statStg->xmit[index].leBuffer = 0;
> +		}
> +	}

just memset, no need to zero explicitly after that



> +	/* FIDs */
> +	startStgAddr = stgAddr;
> +
> +	/* fill in every entry, even if no buffer */
> +	number_of_buffs = NUM_RBUFFS;

pointless variable.  just use constant.


> +	for (index = 0; index < number_of_buffs; 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 < (number_of_buffs - 1)) {
> +			offset =
> +			    stgAddr - (unsigned long) icom_port->statStg;

overzealous indent(1) wrapping...

> +			icom_port->statStg->rcv[index].leNext =
> +			    (unsigned long) cpu_to_le32(icom_port->
> +							statStg_pci +
> +							offset);

BUG:  leNext is a u32.  don't cast u32->unsigned long->u32.  it's dumb, 
particularly on 64-bit boxes.


> +			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 == (number_of_buffs - 1)) {
> +			offset =
> +			    startStgAddr -
> +			    (unsigned long) icom_port->statStg;
> +			icom_port->statStg->rcv[index].leNext =
> +			    (unsigned long) cpu_to_le32(icom_port->
> +							statStg_pci +
> +							offset);

ditto


> +			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;
> +
> +	spin_lock_irqsave(&icom_lock, flags);
> +
> +	switch (icom_port->port) {
> +	case 0:
> +		temp = readl(&icom_port->global_reg->control);
> +		temp =
> +		    (temp & ~ICOM_CONTROL_START_A) | ICOM_CONTROL_STOP_A;
> +		writel(temp, &icom_port->global_reg->control);
> +		trace(icom_port, "STOP_PROC_A", 0);
> +		break;
> +	case 1:
> +		temp = readl(&icom_port->global_reg->control);
> +		temp =
> +		    (temp & ~ICOM_CONTROL_START_B) | ICOM_CONTROL_STOP_B;
> +		writel(temp, &icom_port->global_reg->control);
> +		trace(icom_port, "STOP_PROC_B", 0);
> +		break;
> +	case 2:
> +		temp = readl(&icom_port->global_reg->control_2);
> +		temp =
> +		    (temp & ~ICOM_CONTROL_START_C) | ICOM_CONTROL_STOP_C;
> +		writel(temp, &icom_port->global_reg->control_2);
> +		trace(icom_port, "STOP_PROC_C", 0);
> +		break;
> +	case 3:
> +		temp = readl(&icom_port->global_reg->control_2);
> +		temp =
> +		    (temp & ~ICOM_CONTROL_START_D) | ICOM_CONTROL_STOP_D;
> +		writel(temp, &icom_port->global_reg->control_2);
> +		trace(icom_port, "STOP_PROC_D", 0);
> +		break;
> +	default:
> +		dev_err(&icom_port->adapter->pci_dev->dev,
> +			"Invalid port assignment\n");
> +	}

you could shorten this function by using a lookup table.

lookup table is preferred for clarity generally, when speed is not an 
issue (as it is not here).

also:  MMIO posting flush


> +	spin_unlock_irqrestore(&icom_lock, flags);
> +}
> +
> +static void start_processor(struct icom_port *icom_port)
> +{
> +	unsigned long temp;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&icom_lock, flags);
> +
> +	switch (icom_port->port) {
> +	case 0:
> +		temp = readl(&icom_port->global_reg->control);
> +		temp =
> +		    (temp & ~ICOM_CONTROL_STOP_A) | ICOM_CONTROL_START_A;
> +		writel(temp, &icom_port->global_reg->control);
> +		trace(icom_port, "START_PROC_A", 0);
> +		break;
> +	case 1:
> +		temp = readl(&icom_port->global_reg->control);
> +		temp =
> +		    (temp & ~ICOM_CONTROL_STOP_B) | ICOM_CONTROL_START_B;
> +		writel(temp, &icom_port->global_reg->control);
> +		trace(icom_port, "START_PROC_B", 0);
> +		break;
> +	case 2:
> +		temp = readl(&icom_port->global_reg->control_2);
> +		temp =
> +		    (temp & ~ICOM_CONTROL_STOP_C) | ICOM_CONTROL_START_C;
> +		writel(temp, &icom_port->global_reg->control_2);
> +		trace(icom_port, "START_PROC_C", 0);
> +		break;
> +	case 3:
> +		temp = readl(&icom_port->global_reg->control_2);
> +		temp =
> +		    (temp & ~ICOM_CONTROL_STOP_D) | ICOM_CONTROL_START_D;
> +		writel(temp, &icom_port->global_reg->control_2);
> +		trace(icom_port, "START_PROC_D", 0);
> +		break;
> +	default:
> +		dev_err(&icom_port->adapter->pci_dev->dev,
> +			"Invalid port assignment\n");
> +	}

same comments as above


> +	spin_unlock_irqrestore(&icom_lock, flags);
> +}
> +
> +static void load_code(struct icom_port *icom_port)
> +{
> +	const struct firmware *fw;
> +	char *iram_ptr;
> +	int index;
> +	int status = 0;
> +	char *dram_ptr = (char *) 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, (void *) icom_port->int_reg);

posting

> +	trace(icom_port, "CLEAR_INTERRUPTS", 0);
> +
> +	/* Stop processor */
> +	stop_processor(icom_port);
> +
> +	/* Zero out DRAM */
> +	for (index = 0; index < 512; index++)
> +		writeb(0x00, &dram_ptr[index]);

memset_io() instead?



> +	/* 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 *) 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 *) 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.
> +	 */
> +	writeb(START_DOWNLOAD, &icom_port->dram->sync);

PCI posting


> +	/* Wait max 1 Sec for data download and processor to start */
> +	for (index = 0; index < 10; index++) {
> +		set_current_state(TASK_UNINTERRUPTIBLE);
> +		schedule_timeout(HZ / 10);

use msleep()

> +		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, (void *) 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;
> +
> +	trace(icom_port, "STARTUP", 0);
> +
> +	if (icom_port->dram == 0x00000000) {
> +		/* should NEVER be zero */
> +		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);
> +	switch (icom_port->port) {
> +	case 0:
> +		/* Clear out any pending interrupts */
> +		writew(0x00FF, (void *) icom_port->int_reg);
> +
> +		/* Enable interrupts for first port */
> +		trace(icom_port, "ENABLE_INTERRUPTS_PA", 0);
> +		temp = readl(&icom_port->global_reg->int_mask);
> +		writel((temp & ~ICOM_INT_MASK_PRC_A),
> +		       &icom_port->global_reg->int_mask);
> +		break;
> +	case 1:
> +		/* Clear out any pending interrupts */
> +		writew(0x3F00, (void *) icom_port->int_reg);
> +
> +		/* Enable interrupts for second port */
> +		trace(icom_port, "ENABLE_INTERRUPTS_PB", 0);
> +		temp = readl(&icom_port->global_reg->int_mask);
> +		writel((temp & ~ICOM_INT_MASK_PRC_B),
> +		       &icom_port->global_reg->int_mask);
> +		break;
> +	case 2:
> +		/* Clear out any pending interrupts */
> +		writew(0x00FF, (void *) icom_port->int_reg);
> +
> +		/* Enable interrupts for first port */
> +		trace(icom_port, "ENABLE_INTERRUPTS_PC", 0);
> +		temp = readl(&icom_port->global_reg->int_mask_2);
> +		writel((temp & ~ICOM_INT_MASK_PRC_C),
> +		       &icom_port->global_reg->int_mask_2);
> +		break;
> +	case 3:
> +		/* Clear out any pending interrupts */
> +		writew(0x3F00, (void *) icom_port->int_reg);
> +
> +		/* Enable interrupts for second port */
> +		trace(icom_port, "ENABLE_INTERRUPTS_PD", 0);
> +		temp = readl(&icom_port->global_reg->int_mask_2);
> +		writel((temp & ~ICOM_INT_MASK_PRC_D),
> +		       &icom_port->global_reg->int_mask_2);
> +		break;
> +	default:
> +		dev_err(&icom_port->adapter->pci_dev->dev,
> +			"Invalid port defined\n");
> +	}

lookup table, PCI posting


> +	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;
> +
> +	spin_lock_irqsave(&icom_lock, flags);
> +	trace(icom_port, "SHUTDOWN", 0);
> +
> +	/*
> +	 * disable all interrupts
> +	 */
> +	switch (icom_port->port) {
> +	case 0:
> +		trace(icom_port, "DIS_INTERRUPTS_PA", 0);
> +		temp = readl(&icom_port->global_reg->int_mask);
> +		writel((temp | ICOM_INT_MASK_PRC_A),
> +		       &icom_port->global_reg->int_mask);
> +		break;
> +	case 1:
> +		trace(icom_port, "DIS_INTERRUPTS_PB", 0);
> +		temp = readl(&icom_port->global_reg->int_mask);
> +		writel((temp | ICOM_INT_MASK_PRC_B),
> +		       &icom_port->global_reg->int_mask);
> +		break;
> +	case 2:
> +		trace(icom_port, "DIS_INTERRUPTS_PC", 0);
> +		temp = readl(&icom_port->global_reg->int_mask_2);
> +		writel((temp | ICOM_INT_MASK_PRC_C),
> +		       &icom_port->global_reg->int_mask_2);
> +		break;
> +	case 3:
> +		trace(icom_port, "DIS_INTERRUPTS_PD", 0);
> +		temp = readl(&icom_port->global_reg->int_mask_2);
> +		writel((temp | ICOM_INT_MASK_PRC_D),
> +		       &icom_port->global_reg->int_mask_2);
> +		break;

lookup table, PCI posting


> +	default:
> +		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 = (unsigned short int)
> +		    cpu_to_le16(SA_FLAGS_READY_TO_XMIT);

useless cast.  flags is defined as u16 not unsigned short int, as well.


> +		ICOM_PORT->statStg->xmit[0].leLength =
> +		    (unsigned short int) cpu_to_le16(data_count);

useless cast

> +		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);
> +	}

PCI posting

> +	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;
> +	unsigned long flags;

this function is used entirely within the interrupt handler.

don't bother with spin_lock_irqsave(), just use spin_lock()


> +	spin_lock_irqsave(&icom_port->uart_port.lock, flags);
> +
> +	/*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_irqrestore(&icom_port->uart_port.lock, flags);
> +}
> +
> +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)
> +{
> +	unsigned long flags;

this is used 100% within the interrupt handler, so no need for 
spin_lock_irqsave().  spin_lock() is sufficient


> +	spin_lock_irqsave(&icom_port->uart_port.lock, flags);
> +	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_irqrestore(&icom_port->uart_port.lock, flags);
> +}
> +
> +static irqreturn_t icom_interrupt(int irq, void *dev_id,
> +				  struct pt_regs *regs)
> +{
> +	unsigned long 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((void *) 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, (void *) int_reg);
> +
> +		int_reg = icom_adapter->base_addr + 0x8004;
> +	} else {
> +		int_reg = icom_adapter->base_addr + 0x4004;
> +	}
> +
> +	adapter_interrupts = readl((void *) 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, (void *) int_reg);
> +	/* flush the write */
> +	adapter_interrupts = readl((void *) 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);
> +		set_current_state(TASK_UNINTERRUPTIBLE);
> +		schedule_timeout(HZ / 100);

msleep()

> +	}
> +}
> +
> +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 */
> +}

We should change serial_core.c to conditionally call this hook.


> +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 == 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 ==
> +			    V2_ONE_PORT_RVX_ONE_PORT_IMBED_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 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) {
> +
> +			if (icom_adapter->version == ADAPTER_V1) {
> +				icom_port->global_reg =
> +				    (struct icom_regs *) ((char *)
> +					icom_adapter->base_addr + 0x4000);
> +				icom_port->int_reg = (unsigned long)
> +				    icom_adapter->base_addr +
> +				    0x4004 + 2 - 2 * port_num;
> +			} else {
> +				icom_port->global_reg =
> +				    (struct icom_regs *) ((char *)
> +					icom_adapter->base_addr + 0x8000);
> +				if (icom_port->port < 2)
> +					icom_port->int_reg = (unsigned long)
> +					    icom_adapter->base_addr +
> +					    0x8004 + 2 -
> +					    2 * icom_port->port;
> +				else
> +					icom_port->int_reg = (unsigned long)
> +					    icom_adapter->base_addr +
> +					    0x8024 + 2 -
> +					    2 * (icom_port->port - 2);
> +			}

overzealous indent(1) making code less readable...


> +			icom_port->dram = (struct func_dram *) ((char *)
> +					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 */
> +			set_current_state(TASK_UNINTERRUPTIBLE);
> +			schedule_timeout(HZ / 10);

msleep()

> +			/* Stop proccessor */
> +			stop_processor(icom_port);
> +
> +			return_port_memory(icom_port);
> +		}
> +	}
> +
> +	free_irq(icom_adapter->irq_number, (void *) icom_adapter);
> +	iounmap((void *) icom_adapter->base_addr);
> +	release_mem_region(icom_adapter->base_addr_pci,
> +			   pci_resource_len(icom_adapter->pci_dev, 0));
> +	icom_free_adapter(icom_adapter);
> +}
> +
> +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;
> +
> +	if (pci_enable_device(dev)) {
> +		dev_err(&dev->dev, "Device enable FAILED\n");
> +		return -EIO;
> +	}

propagate return value from pci_enable_device()

> +	if (pci_read_config_dword(dev, PCI_COMMAND, &command_reg)) {
> +		dev_err(&dev->dev, "PCI Config read FAILED\n");
> +		return -EIO;
> +	}
> +
> +	pci_write_config_dword(dev, PCI_COMMAND, command_reg | 0x00000146);

wrong wrong wrong wrong.

1) use pci_set_master() to set bus mastering bit
2) pci_enable_device sets I/O and memory bits
3) use constants from linux/pci.h for the bits you are setting


> +	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)

BUG 1: you return without undoing pci_enable_device() [counterpart is 
pci_disable_device]

BUG 2 / race: you mess with the hardware before calling 
pci_request_regions() to reserve the hardware for your own use

> +		return retval;
> +
> +	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;

do not store this in the struct, just reference the pci_dev


> +	retval = icom_init_ports(icom_adapter);
> +	if (retval) {
> +		dev_err(&dev->dev, "Port configuration failed\n");
> +		goto probe_exit0;
> +	}
> +
> +	if (!request_mem_region(icom_adapter->base_addr_pci,
> +				pci_resource_len(dev, 0), "icom")) {
> +		dev_err(&dev->dev, "request_mem_region FAILED\n");
> +		retval = -EIO;
> +		goto probe_exit0;
> +	}

do not use request_mem_region() and release_mem_region() for PCI 
devices.  use pci_{request,release}_regions()


> +	icom_adapter->base_addr =
> +	    (unsigned long) ioremap(icom_adapter->base_addr_pci,
> +				    pci_resource_len(dev, 0));

BUG: check for NULL return value


> +	/* save off irq and request irq line */
> +	if (request_irq(dev->irq, icom_interrupt,
> +			SA_INTERRUPT | SA_SHIRQ, ICOM_DRIVER_NAME,
> +			(void *) icom_adapter)) {
> +		retval = -EIO;
> +		goto probe_exit1;
> +	}

BUG:  propagate return value from request_irq()


> +	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_exit1:
> +	iounmap((void *) icom_adapter->base_addr);
> +	release_mem_region(icom_adapter->base_addr_pci,
> +			   pci_resource_len(dev, 0));
> +
> +      probe_exit0:
> +	icom_free_adapter(icom_adapter);

ditto above -- undo pci_enable_device() etc.


> +	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);

race.  register the uart _after_ you figure out what hardware you have.


      parent reply	other threads:[~2004-07-27 15:27 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-07-26 19:10 new device driver to enable the IBM Multiport Serial Adapter in Linux Janice M Girouard
2004-07-27  7:03 ` Andrew Morton
2004-07-27 15:22 ` Jeff Garzik [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4106733B.3080108@pobox.com \
    --to=jgarzik@pobox.com \
    --cc=janiceg@us.ibm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=rmk+lkml@arm.linux.org.uk \
    --cc=wenxiong@us.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox