diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/serial/ioc4_serial.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/serial/ioc4_serial.c')
-rw-r--r-- | drivers/serial/ioc4_serial.c | 2909 |
1 files changed, 2909 insertions, 0 deletions
diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c new file mode 100644 index 00000000000..d054f126570 --- /dev/null +++ b/drivers/serial/ioc4_serial.c @@ -0,0 +1,2909 @@ +/* + * 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) 2003-2005 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * This file contains a module version of the ioc4 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/serialP.h> +#include <linux/circ_buf.h> +#include <linux/serial_reg.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/ioc4_common.h> +#include <linux/serial_core.h> + +/* + * interesting things about the ioc4 + */ + +#define IOC4_NUM_SERIAL_PORTS 4 /* max ports per card */ +#define IOC4_NUM_CARDS 8 /* max cards per partition */ + +#define GET_SIO_IR(_n) (_n == 0) ? (IOC4_SIO_IR_S0) : \ + (_n == 1) ? (IOC4_SIO_IR_S1) : \ + (_n == 2) ? (IOC4_SIO_IR_S2) : \ + (IOC4_SIO_IR_S3) + +#define GET_OTHER_IR(_n) (_n == 0) ? (IOC4_OTHER_IR_S0_MEMERR) : \ + (_n == 1) ? (IOC4_OTHER_IR_S1_MEMERR) : \ + (_n == 2) ? (IOC4_OTHER_IR_S2_MEMERR) : \ + (IOC4_OTHER_IR_S3_MEMERR) + + +/* + * All IOC4 registers are 32 bits wide. + */ + +/* + * PCI Memory Space Map + */ +#define IOC4_PCI_ERR_ADDR_L 0x000 /* Low Error Address */ +#define IOC4_PCI_ERR_ADDR_VLD (0x1 << 0) +#define IOC4_PCI_ERR_ADDR_MST_ID_MSK (0xf << 1) +#define IOC4_PCI_ERR_ADDR_MST_NUM_MSK (0xe << 1) +#define IOC4_PCI_ERR_ADDR_MST_TYP_MSK (0x1 << 1) +#define IOC4_PCI_ERR_ADDR_MUL_ERR (0x1 << 5) +#define IOC4_PCI_ERR_ADDR_ADDR_MSK (0x3ffffff << 6) + +/* Interrupt types */ +#define IOC4_SIO_INTR_TYPE 0 +#define IOC4_OTHER_INTR_TYPE 1 +#define IOC4_NUM_INTR_TYPES 2 + +/* Bitmasks for IOC4_SIO_IR, IOC4_SIO_IEC, and IOC4_SIO_IES */ +#define IOC4_SIO_IR_S0_TX_MT 0x00000001 /* Serial port 0 TX empty */ +#define IOC4_SIO_IR_S0_RX_FULL 0x00000002 /* Port 0 RX buf full */ +#define IOC4_SIO_IR_S0_RX_HIGH 0x00000004 /* Port 0 RX hiwat */ +#define IOC4_SIO_IR_S0_RX_TIMER 0x00000008 /* Port 0 RX timeout */ +#define IOC4_SIO_IR_S0_DELTA_DCD 0x00000010 /* Port 0 delta DCD */ +#define IOC4_SIO_IR_S0_DELTA_CTS 0x00000020 /* Port 0 delta CTS */ +#define IOC4_SIO_IR_S0_INT 0x00000040 /* Port 0 pass-thru intr */ +#define IOC4_SIO_IR_S0_TX_EXPLICIT 0x00000080 /* Port 0 explicit TX thru */ +#define IOC4_SIO_IR_S1_TX_MT 0x00000100 /* Serial port 1 */ +#define IOC4_SIO_IR_S1_RX_FULL 0x00000200 /* */ +#define IOC4_SIO_IR_S1_RX_HIGH 0x00000400 /* */ +#define IOC4_SIO_IR_S1_RX_TIMER 0x00000800 /* */ +#define IOC4_SIO_IR_S1_DELTA_DCD 0x00001000 /* */ +#define IOC4_SIO_IR_S1_DELTA_CTS 0x00002000 /* */ +#define IOC4_SIO_IR_S1_INT 0x00004000 /* */ +#define IOC4_SIO_IR_S1_TX_EXPLICIT 0x00008000 /* */ +#define IOC4_SIO_IR_S2_TX_MT 0x00010000 /* Serial port 2 */ +#define IOC4_SIO_IR_S2_RX_FULL 0x00020000 /* */ +#define IOC4_SIO_IR_S2_RX_HIGH 0x00040000 /* */ +#define IOC4_SIO_IR_S2_RX_TIMER 0x00080000 /* */ +#define IOC4_SIO_IR_S2_DELTA_DCD 0x00100000 /* */ +#define IOC4_SIO_IR_S2_DELTA_CTS 0x00200000 /* */ +#define IOC4_SIO_IR_S2_INT 0x00400000 /* */ +#define IOC4_SIO_IR_S2_TX_EXPLICIT 0x00800000 /* */ +#define IOC4_SIO_IR_S3_TX_MT 0x01000000 /* Serial port 3 */ +#define IOC4_SIO_IR_S3_RX_FULL 0x02000000 /* */ +#define IOC4_SIO_IR_S3_RX_HIGH 0x04000000 /* */ +#define IOC4_SIO_IR_S3_RX_TIMER 0x08000000 /* */ +#define IOC4_SIO_IR_S3_DELTA_DCD 0x10000000 /* */ +#define IOC4_SIO_IR_S3_DELTA_CTS 0x20000000 /* */ +#define IOC4_SIO_IR_S3_INT 0x40000000 /* */ +#define IOC4_SIO_IR_S3_TX_EXPLICIT 0x80000000 /* */ + +/* Per device interrupt masks */ +#define IOC4_SIO_IR_S0 (IOC4_SIO_IR_S0_TX_MT | \ + IOC4_SIO_IR_S0_RX_FULL | \ + IOC4_SIO_IR_S0_RX_HIGH | \ + IOC4_SIO_IR_S0_RX_TIMER | \ + IOC4_SIO_IR_S0_DELTA_DCD | \ + IOC4_SIO_IR_S0_DELTA_CTS | \ + IOC4_SIO_IR_S0_INT | \ + IOC4_SIO_IR_S0_TX_EXPLICIT) +#define IOC4_SIO_IR_S1 (IOC4_SIO_IR_S1_TX_MT | \ + IOC4_SIO_IR_S1_RX_FULL | \ + IOC4_SIO_IR_S1_RX_HIGH | \ + IOC4_SIO_IR_S1_RX_TIMER | \ + IOC4_SIO_IR_S1_DELTA_DCD | \ + IOC4_SIO_IR_S1_DELTA_CTS | \ + IOC4_SIO_IR_S1_INT | \ + IOC4_SIO_IR_S1_TX_EXPLICIT) +#define IOC4_SIO_IR_S2 (IOC4_SIO_IR_S2_TX_MT | \ + IOC4_SIO_IR_S2_RX_FULL | \ + IOC4_SIO_IR_S2_RX_HIGH | \ + IOC4_SIO_IR_S2_RX_TIMER | \ + IOC4_SIO_IR_S2_DELTA_DCD | \ + IOC4_SIO_IR_S2_DELTA_CTS | \ + IOC4_SIO_IR_S2_INT | \ + IOC4_SIO_IR_S2_TX_EXPLICIT) +#define IOC4_SIO_IR_S3 (IOC4_SIO_IR_S3_TX_MT | \ + IOC4_SIO_IR_S3_RX_FULL | \ + IOC4_SIO_IR_S3_RX_HIGH | \ + IOC4_SIO_IR_S3_RX_TIMER | \ + IOC4_SIO_IR_S3_DELTA_DCD | \ + IOC4_SIO_IR_S3_DELTA_CTS | \ + IOC4_SIO_IR_S3_INT | \ + IOC4_SIO_IR_S3_TX_EXPLICIT) + +/* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES */ +#define IOC4_OTHER_IR_ATA_INT 0x00000001 /* ATAPI intr pass-thru */ +#define IOC4_OTHER_IR_ATA_MEMERR 0x00000002 /* ATAPI DMA PCI error */ +#define IOC4_OTHER_IR_S0_MEMERR 0x00000004 /* Port 0 PCI error */ +#define IOC4_OTHER_IR_S1_MEMERR 0x00000008 /* Port 1 PCI error */ +#define IOC4_OTHER_IR_S2_MEMERR 0x00000010 /* Port 2 PCI error */ +#define IOC4_OTHER_IR_S3_MEMERR 0x00000020 /* Port 3 PCI error */ + +/* Bitmasks for IOC4_SIO_CR */ +#define IOC4_SIO_CR_CMD_PULSE_SHIFT 0 /* byte bus strobe shift */ +#define IOC4_SIO_CR_ARB_DIAG_TX0 0x00000000 +#define IOC4_SIO_CR_ARB_DIAG_RX0 0x00000010 +#define IOC4_SIO_CR_ARB_DIAG_TX1 0x00000020 +#define IOC4_SIO_CR_ARB_DIAG_RX1 0x00000030 +#define IOC4_SIO_CR_ARB_DIAG_TX2 0x00000040 +#define IOC4_SIO_CR_ARB_DIAG_RX2 0x00000050 +#define IOC4_SIO_CR_ARB_DIAG_TX3 0x00000060 +#define IOC4_SIO_CR_ARB_DIAG_RX3 0x00000070 +#define IOC4_SIO_CR_SIO_DIAG_IDLE 0x00000080 /* 0 -> active request among + serial ports (ro) */ +/* Defs for some of the generic I/O pins */ +#define IOC4_GPCR_UART0_MODESEL 0x10 /* Pin is output to port 0 + mode sel */ +#define IOC4_GPCR_UART1_MODESEL 0x20 /* Pin is output to port 1 + mode sel */ +#define IOC4_GPCR_UART2_MODESEL 0x40 /* Pin is output to port 2 + mode sel */ +#define IOC4_GPCR_UART3_MODESEL 0x80 /* Pin is output to port 3 + mode sel */ + +#define IOC4_GPPR_UART0_MODESEL_PIN 4 /* GIO pin controlling + uart 0 mode select */ +#define IOC4_GPPR_UART1_MODESEL_PIN 5 /* GIO pin controlling + uart 1 mode select */ +#define IOC4_GPPR_UART2_MODESEL_PIN 6 /* GIO pin controlling + uart 2 mode select */ +#define IOC4_GPPR_UART3_MODESEL_PIN 7 /* GIO pin controlling + uart 3 mode select */ + +/* Bitmasks for serial RX status byte */ +#define IOC4_RXSB_OVERRUN 0x01 /* Char(s) lost */ +#define IOC4_RXSB_PAR_ERR 0x02 /* Parity error */ +#define IOC4_RXSB_FRAME_ERR 0x04 /* Framing error */ +#define IOC4_RXSB_BREAK 0x08 /* Break character */ +#define IOC4_RXSB_CTS 0x10 /* State of CTS */ +#define IOC4_RXSB_DCD 0x20 /* State of DCD */ +#define IOC4_RXSB_MODEM_VALID 0x40 /* DCD, CTS, and OVERRUN are valid */ +#define IOC4_RXSB_DATA_VALID 0x80 /* Data byte, FRAME_ERR PAR_ERR + * & BREAK valid */ + +/* Bitmasks for serial TX control byte */ +#define IOC4_TXCB_INT_WHEN_DONE 0x20 /* Interrupt after this byte is sent */ +#define IOC4_TXCB_INVALID 0x00 /* Byte is invalid */ +#define IOC4_TXCB_VALID 0x40 /* Byte is valid */ +#define IOC4_TXCB_MCR 0x80 /* Data<7:0> to modem control reg */ +#define IOC4_TXCB_DELAY 0xc0 /* Delay data<7:0> mSec */ + +/* Bitmasks for IOC4_SBBR_L */ +#define IOC4_SBBR_L_SIZE 0x00000001 /* 0 == 1KB rings, 1 == 4KB rings */ + +/* Bitmasks for IOC4_SSCR_<3:0> */ +#define IOC4_SSCR_RX_THRESHOLD 0x000001ff /* Hiwater mark */ +#define IOC4_SSCR_TX_TIMER_BUSY 0x00010000 /* TX timer in progress */ +#define IOC4_SSCR_HFC_EN 0x00020000 /* Hardware flow control enabled */ +#define IOC4_SSCR_RX_RING_DCD 0x00040000 /* Post RX record on delta-DCD */ +#define IOC4_SSCR_RX_RING_CTS 0x00080000 /* Post RX record on delta-CTS */ +#define IOC4_SSCR_DIAG 0x00200000 /* Bypass clock divider for sim */ +#define IOC4_SSCR_RX_DRAIN 0x08000000 /* Drain RX buffer to memory */ +#define IOC4_SSCR_DMA_EN 0x10000000 /* Enable ring buffer DMA */ +#define IOC4_SSCR_DMA_PAUSE 0x20000000 /* Pause DMA */ +#define IOC4_SSCR_PAUSE_STATE 0x40000000 /* Sets when PAUSE takes effect */ +#define IOC4_SSCR_RESET 0x80000000 /* Reset DMA channels */ + +/* All producer/comsumer pointers are the same bitfield */ +#define IOC4_PROD_CONS_PTR_4K 0x00000ff8 /* For 4K buffers */ +#define IOC4_PROD_CONS_PTR_1K 0x000003f8 /* For 1K buffers */ +#define IOC4_PROD_CONS_PTR_OFF 3 + +/* Bitmasks for IOC4_SRCIR_<3:0> */ +#define IOC4_SRCIR_ARM 0x80000000 /* Arm RX timer */ + +/* Bitmasks for IOC4_SHADOW_<3:0> */ +#define IOC4_SHADOW_DR 0x00000001 /* Data ready */ +#define IOC4_SHADOW_OE 0x00000002 /* Overrun error */ +#define IOC4_SHADOW_PE 0x00000004 /* Parity error */ +#define IOC4_SHADOW_FE 0x00000008 /* Framing error */ +#define IOC4_SHADOW_BI 0x00000010 /* Break interrupt */ +#define IOC4_SHADOW_THRE 0x00000020 /* Xmit holding register empty */ +#define IOC4_SHADOW_TEMT 0x00000040 /* Xmit shift register empty */ +#define IOC4_SHADOW_RFCE 0x00000080 /* Char in RX fifo has an error */ +#define IOC4_SHADOW_DCTS 0x00010000 /* Delta clear to send */ +#define IOC4_SHADOW_DDCD 0x00080000 /* Delta data carrier detect */ +#define IOC4_SHADOW_CTS 0x00100000 /* Clear to send */ +#define IOC4_SHADOW_DCD 0x00800000 /* Data carrier detect */ +#define IOC4_SHADOW_DTR 0x01000000 /* Data terminal ready */ +#define IOC4_SHADOW_RTS 0x02000000 /* Request to send */ +#define IOC4_SHADOW_OUT1 0x04000000 /* 16550 OUT1 bit */ +#define IOC4_SHADOW_OUT2 0x08000000 /* 16550 OUT2 bit */ +#define IOC4_SHADOW_LOOP 0x10000000 /* Loopback enabled */ + +/* Bitmasks for IOC4_SRTR_<3:0> */ +#define IOC4_SRTR_CNT 0x00000fff /* Reload value for RX timer */ +#define IOC4_SRTR_CNT_VAL 0x0fff0000 /* Current value of RX timer */ +#define IOC4_SRTR_CNT_VAL_SHIFT 16 +#define IOC4_SRTR_HZ 16000 /* SRTR clock frequency */ + +/* Serial port register map used for DMA and PIO serial I/O */ +struct ioc4_serialregs { + uint32_t sscr; + uint32_t stpir; + uint32_t stcir; + uint32_t srpir; + uint32_t srcir; + uint32_t srtr; + uint32_t shadow; +}; + +/* IOC4 UART register map */ +struct ioc4_uartregs { + char i4u_lcr; + union { + char iir; /* read only */ + char fcr; /* write only */ + } u3; + union { + char ier; /* DLAB == 0 */ + char dlm; /* DLAB == 1 */ + } u2; + union { + char rbr; /* read only, DLAB == 0 */ + char thr; /* write only, DLAB == 0 */ + char dll; /* DLAB == 1 */ + } u1; + char i4u_scr; + char i4u_msr; + char i4u_lsr; + char i4u_mcr; +}; + +/* short names */ +#define i4u_dll u1.dll +#define i4u_ier u2.ier +#define i4u_dlm u2.dlm +#define i4u_fcr u3.fcr + +/* PCI memory space register map addressed using pci_bar0 */ +struct ioc4_memregs { + struct ioc4_mem { + /* Miscellaneous IOC4 registers */ + uint32_t pci_err_addr_l; + uint32_t pci_err_addr_h; + uint32_t sio_ir; + uint32_t other_ir; + + /* These registers are read-only for general kernel code. */ + uint32_t sio_ies_ro; + uint32_t other_ies_ro; + uint32_t sio_iec_ro; + uint32_t other_iec_ro; + uint32_t sio_cr; + uint32_t misc_fill1; + uint32_t int_out; + uint32_t misc_fill2; + uint32_t gpcr_s; + uint32_t gpcr_c; + uint32_t gpdr; + uint32_t misc_fill3; + uint32_t gppr_0; + uint32_t gppr_1; + uint32_t gppr_2; + uint32_t gppr_3; + uint32_t gppr_4; + uint32_t gppr_5; + uint32_t gppr_6; + uint32_t gppr_7; + } ioc4_mem; + + char misc_fill4[0x100 - 0x5C - 4]; + + /* ATA/ATAP registers */ + uint32_t ata_notused[9]; + char ata_fill1[0x140 - 0x120 - 4]; + uint32_t ata_notused1[8]; + char ata_fill2[0x200 - 0x15C - 4]; + + /* Keyboard and mouse registers */ + uint32_t km_notused[5];; + char km_fill1[0x300 - 0x210 - 4]; + + /* Serial port registers used for DMA serial I/O */ + struct ioc4_serial { + uint32_t sbbr01_l; + uint32_t sbbr01_h; + uint32_t sbbr23_l; + uint32_t sbbr23_h; + + struct ioc4_serialregs port_0; + struct ioc4_serialregs port_1; + struct ioc4_serialregs port_2; + struct ioc4_serialregs port_3; + struct ioc4_uartregs uart_0; + struct ioc4_uartregs uart_1; + struct ioc4_uartregs uart_2; + struct ioc4_uartregs uart_3; + } ioc4_serial; +}; + +/* UART clock speed */ +#define IOC4_SER_XIN_CLK IOC4_SER_XIN_CLK_66 +#define IOC4_SER_XIN_CLK_66 66666667 +#define IOC4_SER_XIN_CLK_33 33333333 + +#define IOC4_W_IES 0 +#define IOC4_W_IEC 1 + +typedef void ioc4_intr_func_f(void *, uint32_t); +typedef ioc4_intr_func_f *ioc4_intr_func_t; + +/* defining this will get you LOTS of great debug info */ +//#define DEBUG_INTERRUPTS +#define DPRINT_CONFIG(_x...) ; +//#define DPRINT_CONFIG(_x...) printk _x + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* number of characters we want to transmit to the lower level at a time */ +#define IOC4_MAX_CHARS 128 + +/* Device name we're using */ +#define DEVICE_NAME "ttyIOC" +#define DEVICE_MAJOR 204 +#define DEVICE_MINOR 50 + +/* register offsets */ +#define IOC4_SERIAL_OFFSET 0x300 + +/* 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 */ +enum sio_proto { + PROTO_RS232, + PROTO_RS422 +}; + +/* 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_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(_p) (readl(&(_p)->ip_mem->sio_ir) & _p->ip_ienb) +#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir) + +/* Default to 4k buffers */ +#ifdef IOC4_1K_BUFFERS +#define RING_BUF_SIZE 1024 +#define IOC4_BUF_SIZE_BIT 0 +#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_1K +#else +#define RING_BUF_SIZE 4096 +#define IOC4_BUF_SIZE_BIT IOC4_SBBR_L_SIZE +#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_4K +#endif + +#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4) + +/* + * This is the entry saved by the driver - one per card + */ +struct ioc4_control { + int ic_irq; + struct { + /* uart ports are allocated here */ + struct uart_port icp_uart_port; + /* Handy reference material */ + struct ioc4_port *icp_port; + } ic_port[IOC4_NUM_SERIAL_PORTS]; + struct ioc4_soft *ic_soft; +}; + +/* + * per-IOC4 data structure + */ +#define MAX_IOC4_INTR_ENTS (8 * sizeof(uint32_t)) +struct ioc4_soft { + struct ioc4_mem __iomem *is_ioc4_mem_addr; + struct ioc4_serial __iomem *is_ioc4_serial_addr; + + /* Each interrupt type has an entry in the array */ + struct ioc4_intr_type { + + /* + * Each in-use entry in this array contains at least + * one nonzero bit in sd_bits; no two entries in this + * array have overlapping sd_bits values. + */ + struct ioc4_intr_info { + uint32_t sd_bits; + ioc4_intr_func_f *sd_intr; + void *sd_info; + } is_intr_info[MAX_IOC4_INTR_ENTS]; + + /* Number of entries active in the above array */ + atomic_t is_num_intrs; + } is_intr_type[IOC4_NUM_INTR_TYPES]; + + /* is_ir_lock must be held while + * modifying sio_ie values, so + * we can be sure that sio_ie is + * not changing when we read it + * along with sio_ir. + */ + spinlock_t is_ir_lock; /* SIO_IE[SC] mod lock */ +}; + +/* Local port info for each IOC4 serial ports */ +struct ioc4_port { + struct uart_port *ip_port; + /* Back ptrs for this port */ + struct ioc4_control *ip_control; + struct pci_dev *ip_pdev; + struct ioc4_soft *ip_ioc4_soft; + + /* pci mem addresses */ + struct ioc4_mem __iomem *ip_mem; + struct ioc4_serial __iomem *ip_serial; + struct ioc4_serialregs __iomem *ip_serial_regs; + struct ioc4_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 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_ienb; /* Enabled interrupts */ + uint32_t ip_sscr; + uint32_t ip_tx_prod; + uint32_t ip_rx_cons; + int ip_pci_bus_speed; + 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 +#define DCD_ON 0x02 +#define LOWAT_WRITTEN 0x04 +#define READ_ABORTED 0x08 + +/* 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 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_dma_error; + uint32_t intr_clear; + uint32_t intr_all; + char rs422_select_pin; +}; + +static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = { + /* Values for port 0 */ + { + IOC4_SIO_IR_S0_DELTA_DCD, IOC4_SIO_IR_S0_DELTA_CTS, + IOC4_SIO_IR_S0_TX_MT, IOC4_SIO_IR_S0_RX_TIMER, + IOC4_SIO_IR_S0_RX_HIGH, IOC4_SIO_IR_S0_TX_EXPLICIT, + IOC4_OTHER_IR_S0_MEMERR, + (IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL | + IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER | + IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS | + IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT), + IOC4_SIO_IR_S0, IOC4_GPPR_UART0_MODESEL_PIN, + }, + + /* Values for port 1 */ + { + IOC4_SIO_IR_S1_DELTA_DCD, IOC4_SIO_IR_S1_DELTA_CTS, + IOC4_SIO_IR_S1_TX_MT, IOC4_SIO_IR_S1_RX_TIMER, + IOC4_SIO_IR_S1_RX_HIGH, IOC4_SIO_IR_S1_TX_EXPLICIT, + IOC4_OTHER_IR_S1_MEMERR, + (IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL | + IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER | + IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS | + IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT), + IOC4_SIO_IR_S1, IOC4_GPPR_UART1_MODESEL_PIN, + }, + + /* Values for port 2 */ + { + IOC4_SIO_IR_S2_DELTA_DCD, IOC4_SIO_IR_S2_DELTA_CTS, + IOC4_SIO_IR_S2_TX_MT, IOC4_SIO_IR_S2_RX_TIMER, + IOC4_SIO_IR_S2_RX_HIGH, IOC4_SIO_IR_S2_TX_EXPLICIT, + IOC4_OTHER_IR_S2_MEMERR, + (IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL | + IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER | + IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS | + IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT), + IOC4_SIO_IR_S2, IOC4_GPPR_UART2_MODESEL_PIN, + }, + + /* Values for port 3 */ + { + IOC4_SIO_IR_S3_DELTA_DCD, IOC4_SIO_IR_S3_DELTA_CTS, + IOC4_SIO_IR_S3_TX_MT, IOC4_SIO_IR_S3_RX_TIMER, + IOC4_SIO_IR_S3_RX_HIGH, IOC4_SIO_IR_S3_TX_EXPLICIT, + IOC4_OTHER_IR_S3_MEMERR, + (IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL | + IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER | + IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS | + IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT), + IOC4_SIO_IR_S3, IOC4_GPPR_UART3_MODESEL_PIN, + } +}; + +/* A ring buffer entry */ +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)(IOC4_RXSB_MODEM_VALID | IOC4_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_0_OR_2; + struct ring RX_0_OR_2; + struct ring TX_1_OR_3; + struct ring RX_1_OR_3; +}; + +/* Get a ring from a port struct */ +#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh) + +/* Infinite loop detection. + */ +#define MAXITER 10000000 + +/* Prototypes */ +static void receive_chars(struct uart_port *); +static void handle_intr(void *arg, uint32_t sio_ir); + +/** + * write_ireg - write the interrupt regs + * @ioc4_soft: ptr to soft struct for this port + * @val: value to write + * @which: which register + * @type: which ireg set + */ +static inline void +write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type) +{ + struct ioc4_mem __iomem *mem = ioc4_soft->is_ioc4_mem_addr; + unsigned long flags; + + spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags); + + switch (type) { + case IOC4_SIO_INTR_TYPE: + switch (which) { + case IOC4_W_IES: + writel(val, &mem->sio_ies_ro); + break; + + case IOC4_W_IEC: + writel(val, &mem->sio_iec_ro); + break; + } + break; + + case IOC4_OTHER_INTR_TYPE: + switch (which) { + case IOC4_W_IES: + writel(val, &mem->other_ies_ro); + break; + + case IOC4_W_IEC: + writel(val, &mem->other_iec_ro); + break; + } + break; + + default: + break; + } + spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags); +} + +/** + * set_baud - Baud rate setting code + * @port: port to set + * @baud: baud rate to use + */ +static int set_baud(struct ioc4_port *port, int baud) +{ + int actual_baud; + int diff; + int lcr; + unsigned short divisor; + struct ioc4_uartregs __iomem *uart; + + divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed); + if (!divisor) + return 1; + actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed); + + diff = actual_baud - baud; + if (diff < 0) + diff = -diff; + + /* If we're within 1%, we've found a match */ + if (diff * 100 > actual_baud) + return 1; + + uart = port->ip_uart_regs; + lcr = readb(&uart->i4u_lcr); + writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr); + writeb((unsigned char)divisor, &uart->i4u_dll); + writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm); + writeb(lcr, &uart->i4u_lcr); + return 0; +} + + +/** + * get_ioc4_port - given a uart port, return the control structure + * @port: uart port + */ +static struct ioc4_port *get_ioc4_port(struct uart_port *the_port) +{ + struct ioc4_control *control = dev_get_drvdata(the_port->dev); + int ii; + + if (control) { + for ( ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++ ) { + if (!control->ic_port[ii].icp_port) + continue; + if (the_port == control->ic_port[ii].icp_port->ip_port) + return control->ic_port[ii].icp_port; + } + } + return NULL; +} + +/* The IOC4 hardware provides no atomic way to determine if interrupts + * are pending since two reads are required to do so. The handler must + * read the SIO_IR and the SIO_IES, and take the logical and of the + * two. When this value is zero, all interrupts have been serviced and + * the handler may return. + * + * This has the unfortunate "hole" that, if some other CPU or + * some other thread or some higher level interrupt manages to + * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may + * think we have observed SIO_IR&SIO_IE==0 when in fact this + * condition never really occurred. + * + * To solve this, we use a simple spinlock that must be held + * whenever modifying SIO_IE; holding this lock while observing + * both SIO_IR and SIO_IE guarantees that we do not falsely + * conclude that no enabled interrupts are pending. + */ + +static inline uint32_t +pending_intrs(struct ioc4_soft *soft, int type) +{ + struct ioc4_mem __iomem *mem = soft->is_ioc4_mem_addr; + unsigned long flag; + uint32_t intrs = 0; + + BUG_ON(!((type == IOC4_SIO_INTR_TYPE) + || (type == IOC4_OTHER_INTR_TYPE))); + + spin_lock_irqsave(&soft->is_ir_lock, flag); + + switch (type) { + case IOC4_SIO_INTR_TYPE: + intrs = readl(&mem->sio_ir) & readl(&mem->sio_ies_ro); + break; + + case IOC4_OTHER_INTR_TYPE: + intrs = readl(&mem->other_ir) & readl(&mem->other_ies_ro); + + /* Don't process any ATA interrupte */ + intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); + break; + + default: + break; + } + spin_unlock_irqrestore(&soft->is_ir_lock, flag); + return intrs; +} + +/** + * port_init - Initialize the sio and ioc4 hardware for a given port + * called per port from attach... + * @port: port to initialize + */ +static int inline port_init(struct ioc4_port *port) +{ + uint32_t sio_cr; + struct hooks *hooks = port->ip_hooks; + struct ioc4_uartregs __iomem *uart; + + /* Idle the IOC4 serial interface */ + writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr); + + /* Wait until any pending bus activity for this port has ceased */ + do + sio_cr = readl(&port->ip_mem->sio_cr); + while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE)); + + /* 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, &port->ip_serial_regs->srcir); + + /* Disable interrupts for this 16550 */ + uart = port->ip_uart_regs; + writeb(0, &uart->i4u_lcr); + writeb(0, &uart->i4u_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->i4u_lcr); + /* UART_LCR_STOP == 1 stop */ + + /* Enable the FIFOs */ + writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr); + /* then reset 16550 FIFOs */ + writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + &uart->i4u_fcr); + + /* Clear modem control register */ + writeb(0, &uart->i4u_mcr); + + /* Clear deltas in modem status register */ + readb(&uart->i4u_msr); + + /* Only do this once per port pair */ + if (port->ip_hooks == &hooks_array[0] + || port->ip_hooks == &hooks_array[2]) { + unsigned long ring_pci_addr; + uint32_t __iomem *sbbr_l; + uint32_t __iomem *sbbr_h; + + if (port->ip_hooks == &hooks_array[0]) { + sbbr_l = &port->ip_serial->sbbr01_l; + sbbr_h = &port->ip_serial->sbbr01_h; + } else { + sbbr_l = &port->ip_serial->sbbr23_l; + sbbr_h = &port->ip_serial->sbbr23_h; + } + + ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; + DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n", + __FUNCTION__, ring_pci_addr)); + + writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h); + writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l); + } + + /* Set the receive timeout value to 10 msec */ + writel(IOC4_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); + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + + /* Disable and clear all serial related interrupt bits */ + write_ireg(port->ip_ioc4_soft, hooks->intr_clear, + IOC4_W_IEC, IOC4_SIO_INTR_TYPE); + port->ip_ienb &= ~hooks->intr_clear; + writel(hooks->intr_clear, &port->ip_mem->sio_ir); + return 0; +} + +/** + * handle_dma_error_intr - service any pending DMA error interrupts for the + * given port - 2nd level called via sd_intr + * @arg: handler arg + * @other_ir: ioc4regs + */ +static void handle_dma_error_intr(void *arg, uint32_t other_ir) +{ + struct ioc4_port *port = (struct ioc4_port *)arg; + struct hooks *hooks = port->ip_hooks; + unsigned int flags; + + spin_lock_irqsave(&port->ip_lock, flags); + + /* ACK the interrupt */ + writel(hooks->intr_dma_error, &port->ip_mem->other_ir); + + if (readl(&port->ip_mem->pci_err_addr_l) & IOC4_PCI_ERR_ADDR_VLD) { + printk(KERN_ERR + "PCI error address is 0x%lx, " + "master is serial port %c %s\n", + (((uint64_t)readl(&port->ip_mem->pci_err_addr_h) + << 32) + | readl(&port->ip_mem->pci_err_addr_l)) + & IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' + + ((char)(readl(&port->ip_mem-> pci_err_addr_l) & + IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1), + (readl(&port->ip_mem->pci_err_addr_l) + & IOC4_PCI_ERR_ADDR_MST_TYP_MSK) + ? "RX" : "TX"); + + if (readl(&port->ip_mem->pci_err_addr_l) + & IOC4_PCI_ERR_ADDR_MUL_ERR) { + printk(KERN_ERR + "Multiple errors occurred\n"); + } + } + spin_unlock_irqrestore(&port->ip_lock, flags); + + /* Re-enable DMA error interrupts */ + write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, IOC4_W_IES, + IOC4_OTHER_INTR_TYPE); +} + +/** + * intr_connect - interrupt connect function + * @soft: soft struct for this card + * @type: interrupt type + * @intrbits: bit pattern to set + * @intr: handler function + * @info: handler arg + */ +static void +intr_connect(struct ioc4_soft *soft, int type, + uint32_t intrbits, ioc4_intr_func_f * intr, void *info) +{ + int i; + struct ioc4_intr_info *intr_ptr; + + BUG_ON(!((type == IOC4_SIO_INTR_TYPE) + || (type == IOC4_OTHER_INTR_TYPE))); + + i = atomic_inc(&soft-> is_intr_type[type].is_num_intrs) - 1; + BUG_ON(!(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0))); + + /* Save off the lower level interrupt handler */ + intr_ptr = &soft->is_intr_type[type].is_intr_info[i]; + intr_ptr->sd_bits = intrbits; + intr_ptr->sd_intr = intr; + intr_ptr->sd_info = info; +} + +/** + * ioc4_intr - Top level IOC4 interrupt handler. + * @irq: irq value + * @arg: handler arg + * @regs: registers + */ +static irqreturn_t ioc4_intr(int irq, void *arg, struct pt_regs *regs) +{ + struct ioc4_soft *soft; + uint32_t this_ir, this_mir; + int xx, num_intrs = 0; + int intr_type; + int handled = 0; + struct ioc4_intr_info *ii; + + soft = arg; + for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) { + num_intrs = (int)atomic_read( + &soft->is_intr_type[intr_type].is_num_intrs); + + this_mir = this_ir = pending_intrs(soft, intr_type); + + /* Farm out the interrupt to the various drivers depending on + * which interrupt bits are set. + */ + for (xx = 0; xx < num_intrs; xx++) { + ii = &soft->is_intr_type[intr_type].is_intr_info[xx]; + if ((this_mir = this_ir & ii->sd_bits)) { + /* Disable owned interrupts, call handler */ + handled++; + write_ireg(soft, ii->sd_bits, IOC4_W_IEC, + intr_type); + ii->sd_intr(ii->sd_info, this_mir); + this_ir &= ~this_mir; + } + } + if (this_ir) { + printk(KERN_ERR + "unknown IOC4 %s interrupt 0x%x, sio_ir = 0x%x," + " sio_ies = 0x%x, other_ir = 0x%x :" + "other_ies = 0x%x\n", + (intr_type == IOC4_SIO_INTR_TYPE) ? "sio" : + "other", this_ir, + readl(&soft->is_ioc4_mem_addr->sio_ir), + readl(&soft->is_ioc4_mem_addr->sio_ies_ro), + readl(&soft->is_ioc4_mem_addr->other_ir), + readl(&soft->is_ioc4_mem_addr->other_ies_ro)); + } + } +#ifdef DEBUG_INTERRUPTS + { + struct ioc4_mem __iomem *mem = soft->is_ioc4_mem_addr; + spinlock_t *lp = &soft->is_ir_lock; + unsigned long flag; + + spin_lock_irqsave(&soft->is_ir_lock, flag); + printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies_ro 0x%x " + "other_ir 0x%x other_ies_ro 0x%x mask 0x%x\n", + __FUNCTION__, __LINE__, + (void *)mem, readl(&mem->sio_ir), + readl(&mem->sio_ies_ro), + readl(&mem->other_ir), + readl(&mem->other_ies_ro), + IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); + spin_unlock_irqrestore(&soft->is_ir_lock, flag); + } +#endif + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +/** + * ioc4_attach_local - Device initialization. + * Called at *_attach() time for each + * IOC4 with serial ports in the system. + * @control: ioc4_control ptr + * @pdev: PCI handle for this device + * @soft: soft struct for this device + * @ioc4: ioc4 mem space + */ +static int inline ioc4_attach_local(struct pci_dev *pdev, + struct ioc4_control *control, + struct ioc4_soft *soft, void __iomem *ioc4_mem, + void __iomem *ioc4_serial) +{ + struct ioc4_port *port; + struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS]; + int port_number; + uint16_t ioc4_revid_min = 62; + uint16_t ioc4_revid; + + /* IOC4 firmware must be at least rev 62 */ + pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid); + + printk(K |