diff options
Diffstat (limited to 'drivers/tty/serial/ioc3_serial.c')
| -rw-r--r-- | drivers/tty/serial/ioc3_serial.c | 2199 | 
1 files changed, 2199 insertions, 0 deletions
diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c new file mode 100644 index 00000000000..ee43efc7bdc --- /dev/null +++ b/drivers/tty/serial/ioc3_serial.c @@ -0,0 +1,2199 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2005 Silicon Graphics, Inc.  All Rights Reserved. + */ + +/* + * This file contains a module version of the ioc3 serial driver. This + * includes all the support functions needed (support functions, etc.) + * and the serial driver itself. + */ +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/circ_buf.h> +#include <linux/serial_reg.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/serial_core.h> +#include <linux/ioc3.h> +#include <linux/slab.h> + +/* + * Interesting things about the ioc3 + */ + +#define LOGICAL_PORTS		2	/* rs232(0) and rs422(1) */ +#define PORTS_PER_CARD		2 +#define LOGICAL_PORTS_PER_CARD (PORTS_PER_CARD * LOGICAL_PORTS) +#define MAX_CARDS		8 +#define MAX_LOGICAL_PORTS	(LOGICAL_PORTS_PER_CARD * MAX_CARDS) + +/* determine given the sio_ir what port it applies to */ +#define GET_PORT_FROM_SIO_IR(_x)	(_x & SIO_IR_SA) ? 0 : 1 + + +/* + * we have 2 logical ports (rs232, rs422) for each physical port + * evens are rs232, odds are rs422 + */ +#define GET_PHYSICAL_PORT(_x)	((_x) >> 1) +#define GET_LOGICAL_PORT(_x)	((_x) & 1) +#define IS_PHYSICAL_PORT(_x)	!((_x) & 1) +#define IS_RS232(_x)		!((_x) & 1) + +static unsigned int Num_of_ioc3_cards; +static unsigned int Submodule_slot; + +/* defining this will get you LOTS of great debug info */ +//#define DEBUG_INTERRUPTS +#define DPRINT_CONFIG(_x...)	; +//#define DPRINT_CONFIG(_x...)  printk _x +#define NOT_PROGRESS()	; +//#define NOT_PROGRESS()	printk("%s : fails %d\n", __func__, __LINE__) + +/* number of characters we want to transmit to the lower level at a time */ +#define MAX_CHARS		256 +#define FIFO_SIZE		(MAX_CHARS-1)	/* it's a uchar */ + +/* Device name we're using */ +#define DEVICE_NAME		"ttySIOC" +#define DEVICE_MAJOR		204 +#define DEVICE_MINOR		116 + +/* flags for next_char_state */ +#define NCS_BREAK		0x1 +#define NCS_PARITY		0x2 +#define NCS_FRAMING		0x4 +#define NCS_OVERRUN		0x8 + +/* cause we need SOME parameters ... */ +#define MIN_BAUD_SUPPORTED	1200 +#define MAX_BAUD_SUPPORTED	115200 + +/* protocol types supported */ +#define PROTO_RS232		0 +#define PROTO_RS422		1 + +/* Notification types */ +#define N_DATA_READY		0x01 +#define N_OUTPUT_LOWAT		0x02 +#define N_BREAK			0x04 +#define N_PARITY_ERROR		0x08 +#define N_FRAMING_ERROR		0x10 +#define N_OVERRUN_ERROR		0x20 +#define N_DDCD			0x40 +#define N_DCTS			0x80 + +#define N_ALL_INPUT		(N_DATA_READY | N_BREAK			   \ +					| N_PARITY_ERROR | N_FRAMING_ERROR \ +					| N_OVERRUN_ERROR | N_DDCD | N_DCTS) + +#define N_ALL_OUTPUT		N_OUTPUT_LOWAT + +#define N_ALL_ERRORS		(N_PARITY_ERROR | N_FRAMING_ERROR \ +						| N_OVERRUN_ERROR) + +#define N_ALL			(N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK    \ +					| N_PARITY_ERROR | N_FRAMING_ERROR  \ +					| N_OVERRUN_ERROR | N_DDCD | N_DCTS) + +#define SER_CLK_SPEED(prediv)	((22000000 << 1) / prediv) +#define SER_DIVISOR(x, clk)	(((clk) + (x) * 8) / ((x) * 16)) +#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div)) + +/* Some masks */ +#define LCR_MASK_BITS_CHAR	(UART_LCR_WLEN5 | UART_LCR_WLEN6 \ +					| UART_LCR_WLEN7 | UART_LCR_WLEN8) +#define LCR_MASK_STOP_BITS	(UART_LCR_STOP) + +#define PENDING(_a, _p)		(readl(&(_p)->vma->sio_ir) & (_a)->ic_enable) + +#define RING_BUF_SIZE		4096 +#define BUF_SIZE_BIT		SBBR_L_SIZE +#define PROD_CONS_MASK		PROD_CONS_PTR_4K + +#define TOTAL_RING_BUF_SIZE	(RING_BUF_SIZE * 4) + +/* driver specific - one per card */ +struct ioc3_card { +	struct { +		/* uart ports are allocated here */ +		struct uart_port icp_uart_port[LOGICAL_PORTS]; +		/* the ioc3_port used for this port */ +		struct ioc3_port *icp_port; +	} ic_port[PORTS_PER_CARD]; +	/* currently enabled interrupts */ +	uint32_t ic_enable; +}; + +/* Local port info for each IOC3 serial port */ +struct ioc3_port { +	/* handy reference material */ +	struct uart_port *ip_port; +	struct ioc3_card *ip_card; +	struct ioc3_driver_data *ip_idd; +	struct ioc3_submodule *ip_is; + +	/* pci mem addresses for this port */ +	struct ioc3_serialregs __iomem *ip_serial_regs; +	struct ioc3_uartregs __iomem *ip_uart_regs; + +	/* Ring buffer page for this port */ +	dma_addr_t ip_dma_ringbuf; +	/* vaddr of ring buffer */ +	struct ring_buffer *ip_cpu_ringbuf; + +	/* Rings for this port */ +	struct ring *ip_inring; +	struct ring *ip_outring; + +	/* Hook to port specific values */ +	struct port_hooks *ip_hooks; + +	spinlock_t ip_lock; + +	/* Various rx/tx parameters */ +	int ip_baud; +	int ip_tx_lowat; +	int ip_rx_timeout; + +	/* Copy of notification bits */ +	int ip_notify; + +	/* Shadow copies of various registers so we don't need to PIO +	 * read them constantly +	 */ +	uint32_t ip_sscr; +	uint32_t ip_tx_prod; +	uint32_t ip_rx_cons; +	unsigned char ip_flags; +}; + +/* tx low water mark.  We need to notify the driver whenever tx is getting + * close to empty so it can refill the tx buffer and keep things going. + * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll + * have no trouble getting in more chars in time (I certainly hope so). + */ +#define TX_LOWAT_LATENCY      1000 +#define TX_LOWAT_HZ          (1000000 / TX_LOWAT_LATENCY) +#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ) + +/* Flags per port */ +#define INPUT_HIGH		0x01 +	/* used to signify that we have turned off the rx_high +	 * temporarily - we need to drain the fifo and don't +	 * want to get blasted with interrupts. +	 */ +#define DCD_ON			0x02 +	/* DCD state is on */ +#define LOWAT_WRITTEN		0x04 +#define READ_ABORTED		0x08 +	/* the read was aborted - used to avaoid infinate looping +	 * in the interrupt handler +	 */ +#define INPUT_ENABLE		0x10 + +/* Since each port has different register offsets and bitmasks + * for everything, we'll store those that we need in tables so we + * don't have to be constantly checking the port we are dealing with. + */ +struct port_hooks { +	uint32_t intr_delta_dcd; +	uint32_t intr_delta_cts; +	uint32_t intr_tx_mt; +	uint32_t intr_rx_timer; +	uint32_t intr_rx_high; +	uint32_t intr_tx_explicit; +	uint32_t intr_clear; +	uint32_t intr_all; +	char rs422_select_pin; +}; + +static struct port_hooks hooks_array[PORTS_PER_CARD] = { +	/* values for port A */ +	{ +	.intr_delta_dcd = SIO_IR_SA_DELTA_DCD, +	.intr_delta_cts = SIO_IR_SA_DELTA_CTS, +	.intr_tx_mt = SIO_IR_SA_TX_MT, +	.intr_rx_timer = SIO_IR_SA_RX_TIMER, +	.intr_rx_high = SIO_IR_SA_RX_HIGH, +	.intr_tx_explicit = SIO_IR_SA_TX_EXPLICIT, +	.intr_clear = (SIO_IR_SA_TX_MT | SIO_IR_SA_RX_FULL +				| SIO_IR_SA_RX_HIGH +				| SIO_IR_SA_RX_TIMER +				| SIO_IR_SA_DELTA_DCD +				| SIO_IR_SA_DELTA_CTS +				| SIO_IR_SA_INT +				| SIO_IR_SA_TX_EXPLICIT +				| SIO_IR_SA_MEMERR), +	.intr_all =  SIO_IR_SA, +	.rs422_select_pin = GPPR_UARTA_MODESEL_PIN, +	 }, + +	/* values for port B */ +	{ +	.intr_delta_dcd = SIO_IR_SB_DELTA_DCD, +	.intr_delta_cts = SIO_IR_SB_DELTA_CTS, +	.intr_tx_mt = SIO_IR_SB_TX_MT, +	.intr_rx_timer = SIO_IR_SB_RX_TIMER, +	.intr_rx_high = SIO_IR_SB_RX_HIGH, +	.intr_tx_explicit = SIO_IR_SB_TX_EXPLICIT, +	.intr_clear = (SIO_IR_SB_TX_MT | SIO_IR_SB_RX_FULL +				| SIO_IR_SB_RX_HIGH +				| SIO_IR_SB_RX_TIMER +				| SIO_IR_SB_DELTA_DCD +				| SIO_IR_SB_DELTA_CTS +				| SIO_IR_SB_INT +				| SIO_IR_SB_TX_EXPLICIT +				| SIO_IR_SB_MEMERR), +	.intr_all = SIO_IR_SB, +	.rs422_select_pin = GPPR_UARTB_MODESEL_PIN, +	 } +}; + +struct ring_entry { +	union { +		struct { +			uint32_t alldata; +			uint32_t allsc; +		} all; +		struct { +			char data[4];	/* data bytes */ +			char sc[4];	/* status/control */ +		} s; +	} u; +}; + +/* Test the valid bits in any of the 4 sc chars using "allsc" member */ +#define RING_ANY_VALID \ +	((uint32_t)(RXSB_MODEM_VALID | RXSB_DATA_VALID) * 0x01010101) + +#define ring_sc		u.s.sc +#define ring_data	u.s.data +#define ring_allsc	u.all.allsc + +/* Number of entries per ring buffer. */ +#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry)) + +/* An individual ring */ +struct ring { +	struct ring_entry entries[ENTRIES_PER_RING]; +}; + +/* The whole enchilada */ +struct ring_buffer { +	struct ring TX_A; +	struct ring RX_A; +	struct ring TX_B; +	struct ring RX_B; +}; + +/* Get a ring from a port struct */ +#define RING(_p, _wh)	&(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh) + +/* for Infinite loop detection  */ +#define MAXITER		10000000 + + +/** + * set_baud - Baud rate setting code + * @port: port to set + * @baud: baud rate to use + */ +static int set_baud(struct ioc3_port *port, int baud) +{ +	int divisor; +	int actual_baud; +	int diff; +	int lcr, prediv; +	struct ioc3_uartregs __iomem *uart; + +	for (prediv = 6; prediv < 64; prediv++) { +		divisor = SER_DIVISOR(baud, SER_CLK_SPEED(prediv)); +		if (!divisor) +			continue;	/* invalid divisor */ +		actual_baud = DIVISOR_TO_BAUD(divisor, SER_CLK_SPEED(prediv)); + +		diff = actual_baud - baud; +		if (diff < 0) +			diff = -diff; + +		/* if we're within 1% we've found a match */ +		if (diff * 100 <= actual_baud) +			break; +	} + +	/* if the above loop completed, we didn't match +	 * the baud rate.  give up. +	 */ +	if (prediv == 64) { +		NOT_PROGRESS(); +		return 1; +	} + +	uart = port->ip_uart_regs; +	lcr = readb(&uart->iu_lcr); + +	writeb(lcr | UART_LCR_DLAB, &uart->iu_lcr); +	writeb((unsigned char)divisor, &uart->iu_dll); +	writeb((unsigned char)(divisor >> 8), &uart->iu_dlm); +	writeb((unsigned char)prediv, &uart->iu_scr); +	writeb((unsigned char)lcr, &uart->iu_lcr); + +	return 0; +} + +/** + * get_ioc3_port - given a uart port, return the control structure + * @the_port: uart port to find + */ +static struct ioc3_port *get_ioc3_port(struct uart_port *the_port) +{ +	struct ioc3_driver_data *idd = dev_get_drvdata(the_port->dev); +	struct ioc3_card *card_ptr = idd->data[Submodule_slot]; +	int ii, jj; + +	if (!card_ptr) { +		NOT_PROGRESS(); +		return NULL; +	} +	for (ii = 0; ii < PORTS_PER_CARD; ii++) { +		for (jj = 0; jj < LOGICAL_PORTS; jj++) { +			if (the_port == &card_ptr->ic_port[ii].icp_uart_port[jj]) +				return card_ptr->ic_port[ii].icp_port; +		} +	} +	NOT_PROGRESS(); +	return NULL; +} + +/** + * port_init - Initialize the sio and ioc3 hardware for a given port + *			called per port from attach... + * @port: port to initialize + */ +static int inline port_init(struct ioc3_port *port) +{ +	uint32_t sio_cr; +	struct port_hooks *hooks = port->ip_hooks; +	struct ioc3_uartregs __iomem *uart; +	int reset_loop_counter = 0xfffff; +	struct ioc3_driver_data *idd = port->ip_idd; + +	/* Idle the IOC3 serial interface */ +	writel(SSCR_RESET, &port->ip_serial_regs->sscr); + +	/* Wait until any pending bus activity for this port has ceased */ +	do { +		sio_cr = readl(&idd->vma->sio_cr); +		if (reset_loop_counter-- <= 0) { +			printk(KERN_WARNING +			       "IOC3 unable to come out of reset" +				" scr 0x%x\n", sio_cr); +			return -1; +		} +	} while (!(sio_cr & SIO_CR_ARB_DIAG_IDLE) && +	       (((sio_cr &= SIO_CR_ARB_DIAG) == SIO_CR_ARB_DIAG_TXA) +		|| sio_cr == SIO_CR_ARB_DIAG_TXB +		|| sio_cr == SIO_CR_ARB_DIAG_RXA +		|| sio_cr == SIO_CR_ARB_DIAG_RXB)); + +	/* Finish reset sequence */ +	writel(0, &port->ip_serial_regs->sscr); + +	/* Once RESET is done, reload cached tx_prod and rx_cons values +	 * and set rings to empty by making prod == cons +	 */ +	port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; +	writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); +	port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; +	writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); + +	/* Disable interrupts for this 16550 */ +	uart = port->ip_uart_regs; +	writeb(0, &uart->iu_lcr); +	writeb(0, &uart->iu_ier); + +	/* Set the default baud */ +	set_baud(port, port->ip_baud); + +	/* Set line control to 8 bits no parity */ +	writeb(UART_LCR_WLEN8 | 0, &uart->iu_lcr); +	/* UART_LCR_STOP == 1 stop */ + +	/* Enable the FIFOs */ +	writeb(UART_FCR_ENABLE_FIFO, &uart->iu_fcr); +	/* then reset 16550 FIFOs */ +	writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, +	       &uart->iu_fcr); + +	/* Clear modem control register */ +	writeb(0, &uart->iu_mcr); + +	/* Clear deltas in modem status register */ +	writel(0, &port->ip_serial_regs->shadow); + +	/* Only do this once per port pair */ +	if (port->ip_hooks == &hooks_array[0]) { +		unsigned long ring_pci_addr; +		uint32_t __iomem *sbbr_l, *sbbr_h; + +		sbbr_l = &idd->vma->sbbr_l; +		sbbr_h = &idd->vma->sbbr_h; +		ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; +		DPRINT_CONFIG(("%s: ring_pci_addr 0x%p\n", +			       __func__, (void *)ring_pci_addr)); + +		writel((unsigned int)((uint64_t) ring_pci_addr >> 32), sbbr_h); +		writel((unsigned int)ring_pci_addr | BUF_SIZE_BIT, sbbr_l); +	} + +	/* Set the receive timeout value to 10 msec */ +	writel(SRTR_HZ / 100, &port->ip_serial_regs->srtr); + +	/* Set rx threshold, enable DMA */ +	/* Set high water mark at 3/4 of full ring */ +	port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); + +	/* uart experiences pauses at high baud rate reducing actual +	 * throughput by 10% or so unless we enable high speed polling +	 * XXX when this hardware bug is resolved we should revert to +	 * normal polling speed +	 */ +	port->ip_sscr |= SSCR_HIGH_SPD; + +	writel(port->ip_sscr, &port->ip_serial_regs->sscr); + +	/* Disable and clear all serial related interrupt bits */ +	port->ip_card->ic_enable &= ~hooks->intr_clear; +	ioc3_disable(port->ip_is, idd, hooks->intr_clear); +	ioc3_ack(port->ip_is, idd, hooks->intr_clear); +	return 0; +} + +/** + * enable_intrs - enable interrupts + * @port: port to enable + * @mask: mask to use + */ +static void enable_intrs(struct ioc3_port *port, uint32_t mask) +{ +	if ((port->ip_card->ic_enable & mask) != mask) { +		port->ip_card->ic_enable |= mask; +		ioc3_enable(port->ip_is, port->ip_idd, mask); +	} +} + +/** + * local_open - local open a port + * @port: port to open + */ +static inline int local_open(struct ioc3_port *port) +{ +	int spiniter = 0; + +	port->ip_flags = INPUT_ENABLE; + +	/* Pause the DMA interface if necessary */ +	if (port->ip_sscr & SSCR_DMA_EN) { +		writel(port->ip_sscr | SSCR_DMA_PAUSE, +		       &port->ip_serial_regs->sscr); +		while ((readl(&port->ip_serial_regs->sscr) +			& SSCR_PAUSE_STATE) == 0) { +			spiniter++; +			if (spiniter > MAXITER) { +				NOT_PROGRESS(); +				return -1; +			} +		} +	} + +	/* Reset the input fifo.  If the uart received chars while the port +	 * was closed and DMA is not enabled, the uart may have a bunch of +	 * chars hanging around in its rx fifo which will not be discarded +	 * by rclr in the upper layer. We must get rid of them here. +	 */ +	writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR, +	       &port->ip_uart_regs->iu_fcr); + +	writeb(UART_LCR_WLEN8, &port->ip_uart_regs->iu_lcr); +	/* UART_LCR_STOP == 1 stop */ + +	/* Re-enable DMA, set default threshold to intr whenever there is +	 * data available. +	 */ +	port->ip_sscr &= ~SSCR_RX_THRESHOLD; +	port->ip_sscr |= 1;	/* default threshold */ + +	/* Plug in the new sscr.  This implicitly clears the DMA_PAUSE +	 * flag if it was set above +	 */ +	writel(port->ip_sscr, &port->ip_serial_regs->sscr); +	port->ip_tx_lowat = 1; +	return 0; +} + +/** + * set_rx_timeout - Set rx timeout and threshold values. + * @port: port to use + * @timeout: timeout value in ticks + */ +static inline int set_rx_timeout(struct ioc3_port *port, int timeout) +{ +	int threshold; + +	port->ip_rx_timeout = timeout; + +	/* Timeout is in ticks.  Let's figure out how many chars we +	 * can receive at the current baud rate in that interval +	 * and set the rx threshold to that amount.  There are 4 chars +	 * per ring entry, so we'll divide the number of chars that will +	 * arrive in timeout by 4. +	 * So .... timeout * baud / 10 / HZ / 4, with HZ = 100. +	 */ +	threshold = timeout * port->ip_baud / 4000; +	if (threshold == 0) +		threshold = 1;	/* otherwise we'll intr all the time! */ + +	if ((unsigned)threshold > (unsigned)SSCR_RX_THRESHOLD) +		return 1; + +	port->ip_sscr &= ~SSCR_RX_THRESHOLD; +	port->ip_sscr |= threshold; +	writel(port->ip_sscr, &port->ip_serial_regs->sscr); + +	/* Now set the rx timeout to the given value +	 * again timeout * SRTR_HZ / HZ +	 */ +	timeout = timeout * SRTR_HZ / 100; +	if (timeout > SRTR_CNT) +		timeout = SRTR_CNT; +	writel(timeout, &port->ip_serial_regs->srtr); +	return 0; +} + +/** + * config_port - config the hardware + * @port: port to config + * @baud: baud rate for the port + * @byte_size: data size + * @stop_bits: number of stop bits + * @parenb: parity enable ? + * @parodd: odd parity ? + */ +static inline int +config_port(struct ioc3_port *port, +	    int baud, int byte_size, int stop_bits, int parenb, int parodd) +{ +	char lcr, sizebits; +	int spiniter = 0; + +	DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d " +			"parodd %d\n", +		       __func__, ((struct uart_port *)port->ip_port)->line, +			baud, byte_size, stop_bits, parenb, parodd)); + +	if (set_baud(port, baud)) +		return 1; + +	switch (byte_size) { +	case 5: +		sizebits = UART_LCR_WLEN5; +		break; +	case 6: +		sizebits = UART_LCR_WLEN6; +		break; +	case 7: +		sizebits = UART_LCR_WLEN7; +		break; +	case 8: +		sizebits = UART_LCR_WLEN8; +		break; +	default: +		return 1; +	} + +	/* Pause the DMA interface if necessary */ +	if (port->ip_sscr & SSCR_DMA_EN) { +		writel(port->ip_sscr | SSCR_DMA_PAUSE, +		       &port->ip_serial_regs->sscr); +		while ((readl(&port->ip_serial_regs->sscr) +			& SSCR_PAUSE_STATE) == 0) { +			spiniter++; +			if (spiniter > MAXITER) +				return -1; +		} +	} + +	/* Clear relevant fields in lcr */ +	lcr = readb(&port->ip_uart_regs->iu_lcr); +	lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR | +		 UART_LCR_PARITY | LCR_MASK_STOP_BITS); + +	/* Set byte size in lcr */ +	lcr |= sizebits; + +	/* Set parity */ +	if (parenb) { +		lcr |= UART_LCR_PARITY; +		if (!parodd) +			lcr |= UART_LCR_EPAR; +	} + +	/* Set stop bits */ +	if (stop_bits) +		lcr |= UART_LCR_STOP /* 2 stop bits */ ; + +	writeb(lcr, &port->ip_uart_regs->iu_lcr); + +	/* Re-enable the DMA interface if necessary */ +	if (port->ip_sscr & SSCR_DMA_EN) { +		writel(port->ip_sscr, &port->ip_serial_regs->sscr); +	} +	port->ip_baud = baud; + +	/* When we get within this number of ring entries of filling the +	 * entire ring on tx, place an EXPLICIT intr to generate a lowat +	 * notification when output has drained. +	 */ +	port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; +	if (port->ip_tx_lowat == 0) +		port->ip_tx_lowat = 1; + +	set_rx_timeout(port, 2); +	return 0; +} + +/** + * do_write - Write bytes to the port.  Returns the number of bytes + *			actually written. Called from transmit_chars + * @port: port to use + * @buf: the stuff to write + * @len: how many bytes in 'buf' + */ +static inline int do_write(struct ioc3_port *port, char *buf, int len) +{ +	int prod_ptr, cons_ptr, total = 0; +	struct ring *outring; +	struct ring_entry *entry; +	struct port_hooks *hooks = port->ip_hooks; + +	BUG_ON(!(len >= 0)); + +	prod_ptr = port->ip_tx_prod; +	cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; +	outring = port->ip_outring; + +	/* Maintain a 1-entry red-zone.  The ring buffer is full when +	 * (cons - prod) % ring_size is 1.  Rather than do this subtraction +	 * in the body of the loop, I'll do it now. +	 */ +	cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK; + +	/* Stuff the bytes into the output */ +	while ((prod_ptr != cons_ptr) && (len > 0)) { +		int xx; + +		/* Get 4 bytes (one ring entry) at a time */ +		entry = (struct ring_entry *)((caddr_t) outring + prod_ptr); + +		/* Invalidate all entries */ +		entry->ring_allsc = 0; + +		/* Copy in some bytes */ +		for (xx = 0; (xx < 4) && (len > 0); xx++) { +			entry->ring_data[xx] = *buf++; +			entry->ring_sc[xx] = TXCB_VALID; +			len--; +			total++; +		} + +		/* If we are within some small threshold of filling up the +		 * entire ring buffer, we must place an EXPLICIT intr here +		 * to generate a lowat interrupt in case we subsequently +		 * really do fill up the ring and the caller goes to sleep. +		 * No need to place more than one though. +		 */ +		if (!(port->ip_flags & LOWAT_WRITTEN) && +		    ((cons_ptr - prod_ptr) & PROD_CONS_MASK) +		    <= port->ip_tx_lowat * (int)sizeof(struct ring_entry)) { +			port->ip_flags |= LOWAT_WRITTEN; +			entry->ring_sc[0] |= TXCB_INT_WHEN_DONE; +		} + +		/* Go on to next entry */ +		prod_ptr += sizeof(struct ring_entry); +		prod_ptr &= PROD_CONS_MASK; +	} + +	/* If we sent something, start DMA if necessary */ +	if (total > 0 && !(port->ip_sscr & SSCR_DMA_EN)) { +		port->ip_sscr |= SSCR_DMA_EN; +		writel(port->ip_sscr, &port->ip_serial_regs->sscr); +	} + +	/* Store the new producer pointer.  If tx is disabled, we stuff the +	 * data into the ring buffer, but we don't actually start tx. +	 */ +	if (!uart_tx_stopped(port->ip_port)) { +		writel(prod_ptr, &port->ip_serial_regs->stpir); + +		/* If we are now transmitting, enable tx_mt interrupt so we +		 * can disable DMA if necessary when the tx finishes. +		 */ +		if (total > 0) +			enable_intrs(port, hooks->intr_tx_mt); +	} +	port->ip_tx_prod = prod_ptr; + +	return total; +} + +/** + * disable_intrs - disable interrupts + * @port: port to enable + * @mask: mask to use + */ +static inline void disable_intrs(struct ioc3_port *port, uint32_t mask) +{ +	if (port->ip_card->ic_enable & mask) { +		ioc3_disable(port->ip_is, port->ip_idd, mask); +		port->ip_card->ic_enable &= ~mask; +	} +} + +/** + * set_notification - Modify event notification + * @port: port to use + * @mask: events mask + * @set_on: set ? + */ +static int set_notification(struct ioc3_port *port, int mask, int set_on) +{ +	struct port_hooks *hooks = port->ip_hooks; +	uint32_t intrbits, sscrbits; + +	BUG_ON(!mask); + +	intrbits = sscrbits = 0; + +	if (mask & N_DATA_READY) +		intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high); +	if (mask & N_OUTPUT_LOWAT) +		intrbits |= hooks->intr_tx_explicit; +	if (mask & N_DDCD) { +		intrbits |= hooks->intr_delta_dcd; +		sscrbits |= SSCR_RX_RING_DCD; +	} +	if (mask & N_DCTS) +		intrbits |= hooks->intr_delta_cts; + +	if (set_on) { +		enable_intrs(port, intrbits); +		port->ip_notify |= mask; +		port->ip_sscr |= sscrbits; +	} else { +		disable_intrs(port, intrbits); +		port->ip_notify &= ~mask; +		port->ip_sscr &= ~sscrbits; +	} + +	/* We require DMA if either DATA_READY or DDCD notification is +	 * currently requested. If neither of these is requested and +	 * there is currently no tx in progress, DMA may be disabled. +	 */ +	if (port->ip_notify & (N_DATA_READY | N_DDCD)) +		port->ip_sscr |= SSCR_DMA_EN; +	else if (!(port->ip_card->ic_enable & hooks->intr_tx_mt)) +		port->ip_sscr &= ~SSCR_DMA_EN; + +	writel(port->ip_sscr, &port->ip_serial_regs->sscr); +	return 0; +} + +/** + * set_mcr - set the master control reg + * @the_port: port to use + * @mask1: mcr mask + * @mask2: shadow mask + */ +static inline int set_mcr(struct uart_port *the_port, +			  int mask1, int mask2) +{ +	struct ioc3_port *port = get_ioc3_port(the_port); +	uint32_t shadow; +	int spiniter = 0; +	char mcr; + +	if (!port) +		return -1; + +	/* Pause the DMA interface if necessary */ +	if (port->ip_sscr & SSCR_DMA_EN) { +		writel(port->ip_sscr | SSCR_DMA_PAUSE, +		       &port->ip_serial_regs->sscr); +		while ((readl(&port->ip_serial_regs->sscr) +			& SSCR_PAUSE_STATE) == 0) { +			spiniter++; +			if (spiniter > MAXITER) +				return -1; +		} +	} +	shadow = readl(&port->ip_serial_regs->shadow); +	mcr = (shadow & 0xff000000) >> 24; + +	/* Set new value */ +	mcr |= mask1; +	shadow |= mask2; +	writeb(mcr, &port->ip_uart_regs->iu_mcr); +	writel(shadow, &port->ip_serial_regs->shadow); + +	/* Re-enable the DMA interface if necessary */ +	if (port->ip_sscr & SSCR_DMA_EN) { +		writel(port->ip_sscr, &port->ip_serial_regs->sscr); +	} +	return 0; +} + +/** + * ioc3_set_proto - set the protocol for the port + * @port: port to use + * @proto: protocol to use + */ +static int ioc3_set_proto(struct ioc3_port *port, int proto) +{ +	struct port_hooks *hooks = port->ip_hooks; + +	switch (proto) { +	default: +	case PROTO_RS232: +		/* Clear the appropriate GIO pin */ +		DPRINT_CONFIG(("%s: rs232\n", __func__)); +		writel(0, (&port->ip_idd->vma->gppr[0] +					+ hooks->rs422_select_pin)); +		break; + +	case PROTO_RS422: +		/* Set the appropriate GIO pin */ +		DPRINT_CONFIG(("%s: rs422\n", __func__)); +		writel(1, (&port->ip_idd->vma->gppr[0] +					+ hooks->rs422_select_pin)); +		break; +	} +	return 0; +} + +/** + * transmit_chars - upper level write, called with the_port->lock + * @the_port: port to write + */ +static void transmit_chars(struct uart_port *the_port) +{ +	int xmit_count, tail, head; +	int result; +	char *start; +	struct tty_struct *tty; +	struct ioc3_port *port = get_ioc3_port(the_port); +	struct uart_state *state; + +	if (!the_port) +		return; +	if (!port) +		return; + +	state = the_port->state; +	tty = state->port.tty; + +	if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { +		/* Nothing to do or hw stopped */ +		set_notification(port, N_ALL_OUTPUT, 0); +		return; +	} + +	head = state->xmit.head; +	tail = state->xmit.tail; +	start = (char *)&state->xmit.buf[tail]; + +	/* write out all the data or until the end of the buffer */ +	xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); +	if (xmit_count > 0) { +		result = do_write(port, start, xmit_count); +		if (result > 0) { +			/* booking */ +			xmit_count -= result; +			the_port->icount.tx += result; +			/* advance the pointers */ +			tail += result; +			tail &= UART_XMIT_SIZE - 1; +			state->xmit.tail = tail; +			start = (char *)&state->xmit.buf[tail]; +		} +	} +	if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) +		uart_write_wakeup(the_port); + +	if (uart_circ_empty(&state->xmit)) { +		set_notification(port, N_OUTPUT_LOWAT, 0); +	} else { +		set_notification(port, N_OUTPUT_LOWAT, 1); +	} +} + +/** + * ioc3_change_speed - change the speed of the port + * @the_port: port to change + * @new_termios: new termios settings + * @old_termios: old termios settings + */ +static void +ioc3_change_speed(struct uart_port *the_port, +		  struct ktermios *new_termios, struct ktermios *old_termios) +{ +	struct ioc3_port *port = get_ioc3_port(the_port); +	unsigned int cflag, iflag; +	int baud; +	int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; +	struct uart_state *state = the_port->state; + +	cflag = new_termios->c_cflag; +	iflag = new_termios->c_iflag; + +	switch (cflag & CSIZE) { +	case CS5: +		new_data = 5; +		break; +	case CS6: +		new_data = 6; +		break; +	case CS7: +		new_data = 7; +		break; +	case CS8: +		new_data = 8; +		break; +	default: +		/* cuz we always need a default ... */ +		new_data = 5; +		break; +	} +	if (cflag & CSTOPB) { +		new_stop = 1; +	} +	if (cflag & PARENB) { +		new_parity_enable = 1; +		if (cflag & PARODD) +			new_parity = 1; +	} +	baud = uart_get_baud_rate(the_port, new_termios, old_termios, +				  MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); +	DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __func__, baud, +				the_port->line)); + +	if (!the_port->fifosize) +		the_port->fifosize = FIFO_SIZE; +	uart_update_timeout(the_port, cflag, baud); + +	the_port->ignore_status_mask = N_ALL_INPUT; + +	state->port.tty->low_latency = 1; + +	if (iflag & IGNPAR) +		the_port->ignore_status_mask &= ~(N_PARITY_ERROR +						  | N_FRAMING_ERROR); +	if (iflag & IGNBRK) { +		the_port->ignore_status_mask &= ~N_BREAK; +		if (iflag & IGNPAR) +			the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; +	} +	if (!(cflag & CREAD)) { +		/* ignore everything */ +		the_port->ignore_status_mask &= ~N_DATA_READY; +	} + +	if (cflag & CRTSCTS) { +		/* enable hardware flow control */ +		port->ip_sscr |= SSCR_HFC_EN; +	} +	else { +		/* disable hardware flow control */ +		port->ip_sscr &= ~SSCR_HFC_EN; +	} +	writel(port->ip_sscr, &port->ip_serial_regs->sscr); + +	/* Set the configuration and proper notification call */ +	DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o " +		       "config_port(baud %d data %d stop %d penable %d " +			" parity %d), notification 0x%x\n", +		       __func__, (void *)port, the_port->line, cflag, baud, +		       new_data, new_stop, new_parity_enable, new_parity, +		       the_port->ignore_status_mask)); + +	if ((config_port(port, baud,	/* baud */ +			 new_data,	/* byte size */ +			 new_stop,	/* stop bits */ +			 new_parity_enable,	/* set parity */ +			 new_parity)) >= 0) {	/* parity 1==odd */ +		set_notification(port, the_port->ignore_status_mask, 1); +	} +} + +/** + * ic3_startup_local - Start up the serial port - returns >= 0 if no errors + * @the_port: Port to operate on + */ +static inline int ic3_startup_local(struct uart_port *the_port) +{ +	struct ioc3_port *port; + +	if (!the_port) { +		NOT_PROGRESS(); +		return -1; +	} + +	port = get_ioc3_port(the_port); +	if (!port) { +		NOT_PROGRESS(); +		return -1; +	} + +	local_open(port); + +	/* set the protocol */ +	ioc3_set_proto(port, IS_RS232(the_port->line) ? PROTO_RS232 : +							PROTO_RS422); +	return 0; +} + +/* + * ioc3_cb_output_lowat - called when the output low water mark is hit + * @port: port to output + */ +static void ioc3_cb_output_lowat(struct ioc3_port *port) +{ +	unsigned long pflags; + +	/* the_port->lock is set on the call here */ +	if (port->ip_port) { +		spin_lock_irqsave(&port->ip_port->lock, pflags); +		transmit_chars(port->ip_port); +		spin_unlock_irqrestore(&port->ip_port->lock, pflags); +	} +} + +/* + * ioc3_cb_post_ncs - called for some basic errors + * @port: port to use + * @ncs: event + */ +static void ioc3_cb_post_ncs(struct uart_port *the_port, int ncs) +{ +	struct uart_icount *icount; + +	icount = &the_port->icount; + +	if (ncs & NCS_BREAK) +		icount->brk++; +	if (ncs & NCS_FRAMING) +		icount->frame++; +	if (ncs & NCS_OVERRUN) +		icount->overrun++; +	if (ncs & NCS_PARITY) +		icount->parity++; +} + +/** + * do_read - Read in bytes from the port.  Return the number of bytes + *			actually read. + * @the_port: port to use + * @buf: place to put the stuff we read + * @len: how big 'buf' is + */ + +static inline int do_read(struct uart_port *the_port, char *buf, int len) +{ +	int prod_ptr, cons_ptr, total; +	struct ioc3_port *port = get_ioc3_port(the_port); +	struct ring *inring; +	struct ring_entry *entry; +	struct port_hooks *hooks = port->ip_hooks; +	int byte_num; +	char *sc; +	int loop_counter; + +	BUG_ON(!(len >= 0)); +	BUG_ON(!port); + +	/* There is a nasty timing issue in the IOC3. When the rx_timer +	 * expires or the rx_high condition arises, we take an interrupt. +	 * At some point while servicing the interrupt, we read bytes from +	 * the ring buffer and re-arm the rx_timer.  However the rx_timer is +	 * not started until the first byte is received *after* it is armed, +	 * and any bytes pending in the rx construction buffers are not drained +	 * to memory until either there are 4 bytes available or the rx_timer +	 * expires.  This leads to a potential situation where data is left +	 * in the construction buffers forever - 1 to 3 bytes were received +	 * after the interrupt was generated but before the rx_timer was +	 * re-armed. At that point as long as no subsequent bytes are received +	 * the timer will never be started and the bytes will remain in the +	 * construction buffer forever.  The solution is to execute a DRAIN +	 * command after rearming the timer.  This way any bytes received before +	 * the DRAIN will be drained to memory, and any bytes received after +	 * the DRAIN will start the TIMER and be drained when it expires. +	 * Luckily, this only needs to be done when the DMA buffer is empty +	 * since there is no requirement that this function return all +	 * available data as long as it returns some. +	 */ +	/* Re-arm the timer */ + +	writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); + +	prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; +	cons_ptr = port->ip_rx_cons; + +	if (prod_ptr == cons_ptr) { +		int reset_dma = 0; + +		/* Input buffer appears empty, do a flush. */ + +		/* DMA must be enabled for this to work. */ +		if (!(port->ip_sscr & SSCR_DMA_EN)) { +			port->ip_sscr |= SSCR_DMA_EN; +			reset_dma = 1; +		} + +		/* Potential race condition: we must reload the srpir after +		 * issuing the drain command, otherwise we could think the rx +		 * buffer is empty, then take a very long interrupt, and when +		 * we come back it's full and we wait forever for the drain to +		 * complete. +		 */ +		writel(port->ip_sscr | SSCR_RX_DRAIN, +		       &port->ip_serial_regs->sscr); +		prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; + +		/* We must not wait for the DRAIN to complete unless there are +		 * at least 8 bytes (2 ring entries) available to receive the +		 * data otherwise the DRAIN will never complete and we'll +		 * deadlock here. +		 * In fact, to make things easier, I'll just ignore the flush if +		 * there is any data at all now available. +		 */ +		if (prod_ptr == cons_ptr) { +			loop_counter = 0; +			while (readl(&port->ip_serial_regs->sscr) & +			       SSCR_RX_DRAIN) { +				loop_counter++; +				if (loop_counter > MAXITER) +					return -1; +			} + +			/* SIGH. We have to reload the prod_ptr *again* since +			 * the drain may have caused it to change +			 */ +			prod_ptr = readl(&port->ip_serial_regs->srpir) +			    & PROD_CONS_MASK; +		} +		if (reset_dma) { +			port->ip_sscr &= ~SSCR_DMA_EN; +			writel(port->ip_sscr, &port->ip_serial_regs->sscr); +		} +	} +	inring = port->ip_inring; +	port->ip_flags &= ~READ_ABORTED; + +	total = 0; +	loop_counter = 0xfffff;	/* to avoid hangs */ + +	/* Grab bytes from the hardware */ +	while ((prod_ptr != cons_ptr) && (len > 0)) { +		entry = (struct ring_entry *)((caddr_t) inring + cons_ptr); + +		if (loop_counter-- <= 0) { +			printk(KERN_WARNING "IOC3 serial: " +			       "possible hang condition/" +			       "port stuck on read (line %d).\n", +				the_port->line); +			break; +		} + +		/* According to the producer pointer, this ring entry +		 * must contain some data.  But if the PIO happened faster +		 * than the DMA, the data may not be available yet, so let's +		 * wait until it arrives. +		 */ +		if ((entry->ring_allsc & RING_ANY_VALID) == 0) { +			/* Indicate the read is aborted so we don't disable +			 * the interrupt thinking that the consumer is +			 * congested. +			 */ +			port->ip_flags |= READ_ABORTED; +			len = 0; +			break; +		} + +		/* Load the bytes/status out of the ring entry */ +		for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) { +			sc = &(entry->ring_sc[byte_num]); + +			/* Check for change in modem state or overrun */ +			if ((*sc & RXSB_MODEM_VALID) +			    && (port->ip_notify & N_DDCD)) { +				/* Notify upper layer if DCD dropped */ +				if ((port->ip_flags & DCD_ON) +				    && !(*sc & RXSB_DCD)) { +					/* If we have already copied some data, +					 * return it.  We'll pick up the carrier +					 * drop on the next pass.  That way we +					 * don't throw away the data that has +					 * already been copied back to +					 * the caller's buffer. +					 */ +					if (total > 0) { +						len = 0; +						break; +					} +					port->ip_flags &= ~DCD_ON; + +					/* Turn off this notification so the +					 * carrier drop protocol won't see it +					 * again when it does a read. +					 */ +					*sc &= ~RXSB_MODEM_VALID; + +					/* To keep things consistent, we need +					 * to update the consumer pointer so +					 * the next reader won't come in and +					 * try to read the same ring entries +					 * again. This must be done here before +					 * the dcd change. +					 */ + +					if ((entry->ring_allsc & RING_ANY_VALID) +					    == 0) { +						cons_ptr += (int)sizeof +						    (struct ring_entry); +						cons_ptr &= PROD_CONS_MASK; +					} +					writel(cons_ptr, +					       &port->ip_serial_regs->srcir); +					port->ip_rx_cons = cons_ptr; + +					/* Notify upper layer of carrier drop */ +					if ((port->ip_notify & N_DDCD) +					    && port->ip_port) { +						uart_handle_dcd_change +							(port->ip_port, 0); +						wake_up_interruptible +						    (&the_port->state-> +						     port.delta_msr_wait); +					} + +					/* If we had any data to return, we +					 * would have returned it above. +					 */ +					return 0; +				} +			} +			if (*sc & RXSB_MODEM_VALID) { +				/* Notify that an input overrun occurred */ +				if ((*sc & RXSB_OVERRUN) +				    && (port->ip_notify & N_OVERRUN_ERROR)) { +					ioc3_cb_post_ncs(the_port, NCS_OVERRUN); +				} +				/* Don't look at this byte again */ +				*sc &= ~RXSB_MODEM_VALID; +			} + +			/* Check for valid data or RX errors */ +			if ((*sc & RXSB_DATA_VALID) && +			    ((*sc & (RXSB_PAR_ERR +				     | RXSB_FRAME_ERR | RXSB_BREAK)) +			     && (port->ip_notify & (N_PARITY_ERROR +						    | N_FRAMING_ERROR +						    | N_BREAK)))) { +				/* There is an error condition on the next byte. +				 * If we have already transferred some bytes, +				 * we'll stop here. Otherwise if this is the +				 * first byte to be read, we'll just transfer +				 * it alone after notifying the +				 * upper layer of its status. +				 */ +				if (total > 0) { +					len = 0; +					break; +				} else { +					if ((*sc & RXSB_PAR_ERR) && +					    (port-> +					     ip_notify & N_PARITY_ERROR)) { +						ioc3_cb_post_ncs(the_port, +								 NCS_PARITY); +					} +					if ((*sc & RXSB_FRAME_ERR) && +					    (port-> +					     ip_notify & N_FRAMING_ERROR)) { +						ioc3_cb_post_ncs(the_port, +								 NCS_FRAMING); +					} +					if ((*sc & RXSB_BREAK) +					    && (port->ip_notify & N_BREAK)) { +						ioc3_cb_post_ncs +						    (the_port, NCS_BREAK); +					} +					len = 1; +				} +			} +			if (*sc & RXSB_DATA_VALID) { +				*sc &= ~RXSB_DATA_VALID; +				*buf = entry->ring_data[byte_num]; +				buf++; +				len--; +				total++; +			} +		} + +		/* If we used up this entry entirely, go on to the next one, +		 * otherwise we must have run out of buffer space, so +		 * leave the consumer pointer here for the next read in case +		 * there are still unread bytes in this entry. +		 */ +		if ((entry->ring_allsc & RING_ANY_VALID) == 0) { +			cons_ptr += (int)sizeof(struct ring_entry); +			cons_ptr &= PROD_CONS_MASK; +		} +	} + +	/* Update consumer pointer and re-arm rx timer interrupt */ +	writel(cons_ptr, &port->ip_serial_regs->srcir); +	port->ip_rx_cons = cons_ptr; + +	/* If we have now dipped below the rx high water mark and we have +	 * rx_high interrupt turned off, we can now turn it back on again. +	 */ +	if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr) +					       & PROD_CONS_MASK) < +					      ((port-> +						ip_sscr & +						SSCR_RX_THRESHOLD) +					       << PROD_CONS_PTR_OFF))) { +		port->ip_flags &= ~INPUT_HIGH; +		enable_intrs(port, hooks->intr_rx_high); +	} +	return total; +} + +/** + * receive_chars - upper level read. + * @the_port: port to read from + */ +static int receive_chars(struct uart_port *the_port) +{ +	struct tty_struct *tty; +	unsigned char ch[MAX_CHARS]; +	int read_count = 0, read_room, flip = 0; +	struct uart_state *state = the_port->state; +	struct ioc3_port *port = get_ioc3_port(the_port); +	unsigned long pflags; + +	/* Make sure all the pointers are "good" ones */ +	if (!state) +		return 0; +	if (!state->port.tty) +		return 0; + +	if (!(port->ip_flags & INPUT_ENABLE)) +		return 0; + +	spin_lock_irqsave(&the_port->lock, pflags); +	tty = state->port.tty; + +	read_count = do_read(the_port, ch, MAX_CHARS); +	if (read_count > 0) { +		flip = 1; +		read_room = tty_insert_flip_string(tty, ch, read_count); +		the_port->icount.rx += read_count; +	} +	spin_unlock_irqrestore(&the_port->lock, pflags); + +	if (flip) +		tty_flip_buffer_push(tty); + +	return read_count; +} + +/** + * ioc3uart_intr_one - lowest level (per port) interrupt handler. + * @is : submodule + * @idd: driver data + * @pending: interrupts to handle + */ + +static int inline +ioc3uart_intr_one(struct ioc3_submodule *is, +			struct ioc3_driver_data *idd, +			unsigned int pending) +{ +	int port_num = GET_PORT_FROM_SIO_IR(pending); +	struct port_hooks *hooks; +	unsigned int rx_high_rd_aborted = 0; +	unsigned long flags; +	struct uart_port *the_port; +	struct ioc3_port *port; +	int loop_counter; +	struct ioc3_card *card_ptr; +	unsigned int sio_ir; + +	card_ptr = idd->data[is->id]; +	port = card_ptr->ic_port[port_num].icp_port; +	hooks = port->ip_hooks; + +	/* Possible race condition here: The tx_mt interrupt bit may be +	 * cleared without the intervention of the interrupt handler, +	 * e.g. by a write.  If the top level interrupt handler reads a +	 * tx_mt, then some other processor does a write, starting up +	 * output, then we come in here, see the tx_mt and stop DMA, the +	 * output started by the other processor will hang.  Thus we can +	 * only rely on tx_mt being legitimate if it is read while the +	 * port lock is held.  Therefore this bit must be ignored in the +	 * passed in interrupt mask which was read by the top level +	 * interrupt handler since the port lock was not held at the time +	 * it was read.  We can only rely on this bit being accurate if it +	 * is read while the port lock is held.  So we'll clear it for now, +	 * and reload it later once we have the port lock. +	 */ + +	sio_ir = pending & ~(hooks->intr_tx_mt); +	spin_lock_irqsave(&port->ip_lock, flags); + +	loop_counter = MAXITER;	/* to avoid hangs */ + +	do { +		uint32_t shadow; + +		if (loop_counter-- <= 0) { +			printk(KERN_WARNING "IOC3 serial: " +			       "possible hang condition/" +			       "port stuck on interrupt (line %d).\n", +				((struct uart_port *)port->ip_port)->line); +			break; +		} +		/* Handle a DCD change */ +		if (sio_ir & hooks->intr_delta_dcd) { +			ioc3_ack(is, idd, hooks->intr_delta_dcd); +			shadow = readl(&port->ip_serial_regs->shadow); + +			if ((port->ip_notify & N_DDCD) +			    && (shadow & SHADOW_DCD) +			    && (port->ip_port)) { +				the_port = port->ip_port; +				uart_handle_dcd_change(the_port, +						shadow & SHADOW_DCD); +				wake_up_interruptible +				    (&the_port->state->port.delta_msr_wait); +			} else if ((port->ip_notify & N_DDCD) +				   && !(shadow & SHADOW_DCD)) { +				/* Flag delta DCD/no DCD */ +				uart_handle_dcd_change(port->ip_port, +						shadow & SHADOW_DCD); +				port->ip_flags |= DCD_ON; +			} +		} + +		/* Handle a CTS change */ +		if (sio_ir & hooks->intr_delta_cts) { +			ioc3_ack(is, idd, hooks->intr_delta_cts); +			shadow = readl(&port->ip_serial_regs->shadow); + +			if ((port->ip_notify & N_DCTS) && (port->ip_port)) { +				the_port = port->ip_port; +				uart_handle_cts_change(the_port, shadow +						& SHADOW_CTS); +				wake_up_interruptible +				    (&the_port->state->port.delta_msr_wait); +			} +		} + +		/* rx timeout interrupt.  Must be some data available.  Put this +		 * before the check for rx_high since servicing this condition +		 * may cause that condition to clear. +		 */ +		if (sio_ir & hooks->intr_rx_timer) { +			ioc3_ack(is, idd, hooks->intr_rx_timer); +			if ((port->ip_notify & N_DATA_READY) +						&& (port->ip_port)) { +				receive_chars(port->ip_port); +			} +		} + +		/* rx high interrupt. Must be after rx_timer.  */ +		else if (sio_ir & hooks->intr_rx_high) { +			/* Data available, notify upper layer */ +			if ((port->ip_notify & N_DATA_READY) && port->ip_port) { +				receive_chars(port->ip_port); +			} + +			/* We can't ACK this interrupt.  If receive_chars didn't +			 * cause the condition to clear, we'll have to disable +			 * the interrupt until the data is drained. +			 * If the read was aborted, don't disable the interrupt +			 * as this may cause us to hang indefinitely.  An +			 * aborted read generally means that this interrupt +			 * hasn't been delivered to the cpu yet anyway, even +			 * though we see it as asserted when we read the sio_ir. +			 */ +			if ((sio_ir = PENDING(card_ptr, idd)) +					& hooks->intr_rx_high) { +				if (port->ip_flags & READ_ABORTED) { +					rx_high_rd_aborted++; +				} +				else { +					card_ptr->ic_enable &= ~hooks->intr_rx_high; +					port->ip_flags |= INPUT_HIGH; +				} +			} +		} + +		/* We got a low water interrupt: notify upper layer to +		 * send more data.  Must come before tx_mt since servicing +		 * this condition may cause that condition to clear. +		 */ +		if (sio_ir & hooks->intr_tx_explicit) { +			port->ip_flags &= ~LOWAT_WRITTEN; +			ioc3_ack(is, idd, hooks->intr_tx_explicit); +			if (port->ip_notify & N_OUTPUT_LOWAT) +				ioc3_cb_output_lowat(port); +		} + +		/* Handle tx_mt.  Must come after tx_explicit.  */ +		else if (sio_ir & hooks->intr_tx_mt) { +			/* If we are expecting a lowat notification +			 * and we get to this point it probably means that for +			 * some reason the tx_explicit didn't work as expected +			 * (that can legitimately happen if the output buffer is +			 * filled up in just the right way). +			 * So send the notification now. +			 */ +			if (port->ip_notify & N_OUTPUT_LOWAT) { +				ioc3_cb_output_lowat(port); + +				/* We need to reload the sio_ir since the lowat +				 * call may have caused another write to occur, +				 * clearing the tx_mt condition. +				 */ +				sio_ir = PENDING(card_ptr, idd); +			} + +			/* If the tx_mt condition still persists even after the +			 * lowat call, we've got some work to do. +			 */ +			if (sio_ir & hooks->intr_tx_mt) { +				/* If we are not currently expecting DMA input, +				 * and the transmitter has just gone idle, +				 * there is no longer any reason for DMA, so +				 * disable it. +				 */ +				if (!(port->ip_notify +				      & (N_DATA_READY | N_DDCD))) { +					BUG_ON(!(port->ip_sscr +						 & SSCR_DMA_EN)); +					port->ip_sscr &= ~SSCR_DMA_EN; +					writel(port->ip_sscr, +					       &port->ip_serial_regs->sscr); +				} +				/* Prevent infinite tx_mt interrupt */ +				card_ptr->ic_enable &= ~hooks->intr_tx_mt; +			} +		} +		sio_ir = PENDING(card_ptr, idd); + +		/* if the read was aborted and only hooks->intr_rx_high, +		 * clear hooks->intr_rx_high, so we do not loop forever. +		 */ + +		if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) { +			sio_ir &= ~hooks->intr_rx_high; +		} +	} while (sio_ir & hooks->intr_all); + +	spin_unlock_irqrestore(&port->ip_lock, flags); +	ioc3_enable(is, idd, card_ptr->ic_enable); +	return 0; +} + +/** + * ioc3uart_intr - field all serial interrupts + * @is : submodule + * @idd: driver data + * @pending: interrupts to handle + * + */ + +static int ioc3uart_intr(struct ioc3_submodule *is, +			struct ioc3_driver_data *idd, +			unsigned int pending) +{ +	int ret = 0; + +	/* +	 * The upper level interrupt handler sends interrupts for both ports +	 * here. So we need to call for each port with its interrupts. +	 */ + +	if (pending & SIO_IR_SA) +		ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SA); +	if (pending & SIO_IR_SB) +		ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SB); + +	return ret; +} + +/** + * ic3_type + * @port: Port to operate with (we ignore since we only have one port) + * + */ +static const char *ic3_type(struct uart_port *the_port) +{ +	if (IS_RS232(the_port->line)) +		return "SGI IOC3 Serial [rs232]"; +	else +		return "SGI IOC3 Serial [rs422]"; +} + +/** + * ic3_tx_empty - Is the transmitter empty? + * @port: Port to operate on + * + */ +static unsigned int ic3_tx_empty(struct uart_port *the_port) +{ +	unsigned int ret = 0; +	struct ioc3_port *port = get_ioc3_port(the_port); + +	if (readl(&port->ip_serial_regs->shadow) & SHADOW_TEMT) +		ret = TIOCSER_TEMT; +	return ret; +} + +/** + * ic3_stop_tx - stop the transmitter + * @port: Port to operate on + * + */ +static void ic3_stop_tx(struct uart_port *the_port) +{ +	struct ioc3_port *port = get_ioc3_port(the_port); + +	if (port) +		set_notification(port, N_OUTPUT_LOWAT, 0); +} + +/** + * ic3_stop_rx - stop the receiver + * @port: Port to operate on + * + */ +static void ic3_stop_rx(struct uart_port *the_port) +{ +	struct ioc3_port *port = get_ioc3_port(the_port); + +	if (port) +		port->ip_flags &= ~INPUT_ENABLE; +} + +/** + * null_void_function + * @port: Port to operate on + * + */ +static void null_void_function(struct uart_port *the_port) +{ +} + +/** + * ic3_shutdown - shut down the port - free irq and disable + * @port: port to shut down + * + */ +static void ic3_shutdown(struct uart_port *the_port) +{ +	unsigned long port_flags; +	struct ioc3_port *port; +	struct uart_state *state; + +	port = get_ioc3_port(the_port); +	if (!port) +		return; + +	state = the_port->state; +	wake_up_interruptible(&state->port.delta_msr_wait); + +	spin_lock_irqsave(&the_port->lock, port_flags); +	set_notification(port, N_ALL, 0); +	spin_unlock_irqrestore(&the_port->lock, port_flags); +} + +/** + * ic3_set_mctrl - set control lines (dtr, rts, etc) + * @port: Port to operate on + * @mctrl: Lines to set/unset + * + */ +static void ic3_set_mctrl(struct uart_port *the_port, unsigned int mctrl) +{ +	unsigned char mcr = 0; + +	if (mctrl & TIOCM_RTS) +		mcr |= UART_MCR_RTS; +	if (mctrl & TIOCM_DTR) +		mcr |= UART_MCR_DTR; +	if (mctrl & TIOCM_OUT1) +		mcr |= UART_MCR_OUT1; +	if (mctrl & TIOCM_OUT2) +		mcr |= UART_MCR_OUT2; +	if (mctrl & TIOCM_LOOP) +		mcr |= UART_MCR_LOOP; + +	set_mcr(the_port, mcr, SHADOW_DTR); +} + +/** + * ic3_get_mctrl - get control line info + * @port: port to operate on + * + */ +static unsigned int ic3_get_mctrl(struct uart_port *the_port) +{ +	struct ioc3_port *port = get_ioc3_port(the_port); +	uint32_t shadow; +	unsigned int ret = 0; + +	if (!port) +		return 0; + +	shadow = readl(&port->ip_serial_regs->shadow); +	if (shadow & SHADOW_DCD) +		ret |= TIOCM_CD; +	if (shadow & SHADOW_DR) +		ret |= TIOCM_DSR; +	if (shadow & SHADOW_CTS) +		ret |= TIOCM_CTS; +	return ret; +} + +/** + * ic3_start_tx - Start transmitter. Called with the_port->lock + * @port: Port to operate on + * + */ +static void ic3_start_tx(struct uart_port *the_port) +{ +	struct ioc3_port *port = get_ioc3_port(the_port); + +	if (port) { +		set_notification(port, N_OUTPUT_LOWAT, 1); +		enable_intrs(port, port->ip_hooks->intr_tx_mt); +	} +} + +/** + * ic3_break_ctl - handle breaks + * @port: Port to operate on + * @break_state: Break state + * + */ +static void ic3_break_ctl(struct uart_port *the_port, int break_state) +{ +} + +/** + * ic3_startup - Start up the serial port - always return 0 (We're always on) + * @port: Port to operate on + * + */ +static int ic3_startup(struct uart_port *the_port) +{ +	int retval; +	struct ioc3_port *port; +	struct ioc3_card *card_ptr; +	unsigned long port_flags; + +	if (!the_port) { +		NOT_PROGRESS(); +		return -ENODEV; +	} +	port = get_ioc3_port(the_port); +	if (!port) { +		NOT_PROGRESS(); +		return -ENODEV; +	} +	card_ptr = port->ip_card; +	port->ip_port = the_port; + +	if (!card_ptr) { +		NOT_PROGRESS(); +		return -ENODEV; +	} + +	/* Start up the serial port */ +	spin_lock_irqsave(&the_port->lock, port_flags); +	retval = ic3_startup_local(the_port); +	spin_unlock_irqrestore(&the_port->lock, port_flags); +	return retval; +} + +/** + * ic3_set_termios - set termios stuff + * @port: port to operate on + * @termios: New settings + * @termios: Old + * + */ +static void +ic3_set_termios(struct uart_port *the_port, +		struct ktermios *termios, struct ktermios *old_termios) +{ +	unsigned long port_flags; + +	spin_lock_irqsave(&the_port->lock, port_flags); +	ioc3_change_speed(the_port, termios, old_termios); +	spin_unlock_irqrestore(&the_port->lock, port_flags); +} + +/** + * ic3_request_port - allocate resources for port - no op.... + * @port: port to operate on + * + */ +static int ic3_request_port(struct uart_port *port) +{ +	return 0; +} + +/* Associate the uart functions above - given to serial core */ +static struct uart_ops ioc3_ops = { +	.tx_empty = ic3_tx_empty, +	.set_mctrl = ic3_set_mctrl, +	.get_mctrl = ic3_get_mctrl, +	.stop_tx = ic3_stop_tx, +	.start_tx = ic3_start_tx, +	.stop_rx = ic3_stop_rx, +	.enable_ms = null_void_function, +	.break_ctl = ic3_break_ctl, +	.startup = ic3_startup, +	.shutdown = ic3_shutdown, +	.set_termios = ic3_set_termios, +	.type = ic3_type, +	.release_port = null_void_function, +	.request_port = ic3_request_port, +}; + +/* + * Boot-time initialization code + */ + +static struct uart_driver ioc3_uart = { +	.owner = THIS_MODULE, +	.driver_name = "ioc3_serial", +	.dev_name = DEVICE_NAME, +	.major = DEVICE_MAJOR, +	.minor = DEVICE_MINOR, +	.nr = MAX_LOGICAL_PORTS +}; + +/** + * ioc3_serial_core_attach - register with serial core + *		This is done during pci probing + * @is: submodule struct for this + * @idd: handle for this card + */ +static inline int ioc3_serial_core_attach( struct ioc3_submodule *is, +				struct ioc3_driver_data *idd) +{ +	struct ioc3_port *port; +	struct uart_port *the_port; +	struct ioc3_card *card_ptr = idd->data[is->id]; +	int ii, phys_port; +	struct pci_dev *pdev = idd->pdev; + +	DPRINT_CONFIG(("%s: attach pdev 0x%p - card_ptr 0x%p\n", +		       __func__, pdev, (void *)card_ptr)); + +	if (!card_ptr) +		return -ENODEV; + +	/* once around for each logical port on this card */ +	for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) { +		phys_port = GET_PHYSICAL_PORT(ii); +		the_port = &card_ptr->ic_port[phys_port]. +				icp_uart_port[GET_LOGICAL_PORT(ii)]; +		port = card_ptr->ic_port[phys_port].icp_port; +		port->ip_port = the_port; + +		DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p [%d/%d]\n", +			__func__, (void *)the_port, (void *)port, +				phys_port, ii)); + +		/* membase, iobase and mapbase just need to be non-0 */ +		the_port->membase = (unsigned char __iomem *)1; +		the_port->iobase = (pdev->bus->number << 16) |  ii; +		the_port->line = (Num_of_ioc3_cards << 2) | ii; +		the_port->mapbase = 1; +		the_port->type = PORT_16550A; +		the_port->fifosize = FIFO_SIZE; +		the_port->ops = &ioc3_ops; +		the_port->irq = idd->irq_io; +		the_port->dev = &pdev->dev; + +		if (uart_add_one_port(&ioc3_uart, the_port) < 0) { +			printk(KERN_WARNING +		          "%s: unable to add port %d bus %d\n", +			       __func__, the_port->line, pdev->bus->number); +		} else { +			DPRINT_CONFIG(("IOC3 serial port %d irq %d bus %d\n", +		          the_port->line, the_port->irq, pdev->bus->number)); +		} + +		/* all ports are rs232 for now */ +		if (IS_PHYSICAL_PORT(ii)) +			ioc3_set_proto(port, PROTO_RS232); +	} +	return 0; +} + +/** + * ioc3uart_remove - register detach function + * @is: submodule struct for this submodule + * @idd: ioc3 driver data for this submodule + */ + +static int ioc3uart_remove(struct ioc3_submodule *is, +			struct ioc3_driver_data *idd) +{ +	struct ioc3_card *card_ptr = idd->data[is->id]; +	struct uart_port *the_port; +	struct ioc3_port *port; +	int ii; + +	if (card_ptr) { +		for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) { +			the_port = &card_ptr->ic_port[GET_PHYSICAL_PORT(ii)]. +					icp_uart_port[GET_LOGICAL_PORT(ii)]; +			if (the_port) +				uart_remove_one_port(&ioc3_uart, the_port); +			port = card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].icp_port; +			if (port && IS_PHYSICAL_PORT(ii) +					&& (GET_PHYSICAL_PORT(ii) == 0)) { +				pci_free_consistent(port->ip_idd->pdev, +					TOTAL_RING_BUF_SIZE, +					(void *)port->ip_cpu_ringbuf, +					port->ip_dma_ringbuf); +				kfree(port); +				card_ptr->ic_port[GET_PHYSICAL_PORT(ii)]. +							icp_port = NULL; +			} +		} +		kfree(card_ptr); +		idd->data[is->id] = NULL; +	} +	return 0; +} + +/** + * ioc3uart_probe - card probe function called from shim driver + * @is: submodule struct for this submodule + * @idd: ioc3 driver data for this card + */ + +static int __devinit +ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) +{ +	struct pci_dev *pdev = idd->pdev; +	struct ioc3_card *card_ptr; +	int ret = 0; +	struct ioc3_port *port; +	struct ioc3_port *ports[PORTS_PER_CARD]; +	int phys_port; +	int cnt; + +	DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, is, idd)); + +	card_ptr = kzalloc(sizeof(struct ioc3_card), GFP_KERNEL); +	if (!card_ptr) { +		printk(KERN_WARNING "ioc3_attach_one" +		       ": unable to get memory for the IOC3\n"); +		return -ENOMEM; +	} +	idd->data[is->id] = card_ptr; +	Submodule_slot = is->id; + +	writel(((UARTA_BASE >> 3) << SIO_CR_SER_A_BASE_SHIFT) | +		((UARTB_BASE >> 3) << SIO_CR_SER_B_BASE_SHIFT) | +		(0xf << SIO_CR_CMD_PULSE_SHIFT), &idd->vma->sio_cr); + +	pci_write_config_dword(pdev, PCI_LAT, 0xff00); + +	/* Enable serial port mode select generic PIO pins as outputs */ +	ioc3_gpcr_set(idd, GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL); + +	/* Create port structures for each port */ +	for (phys_port = 0; phys_port < PORTS_PER_CARD; phys_port++) { +		port = kzalloc(sizeof(struct ioc3_port), GFP_KERNEL); +		if (!port) { +			printk(KERN_WARNING +			       "IOC3 serial memory not available for port\n"); +			ret = -ENOMEM; +			goto out4; +		} +		spin_lock_init(&port->ip_lock); + +		/* we need to remember the previous ones, to point back to +		 * them farther down - setting up the ring buffers. +		 */ +		ports[phys_port] = port; + +		/* init to something useful */ +		card_ptr->ic_port[phys_port].icp_port = port; +		port->ip_is = is; +		port->ip_idd = idd; +		port->ip_baud = 9600; +		port->ip_card = card_ptr; +		port->ip_hooks = &hooks_array[phys_port]; + +		/* Setup each port */ +		if (phys_port == 0) { +			port->ip_serial_regs = &idd->vma->port_a; +			port->ip_uart_regs = &idd->vma->sregs.uarta; + +			DPRINT_CONFIG(("%s : Port A ip_serial_regs 0x%p " +				       "ip_uart_regs 0x%p\n", +				       __func__, +				       (void *)port->ip_serial_regs, +				       (void *)port->ip_uart_regs)); + +			/* setup ring buffers */ +			port->ip_cpu_ringbuf = pci_alloc_consistent(pdev, +				TOTAL_RING_BUF_SIZE, &port->ip_dma_ringbuf); + +			BUG_ON(!((((int64_t) port->ip_dma_ringbuf) & +				  (TOTAL_RING_BUF_SIZE - 1)) == 0)); +			port->ip_inring = RING(port, RX_A); +			port->ip_outring = RING(port, TX_A); +			DPRINT_CONFIG(("%s : Port A ip_cpu_ringbuf 0x%p " +				       "ip_dma_ringbuf 0x%p, ip_inring 0x%p " +					"ip_outring 0x%p\n", +				       __func__, +				       (void *)port->ip_cpu_ringbuf, +				       (void *)port->ip_dma_ringbuf, +				       (void *)port->ip_inring, +				       (void *)port->ip_outring)); +		} +		else { +			port->ip_serial_regs = &idd->vma->port_b; +			port->ip_uart_regs = &idd->vma->sregs.uartb; + +			DPRINT_CONFIG(("%s : Port B ip_serial_regs 0x%p " +				       "ip_uart_regs 0x%p\n", +				       __func__, +				       (void *)port->ip_serial_regs, +				       (void *)port->ip_uart_regs)); + +			/* share the ring buffers */ +			port->ip_dma_ringbuf = +			    ports[phys_port - 1]->ip_dma_ringbuf; +			port->ip_cpu_ringbuf = +			    ports[phys_port - 1]->ip_cpu_ringbuf; +			port->ip_inring = RING(port, RX_B); +			port->ip_outring = RING(port, TX_B); +			DPRINT_CONFIG(("%s : Port B ip_cpu_ringbuf 0x%p " +				       "ip_dma_ringbuf 0x%p, ip_inring 0x%p " +					"ip_outring 0x%p\n", +				       __func__, +				       (void *)port->ip_cpu_ringbuf, +				       (void *)port->ip_dma_ringbuf, +				       (void *)port->ip_inring, +				       (void *)port->ip_outring)); +		} + +		DPRINT_CONFIG(("%s : port %d [addr 0x%p] card_ptr 0x%p", +			       __func__, +			       phys_port, (void *)port, (void *)card_ptr)); +		DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", +			       (void *)port->ip_serial_regs, +			       (void *)port->ip_uart_regs)); + +		/* Initialize the hardware for IOC3 */ +		port_init(port); + +		DPRINT_CONFIG(("%s: phys_port %d port 0x%p inring 0x%p " +			       "outring 0x%p\n", +			       __func__, +			       phys_port, (void *)port, +			       (void *)port->ip_inring, +			       (void *)port->ip_outring)); + +	} + +	/* register port with the serial core */ + +	if ((ret = ioc3_serial_core_attach(is, idd))) +		goto out4; + +	Num_of_ioc3_cards++; + +	return ret; + +	/* error exits that give back resources */ +out4: +	for (cnt = 0; cnt < phys_port; cnt++) +		kfree(ports[cnt]); + +	kfree(card_ptr); +	return ret; +} + +static struct ioc3_submodule ioc3uart_ops = { +	.name = "IOC3uart", +	.probe = ioc3uart_probe, +	.remove = ioc3uart_remove, +	/* call .intr for both ports initially */ +	.irq_mask = SIO_IR_SA | SIO_IR_SB, +	.intr = ioc3uart_intr, +	.owner = THIS_MODULE, +}; + +/** + * ioc3_detect - module init called, + */ +static int __init ioc3uart_init(void) +{ +	int ret; + +	/* register with serial core */ +	if ((ret = uart_register_driver(&ioc3_uart)) < 0) { +		printk(KERN_WARNING +		       "%s: Couldn't register IOC3 uart serial driver\n", +		       __func__); +		return ret; +	} +	ret = ioc3_register_submodule(&ioc3uart_ops); +	if (ret) +		uart_unregister_driver(&ioc3_uart); +	return ret; +} + +static void __exit ioc3uart_exit(void) +{ +	ioc3_unregister_submodule(&ioc3uart_ops); +	uart_unregister_driver(&ioc3_uart); +} + +module_init(ioc3uart_init); +module_exit(ioc3uart_exit); + +MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>"); +MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC3 card"); +MODULE_LICENSE("GPL");  |