xref: /linux/drivers/tty/serial/icom.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2ab4382d2SGreg Kroah-Hartman /*
3ab4382d2SGreg Kroah-Hartman   * icom.c
4ab4382d2SGreg Kroah-Hartman   *
5ab4382d2SGreg Kroah-Hartman   * Copyright (C) 2001 IBM Corporation. All rights reserved.
6ab4382d2SGreg Kroah-Hartman   *
7ab4382d2SGreg Kroah-Hartman   * Serial device driver.
8ab4382d2SGreg Kroah-Hartman   *
9ab4382d2SGreg Kroah-Hartman   * Based on code from serial.c
10ab4382d2SGreg Kroah-Hartman   */
11ab4382d2SGreg Kroah-Hartman #include <linux/module.h>
12ab4382d2SGreg Kroah-Hartman #include <linux/kernel.h>
13ab4382d2SGreg Kroah-Hartman #include <linux/errno.h>
14ab4382d2SGreg Kroah-Hartman #include <linux/signal.h>
15ab4382d2SGreg Kroah-Hartman #include <linux/timer.h>
16ab4382d2SGreg Kroah-Hartman #include <linux/interrupt.h>
17ab4382d2SGreg Kroah-Hartman #include <linux/tty.h>
18ab4382d2SGreg Kroah-Hartman #include <linux/termios.h>
19ab4382d2SGreg Kroah-Hartman #include <linux/fs.h>
20ab4382d2SGreg Kroah-Hartman #include <linux/tty_flip.h>
21ab4382d2SGreg Kroah-Hartman #include <linux/serial.h>
2259a1d562SJiri Slaby #include <linux/serial_core.h>
23ab4382d2SGreg Kroah-Hartman #include <linux/serial_reg.h>
24ab4382d2SGreg Kroah-Hartman #include <linux/major.h>
25ab4382d2SGreg Kroah-Hartman #include <linux/string.h>
26ab4382d2SGreg Kroah-Hartman #include <linux/fcntl.h>
27ab4382d2SGreg Kroah-Hartman #include <linux/ptrace.h>
28ab4382d2SGreg Kroah-Hartman #include <linux/ioport.h>
29ab4382d2SGreg Kroah-Hartman #include <linux/mm.h>
30ab4382d2SGreg Kroah-Hartman #include <linux/slab.h>
31ab4382d2SGreg Kroah-Hartman #include <linux/init.h>
32ab4382d2SGreg Kroah-Hartman #include <linux/delay.h>
33ab4382d2SGreg Kroah-Hartman #include <linux/pci.h>
34ab4382d2SGreg Kroah-Hartman #include <linux/vmalloc.h>
35ab4382d2SGreg Kroah-Hartman #include <linux/smp.h>
36ab4382d2SGreg Kroah-Hartman #include <linux/spinlock.h>
37ab4382d2SGreg Kroah-Hartman #include <linux/kref.h>
38ab4382d2SGreg Kroah-Hartman #include <linux/firmware.h>
39ab4382d2SGreg Kroah-Hartman #include <linux/bitops.h>
40ab4382d2SGreg Kroah-Hartman 
410ebee1ebSZihao Tang #include <linux/io.h>
42ab4382d2SGreg Kroah-Hartman #include <asm/irq.h>
437c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
44ab4382d2SGreg Kroah-Hartman 
45ab4382d2SGreg Kroah-Hartman /*#define ICOM_TRACE		 enable port trace capabilities */
46ab4382d2SGreg Kroah-Hartman 
47ab4382d2SGreg Kroah-Hartman #define ICOM_DRIVER_NAME "icom"
48ab4382d2SGreg Kroah-Hartman #define NR_PORTS	       128
49ab4382d2SGreg Kroah-Hartman 
508b026d63SJiri Slaby static const unsigned int icom_acfg_baud[] = {
5159a1d562SJiri Slaby 	300,
5259a1d562SJiri Slaby 	600,
5359a1d562SJiri Slaby 	900,
5459a1d562SJiri Slaby 	1200,
5559a1d562SJiri Slaby 	1800,
5659a1d562SJiri Slaby 	2400,
5759a1d562SJiri Slaby 	3600,
5859a1d562SJiri Slaby 	4800,
5959a1d562SJiri Slaby 	7200,
6059a1d562SJiri Slaby 	9600,
6159a1d562SJiri Slaby 	14400,
6259a1d562SJiri Slaby 	19200,
6359a1d562SJiri Slaby 	28800,
6459a1d562SJiri Slaby 	38400,
6559a1d562SJiri Slaby 	57600,
6659a1d562SJiri Slaby 	76800,
6759a1d562SJiri Slaby 	115200,
6859a1d562SJiri Slaby 	153600,
6959a1d562SJiri Slaby 	230400,
7059a1d562SJiri Slaby 	307200,
7159a1d562SJiri Slaby 	460800,
7259a1d562SJiri Slaby };
7305ef2f3dSJiri Slaby #define BAUD_TABLE_LIMIT	(ARRAY_SIZE(icom_acfg_baud) - 1)
7459a1d562SJiri Slaby 
7559a1d562SJiri Slaby struct icom_regs {
7659a1d562SJiri Slaby 	u32 control;		/* Adapter Control Register     */
7759a1d562SJiri Slaby 	u32 interrupt;		/* Adapter Interrupt Register   */
7859a1d562SJiri Slaby 	u32 int_mask;		/* Adapter Interrupt Mask Reg   */
7959a1d562SJiri Slaby 	u32 int_pri;		/* Adapter Interrupt Priority r */
8059a1d562SJiri Slaby 	u32 int_reg_b;		/* Adapter non-masked Interrupt */
8159a1d562SJiri Slaby 	u32 resvd01;
8259a1d562SJiri Slaby 	u32 resvd02;
8359a1d562SJiri Slaby 	u32 resvd03;
8459a1d562SJiri Slaby 	u32 control_2;		/* Adapter Control Register 2   */
8559a1d562SJiri Slaby 	u32 interrupt_2;	/* Adapter Interrupt Register 2 */
8659a1d562SJiri Slaby 	u32 int_mask_2;		/* Adapter Interrupt Mask 2     */
8759a1d562SJiri Slaby 	u32 int_pri_2;		/* Adapter Interrupt Prior 2    */
8859a1d562SJiri Slaby 	u32 int_reg_2b;		/* Adapter non-masked 2         */
8959a1d562SJiri Slaby };
9059a1d562SJiri Slaby 
9159a1d562SJiri Slaby struct func_dram {
9259a1d562SJiri Slaby 	u32 reserved[108];	/* 0-1B0   reserved by personality code */
9359a1d562SJiri Slaby 	u32 RcvStatusAddr;	/* 1B0-1B3 Status Address for Next rcv */
9459a1d562SJiri Slaby 	u8 RcvStnAddr;		/* 1B4     Receive Station Addr */
9559a1d562SJiri Slaby 	u8 IdleState;		/* 1B5     Idle State */
9659a1d562SJiri Slaby 	u8 IdleMonitor;		/* 1B6     Idle Monitor */
9759a1d562SJiri Slaby 	u8 FlagFillIdleTimer;	/* 1B7     Flag Fill Idle Timer */
9859a1d562SJiri Slaby 	u32 XmitStatusAddr;	/* 1B8-1BB Transmit Status Address */
9959a1d562SJiri Slaby 	u8 StartXmitCmd;	/* 1BC     Start Xmit Command */
10059a1d562SJiri Slaby 	u8 HDLCConfigReg;	/* 1BD     Reserved */
10159a1d562SJiri Slaby 	u8 CauseCode;		/* 1BE     Cause code for fatal error */
10259a1d562SJiri Slaby 	u8 xchar;		/* 1BF     High priority send */
10359a1d562SJiri Slaby 	u32 reserved3;		/* 1C0-1C3 Reserved */
10459a1d562SJiri Slaby 	u8 PrevCmdReg;		/* 1C4     Reserved */
10559a1d562SJiri Slaby 	u8 CmdReg;		/* 1C5     Command Register */
10659a1d562SJiri Slaby 	u8 async_config2;	/* 1C6     Async Config Byte 2 */
10759a1d562SJiri Slaby 	u8 async_config3;	/* 1C7     Async Config Byte 3 */
10859a1d562SJiri Slaby 	u8 dce_resvd[20];	/* 1C8-1DB DCE Rsvd           */
10959a1d562SJiri Slaby 	u8 dce_resvd21;		/* 1DC     DCE Rsvd (21st byte */
11059a1d562SJiri Slaby 	u8 misc_flags;		/* 1DD     misc flags         */
11159a1d562SJiri Slaby #define V2_HARDWARE     0x40
11259a1d562SJiri Slaby #define ICOM_HDW_ACTIVE 0x01
11359a1d562SJiri Slaby 	u8 call_length;		/* 1DE     Phone #/CFI buff ln */
11459a1d562SJiri Slaby 	u8 call_length2;	/* 1DF     Upper byte (unused) */
11559a1d562SJiri Slaby 	u32 call_addr;		/* 1E0-1E3 Phn #/CFI buff addr */
11659a1d562SJiri Slaby 	u16 timer_value;	/* 1E4-1E5 general timer value */
11759a1d562SJiri Slaby 	u8 timer_command;	/* 1E6     general timer cmd  */
11859a1d562SJiri Slaby 	u8 dce_command;		/* 1E7     dce command reg    */
11959a1d562SJiri Slaby 	u8 dce_cmd_status;	/* 1E8     dce command stat   */
12059a1d562SJiri Slaby 	u8 x21_r1_ioff;		/* 1E9     dce ready counter  */
12159a1d562SJiri Slaby 	u8 x21_r0_ioff;		/* 1EA     dce not ready ctr  */
12259a1d562SJiri Slaby 	u8 x21_ralt_ioff;	/* 1EB     dce CNR counter    */
12359a1d562SJiri Slaby 	u8 x21_r1_ion;		/* 1EC     dce ready I on ctr */
12459a1d562SJiri Slaby 	u8 rsvd_ier;		/* 1ED     Rsvd for IER (if ne */
12559a1d562SJiri Slaby 	u8 ier;			/* 1EE     Interrupt Enable   */
12659a1d562SJiri Slaby 	u8 isr;			/* 1EF     Input Signal Reg   */
12759a1d562SJiri Slaby 	u8 osr;			/* 1F0     Output Signal Reg  */
12859a1d562SJiri Slaby 	u8 reset;		/* 1F1     Reset/Reload Reg   */
12959a1d562SJiri Slaby 	u8 disable;		/* 1F2     Disable Reg        */
13059a1d562SJiri Slaby 	u8 sync;		/* 1F3     Sync Reg           */
13159a1d562SJiri Slaby 	u8 error_stat;		/* 1F4     Error Status       */
13259a1d562SJiri Slaby 	u8 cable_id;		/* 1F5     Cable ID           */
13359a1d562SJiri Slaby 	u8 cs_length;		/* 1F6     CS Load Length     */
13459a1d562SJiri Slaby 	u8 mac_length;		/* 1F7     Mac Load Length    */
13559a1d562SJiri Slaby 	u32 cs_load_addr;	/* 1F8-1FB Call Load PCI Addr */
13659a1d562SJiri Slaby 	u32 mac_load_addr;	/* 1FC-1FF Mac Load PCI Addr  */
13759a1d562SJiri Slaby };
13859a1d562SJiri Slaby 
13959a1d562SJiri Slaby /*
14059a1d562SJiri Slaby  * adapter defines and structures
14159a1d562SJiri Slaby  */
14259a1d562SJiri Slaby #define ICOM_CONTROL_START_A         0x00000008
14359a1d562SJiri Slaby #define ICOM_CONTROL_STOP_A          0x00000004
14459a1d562SJiri Slaby #define ICOM_CONTROL_START_B         0x00000002
14559a1d562SJiri Slaby #define ICOM_CONTROL_STOP_B          0x00000001
14659a1d562SJiri Slaby #define ICOM_CONTROL_START_C         0x00000008
14759a1d562SJiri Slaby #define ICOM_CONTROL_STOP_C          0x00000004
14859a1d562SJiri Slaby #define ICOM_CONTROL_START_D         0x00000002
14959a1d562SJiri Slaby #define ICOM_CONTROL_STOP_D          0x00000001
15059a1d562SJiri Slaby #define ICOM_IRAM_OFFSET             0x1000
15159a1d562SJiri Slaby #define ICOM_IRAM_SIZE               0x0C00
15259a1d562SJiri Slaby #define ICOM_DCE_IRAM_OFFSET         0x0A00
15359a1d562SJiri Slaby #define ICOM_CABLE_ID_VALID          0x01
15459a1d562SJiri Slaby #define ICOM_CABLE_ID_MASK           0xF0
15559a1d562SJiri Slaby #define ICOM_DISABLE                 0x80
15659a1d562SJiri Slaby #define CMD_XMIT_RCV_ENABLE          0xC0
15759a1d562SJiri Slaby #define CMD_XMIT_ENABLE              0x40
15859a1d562SJiri Slaby #define CMD_RCV_DISABLE              0x00
15959a1d562SJiri Slaby #define CMD_RCV_ENABLE               0x80
16059a1d562SJiri Slaby #define CMD_RESTART                  0x01
16159a1d562SJiri Slaby #define CMD_HOLD_XMIT                0x02
16259a1d562SJiri Slaby #define CMD_SND_BREAK                0x04
16359a1d562SJiri Slaby #define RS232_CABLE                  0x06
16459a1d562SJiri Slaby #define V24_CABLE                    0x0E
16559a1d562SJiri Slaby #define V35_CABLE                    0x0C
16659a1d562SJiri Slaby #define V36_CABLE                    0x02
16759a1d562SJiri Slaby #define NO_CABLE                     0x00
16859a1d562SJiri Slaby #define START_DOWNLOAD               0x80
16959a1d562SJiri Slaby #define ICOM_INT_MASK_PRC_A          0x00003FFF
17059a1d562SJiri Slaby #define ICOM_INT_MASK_PRC_B          0x3FFF0000
17159a1d562SJiri Slaby #define ICOM_INT_MASK_PRC_C          0x00003FFF
17259a1d562SJiri Slaby #define ICOM_INT_MASK_PRC_D          0x3FFF0000
17359a1d562SJiri Slaby #define INT_RCV_COMPLETED            0x1000
17459a1d562SJiri Slaby #define INT_XMIT_COMPLETED           0x2000
17559a1d562SJiri Slaby #define INT_IDLE_DETECT              0x0800
17659a1d562SJiri Slaby #define INT_RCV_DISABLED             0x0400
17759a1d562SJiri Slaby #define INT_XMIT_DISABLED            0x0200
17859a1d562SJiri Slaby #define INT_RCV_XMIT_SHUTDOWN        0x0100
17959a1d562SJiri Slaby #define INT_FATAL_ERROR              0x0080
18059a1d562SJiri Slaby #define INT_CABLE_PULL               0x0020
18159a1d562SJiri Slaby #define INT_SIGNAL_CHANGE            0x0010
18259a1d562SJiri Slaby #define HDLC_PPP_PURE_ASYNC          0x02
18359a1d562SJiri Slaby #define HDLC_FF_FILL                 0x00
18459a1d562SJiri Slaby #define HDLC_HDW_FLOW                0x01
18559a1d562SJiri Slaby #define START_XMIT                   0x80
18659a1d562SJiri Slaby #define ICOM_ACFG_DRIVE1             0x20
18759a1d562SJiri Slaby #define ICOM_ACFG_NO_PARITY          0x00
18859a1d562SJiri Slaby #define ICOM_ACFG_PARITY_ENAB        0x02
18959a1d562SJiri Slaby #define ICOM_ACFG_PARITY_ODD         0x01
19059a1d562SJiri Slaby #define ICOM_ACFG_8BPC               0x00
19159a1d562SJiri Slaby #define ICOM_ACFG_7BPC               0x04
19259a1d562SJiri Slaby #define ICOM_ACFG_6BPC               0x08
19359a1d562SJiri Slaby #define ICOM_ACFG_5BPC               0x0C
19459a1d562SJiri Slaby #define ICOM_ACFG_1STOP_BIT          0x00
19559a1d562SJiri Slaby #define ICOM_ACFG_2STOP_BIT          0x10
19659a1d562SJiri Slaby #define ICOM_DTR                     0x80
19759a1d562SJiri Slaby #define ICOM_RTS                     0x40
19859a1d562SJiri Slaby #define ICOM_RI                      0x08
19959a1d562SJiri Slaby #define ICOM_DSR                     0x80
20059a1d562SJiri Slaby #define ICOM_DCD                     0x20
20159a1d562SJiri Slaby #define ICOM_CTS                     0x40
20259a1d562SJiri Slaby 
20359a1d562SJiri Slaby #define NUM_XBUFFS 1
20459a1d562SJiri Slaby #define NUM_RBUFFS 2
20559a1d562SJiri Slaby #define RCV_BUFF_SZ 0x0200
20659a1d562SJiri Slaby #define XMIT_BUFF_SZ 0x1000
20759a1d562SJiri Slaby struct statusArea {
20859a1d562SJiri Slaby     /**********************************************/
20959a1d562SJiri Slaby 	/* Transmit Status Area                       */
21059a1d562SJiri Slaby     /**********************************************/
21159a1d562SJiri Slaby 	struct xmit_status_area{
21259a1d562SJiri Slaby 		__le32 leNext;	/* Next entry in Little Endian on Adapter */
21359a1d562SJiri Slaby 		__le32 leNextASD;
21459a1d562SJiri Slaby 		__le32 leBuffer;	/* Buffer for entry in LE for Adapter */
21559a1d562SJiri Slaby 		__le16 leLengthASD;
21659a1d562SJiri Slaby 		__le16 leOffsetASD;
21759a1d562SJiri Slaby 		__le16 leLength;	/* Length of data in segment */
21859a1d562SJiri Slaby 		__le16 flags;
21959a1d562SJiri Slaby #define SA_FLAGS_DONE           0x0080	/* Done with Segment */
22059a1d562SJiri Slaby #define SA_FLAGS_CONTINUED      0x8000	/* More Segments */
22159a1d562SJiri Slaby #define SA_FLAGS_IDLE           0x4000	/* Mark IDLE after frm */
22259a1d562SJiri Slaby #define SA_FLAGS_READY_TO_XMIT  0x0800
22359a1d562SJiri Slaby #define SA_FLAGS_STAT_MASK      0x007F
22459a1d562SJiri Slaby 	} xmit[NUM_XBUFFS];
22559a1d562SJiri Slaby 
22659a1d562SJiri Slaby     /**********************************************/
22759a1d562SJiri Slaby 	/* Receive Status Area                        */
22859a1d562SJiri Slaby     /**********************************************/
22959a1d562SJiri Slaby 	struct {
23059a1d562SJiri Slaby 		__le32 leNext;	/* Next entry in Little Endian on Adapter */
23159a1d562SJiri Slaby 		__le32 leNextASD;
23259a1d562SJiri Slaby 		__le32 leBuffer;	/* Buffer for entry in LE for Adapter */
23359a1d562SJiri Slaby 		__le16 WorkingLength;	/* size of segment */
23459a1d562SJiri Slaby 		__le16 reserv01;
23559a1d562SJiri Slaby 		__le16 leLength;	/* Length of data in segment */
23659a1d562SJiri Slaby 		__le16 flags;
23759a1d562SJiri Slaby #define SA_FL_RCV_DONE           0x0010	/* Data ready */
23859a1d562SJiri Slaby #define SA_FLAGS_OVERRUN         0x0040
23959a1d562SJiri Slaby #define SA_FLAGS_PARITY_ERROR    0x0080
24059a1d562SJiri Slaby #define SA_FLAGS_FRAME_ERROR     0x0001
24159a1d562SJiri Slaby #define SA_FLAGS_FRAME_TRUNC     0x0002
24259a1d562SJiri Slaby #define SA_FLAGS_BREAK_DET       0x0004	/* set conditionally by device driver, not hardware */
24359a1d562SJiri Slaby #define SA_FLAGS_RCV_MASK        0xFFE6
24459a1d562SJiri Slaby 	} rcv[NUM_RBUFFS];
24559a1d562SJiri Slaby };
24659a1d562SJiri Slaby 
24759a1d562SJiri Slaby struct icom_adapter;
24859a1d562SJiri Slaby 
24959a1d562SJiri Slaby 
25059a1d562SJiri Slaby #define ICOM_MAJOR       243
25159a1d562SJiri Slaby #define ICOM_MINOR_START 0
25259a1d562SJiri Slaby 
25359a1d562SJiri Slaby struct icom_port {
25459a1d562SJiri Slaby 	struct uart_port uart_port;
25559a1d562SJiri Slaby 	unsigned char cable_id;
25659a1d562SJiri Slaby 	unsigned char read_status_mask;
25759a1d562SJiri Slaby 	unsigned char ignore_status_mask;
25859a1d562SJiri Slaby 	void __iomem * int_reg;
25959a1d562SJiri Slaby 	struct icom_regs __iomem *global_reg;
26059a1d562SJiri Slaby 	struct func_dram __iomem *dram;
26159a1d562SJiri Slaby 	int port;
26259a1d562SJiri Slaby 	struct statusArea *statStg;
26359a1d562SJiri Slaby 	dma_addr_t statStg_pci;
26459a1d562SJiri Slaby 	__le32 *xmitRestart;
26559a1d562SJiri Slaby 	dma_addr_t xmitRestart_pci;
26659a1d562SJiri Slaby 	unsigned char *xmit_buf;
26759a1d562SJiri Slaby 	dma_addr_t xmit_buf_pci;
26859a1d562SJiri Slaby 	unsigned char *recv_buf;
26959a1d562SJiri Slaby 	dma_addr_t recv_buf_pci;
27059a1d562SJiri Slaby 	int next_rcv;
27159a1d562SJiri Slaby 	int status;
27259a1d562SJiri Slaby #define ICOM_PORT_ACTIVE	1	/* Port exists. */
27359a1d562SJiri Slaby #define ICOM_PORT_OFF		0	/* Port does not exist. */
27459a1d562SJiri Slaby 	struct icom_adapter *adapter;
27559a1d562SJiri Slaby };
27659a1d562SJiri Slaby 
27759a1d562SJiri Slaby struct icom_adapter {
27859a1d562SJiri Slaby 	void __iomem * base_addr;
27959a1d562SJiri Slaby 	unsigned long base_addr_pci;
28059a1d562SJiri Slaby 	struct pci_dev *pci_dev;
28159a1d562SJiri Slaby 	struct icom_port port_info[4];
28259a1d562SJiri Slaby 	int index;
28359a1d562SJiri Slaby 	int version;
28459a1d562SJiri Slaby #define ADAPTER_V1	0x0001
28559a1d562SJiri Slaby #define ADAPTER_V2	0x0002
28659a1d562SJiri Slaby 	u32 subsystem_id;
28759a1d562SJiri Slaby #define FOUR_PORT_MODEL				0x0252
28859a1d562SJiri Slaby #define V2_TWO_PORTS_RVX			0x021A
28959a1d562SJiri Slaby #define V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM	0x0251
29059a1d562SJiri Slaby 	int numb_ports;
29159a1d562SJiri Slaby 	struct list_head icom_adapter_entry;
29259a1d562SJiri Slaby 	struct kref kref;
29359a1d562SJiri Slaby };
29459a1d562SJiri Slaby 
29559a1d562SJiri Slaby /* prototype */
29659a1d562SJiri Slaby extern void iCom_sercons_init(void);
29759a1d562SJiri Slaby 
29859a1d562SJiri Slaby struct lookup_proc_table {
29959a1d562SJiri Slaby 	u32	__iomem *global_control_reg;
30059a1d562SJiri Slaby 	unsigned long	processor_id;
30159a1d562SJiri Slaby };
30259a1d562SJiri Slaby 
30359a1d562SJiri Slaby struct lookup_int_table {
30459a1d562SJiri Slaby 	u32	__iomem *global_int_mask;
30559a1d562SJiri Slaby 	unsigned long	processor_id;
30659a1d562SJiri Slaby };
30759a1d562SJiri Slaby 
308f73989f5SJiri Slaby static inline struct icom_port *to_icom_port(struct uart_port *port)
309f73989f5SJiri Slaby {
310f73989f5SJiri Slaby 	return container_of(port, struct icom_port, uart_port);
311f73989f5SJiri Slaby }
312f73989f5SJiri Slaby 
313ab4382d2SGreg Kroah-Hartman static const struct pci_device_id icom_pci_table[] = {
314ab4382d2SGreg Kroah-Hartman 	{
315ab4382d2SGreg Kroah-Hartman 		.vendor = PCI_VENDOR_ID_IBM,
316ab4382d2SGreg Kroah-Hartman 		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1,
317ab4382d2SGreg Kroah-Hartman 		.subvendor = PCI_ANY_ID,
318ab4382d2SGreg Kroah-Hartman 		.subdevice = PCI_ANY_ID,
319ab4382d2SGreg Kroah-Hartman 		.driver_data = ADAPTER_V1,
320ab4382d2SGreg Kroah-Hartman 	},
321ab4382d2SGreg Kroah-Hartman 	{
322ab4382d2SGreg Kroah-Hartman 		.vendor = PCI_VENDOR_ID_IBM,
323ab4382d2SGreg Kroah-Hartman 		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
324ab4382d2SGreg Kroah-Hartman 		.subvendor = PCI_VENDOR_ID_IBM,
325ab4382d2SGreg Kroah-Hartman 		.subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX,
326ab4382d2SGreg Kroah-Hartman 		.driver_data = ADAPTER_V2,
327ab4382d2SGreg Kroah-Hartman 	},
328ab4382d2SGreg Kroah-Hartman 	{
329ab4382d2SGreg Kroah-Hartman 		.vendor = PCI_VENDOR_ID_IBM,
330ab4382d2SGreg Kroah-Hartman 		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
331ab4382d2SGreg Kroah-Hartman 		.subvendor = PCI_VENDOR_ID_IBM,
332ab4382d2SGreg Kroah-Hartman 		.subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM,
333ab4382d2SGreg Kroah-Hartman 		.driver_data = ADAPTER_V2,
334ab4382d2SGreg Kroah-Hartman 	},
335ab4382d2SGreg Kroah-Hartman 	{
336ab4382d2SGreg Kroah-Hartman 		.vendor = PCI_VENDOR_ID_IBM,
337ab4382d2SGreg Kroah-Hartman 		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
338ab4382d2SGreg Kroah-Hartman 		.subvendor = PCI_VENDOR_ID_IBM,
339ab4382d2SGreg Kroah-Hartman 		.subdevice = PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL,
340ab4382d2SGreg Kroah-Hartman 		.driver_data = ADAPTER_V2,
341ab4382d2SGreg Kroah-Hartman 	},
342ab4382d2SGreg Kroah-Hartman 	{
343ab4382d2SGreg Kroah-Hartman 		.vendor = PCI_VENDOR_ID_IBM,
344ab4382d2SGreg Kroah-Hartman 		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
345ab4382d2SGreg Kroah-Hartman 		.subvendor = PCI_VENDOR_ID_IBM,
346ab4382d2SGreg Kroah-Hartman 		.subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE,
347ab4382d2SGreg Kroah-Hartman 		.driver_data = ADAPTER_V2,
348ab4382d2SGreg Kroah-Hartman 	},
349ab4382d2SGreg Kroah-Hartman 	{}
350ab4382d2SGreg Kroah-Hartman };
351ab4382d2SGreg Kroah-Hartman 
3525a7daed8SJingoo Han static struct lookup_proc_table start_proc[4] = {
353ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_CONTROL_START_A},
354ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_CONTROL_START_B},
355ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_CONTROL_START_C},
356ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_CONTROL_START_D}
357ab4382d2SGreg Kroah-Hartman };
358ab4382d2SGreg Kroah-Hartman 
359ab4382d2SGreg Kroah-Hartman 
3605a7daed8SJingoo Han static struct lookup_proc_table stop_proc[4] = {
361ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_CONTROL_STOP_A},
362ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_CONTROL_STOP_B},
363ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_CONTROL_STOP_C},
364ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_CONTROL_STOP_D}
365ab4382d2SGreg Kroah-Hartman };
366ab4382d2SGreg Kroah-Hartman 
3675a7daed8SJingoo Han static struct lookup_int_table int_mask_tbl[4] = {
368ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_INT_MASK_PRC_A},
369ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_INT_MASK_PRC_B},
370ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_INT_MASK_PRC_C},
371ab4382d2SGreg Kroah-Hartman 	{NULL, ICOM_INT_MASK_PRC_D},
372ab4382d2SGreg Kroah-Hartman };
373ab4382d2SGreg Kroah-Hartman 
374ab4382d2SGreg Kroah-Hartman 
375ab4382d2SGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, icom_pci_table);
376ab4382d2SGreg Kroah-Hartman 
377ab4382d2SGreg Kroah-Hartman static LIST_HEAD(icom_adapter_head);
378ab4382d2SGreg Kroah-Hartman 
379ab4382d2SGreg Kroah-Hartman /* spinlock for adapter initialization and changing adapter operations */
38001493ccbSZheng Yongjun static DEFINE_SPINLOCK(icom_lock);
381ab4382d2SGreg Kroah-Hartman 
382ab4382d2SGreg Kroah-Hartman #ifdef ICOM_TRACE
383ab4382d2SGreg Kroah-Hartman static inline void trace(struct icom_port *icom_port, char *trace_pt,
384ab4382d2SGreg Kroah-Hartman 			unsigned long trace_data)
385ab4382d2SGreg Kroah-Hartman {
386ab4382d2SGreg Kroah-Hartman 	dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n",
387ab4382d2SGreg Kroah-Hartman 	icom_port->port, trace_pt, trace_data);
388ab4382d2SGreg Kroah-Hartman }
389ab4382d2SGreg Kroah-Hartman #else
390ab4382d2SGreg Kroah-Hartman static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {};
391ab4382d2SGreg Kroah-Hartman #endif
392ab4382d2SGreg Kroah-Hartman static void icom_kref_release(struct kref *kref);
393ab4382d2SGreg Kroah-Hartman 
394ab4382d2SGreg Kroah-Hartman static void free_port_memory(struct icom_port *icom_port)
395ab4382d2SGreg Kroah-Hartman {
396ab4382d2SGreg Kroah-Hartman 	struct pci_dev *dev = icom_port->adapter->pci_dev;
397ab4382d2SGreg Kroah-Hartman 
398ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "RET_PORT_MEM", 0);
399ab4382d2SGreg Kroah-Hartman 	if (icom_port->recv_buf) {
400c3647f2fSChristophe JAILLET 		dma_free_coherent(&dev->dev, 4096, icom_port->recv_buf,
401ab4382d2SGreg Kroah-Hartman 				  icom_port->recv_buf_pci);
402ab4382d2SGreg Kroah-Hartman 		icom_port->recv_buf = NULL;
403ab4382d2SGreg Kroah-Hartman 	}
404ab4382d2SGreg Kroah-Hartman 	if (icom_port->xmit_buf) {
405c3647f2fSChristophe JAILLET 		dma_free_coherent(&dev->dev, 4096, icom_port->xmit_buf,
406ab4382d2SGreg Kroah-Hartman 				  icom_port->xmit_buf_pci);
407ab4382d2SGreg Kroah-Hartman 		icom_port->xmit_buf = NULL;
408ab4382d2SGreg Kroah-Hartman 	}
409ab4382d2SGreg Kroah-Hartman 	if (icom_port->statStg) {
410c3647f2fSChristophe JAILLET 		dma_free_coherent(&dev->dev, 4096, icom_port->statStg,
411ab4382d2SGreg Kroah-Hartman 				  icom_port->statStg_pci);
412ab4382d2SGreg Kroah-Hartman 		icom_port->statStg = NULL;
413ab4382d2SGreg Kroah-Hartman 	}
414ab4382d2SGreg Kroah-Hartman 
415ab4382d2SGreg Kroah-Hartman 	if (icom_port->xmitRestart) {
416c3647f2fSChristophe JAILLET 		dma_free_coherent(&dev->dev, 4096, icom_port->xmitRestart,
417ab4382d2SGreg Kroah-Hartman 				  icom_port->xmitRestart_pci);
418ab4382d2SGreg Kroah-Hartman 		icom_port->xmitRestart = NULL;
419ab4382d2SGreg Kroah-Hartman 	}
420ab4382d2SGreg Kroah-Hartman }
421ab4382d2SGreg Kroah-Hartman 
4229671f099SBill Pemberton static int get_port_memory(struct icom_port *icom_port)
423ab4382d2SGreg Kroah-Hartman {
424ab4382d2SGreg Kroah-Hartman 	int index;
425ab4382d2SGreg Kroah-Hartman 	unsigned long stgAddr;
426ab4382d2SGreg Kroah-Hartman 	unsigned long startStgAddr;
427ab4382d2SGreg Kroah-Hartman 	unsigned long offset;
428ab4382d2SGreg Kroah-Hartman 	struct pci_dev *dev = icom_port->adapter->pci_dev;
429ab4382d2SGreg Kroah-Hartman 
430ab4382d2SGreg Kroah-Hartman 	icom_port->xmit_buf =
431c3647f2fSChristophe JAILLET 	    dma_alloc_coherent(&dev->dev, 4096, &icom_port->xmit_buf_pci,
432c3647f2fSChristophe JAILLET 			       GFP_KERNEL);
433ab4382d2SGreg Kroah-Hartman 	if (!icom_port->xmit_buf) {
434ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Can not allocate Transmit buffer\n");
435ab4382d2SGreg Kroah-Hartman 		return -ENOMEM;
436ab4382d2SGreg Kroah-Hartman 	}
437ab4382d2SGreg Kroah-Hartman 
438ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "GET_PORT_MEM",
439ab4382d2SGreg Kroah-Hartman 	      (unsigned long) icom_port->xmit_buf);
440ab4382d2SGreg Kroah-Hartman 
441ab4382d2SGreg Kroah-Hartman 	icom_port->recv_buf =
442c3647f2fSChristophe JAILLET 	    dma_alloc_coherent(&dev->dev, 4096, &icom_port->recv_buf_pci,
443c3647f2fSChristophe JAILLET 			       GFP_KERNEL);
444ab4382d2SGreg Kroah-Hartman 	if (!icom_port->recv_buf) {
445ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Can not allocate Receive buffer\n");
446ab4382d2SGreg Kroah-Hartman 		free_port_memory(icom_port);
447ab4382d2SGreg Kroah-Hartman 		return -ENOMEM;
448ab4382d2SGreg Kroah-Hartman 	}
449ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "GET_PORT_MEM",
450ab4382d2SGreg Kroah-Hartman 	      (unsigned long) icom_port->recv_buf);
451ab4382d2SGreg Kroah-Hartman 
452ab4382d2SGreg Kroah-Hartman 	icom_port->statStg =
453c3647f2fSChristophe JAILLET 	    dma_alloc_coherent(&dev->dev, 4096, &icom_port->statStg_pci,
454c3647f2fSChristophe JAILLET 			       GFP_KERNEL);
455ab4382d2SGreg Kroah-Hartman 	if (!icom_port->statStg) {
456ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Can not allocate Status buffer\n");
457ab4382d2SGreg Kroah-Hartman 		free_port_memory(icom_port);
458ab4382d2SGreg Kroah-Hartman 		return -ENOMEM;
459ab4382d2SGreg Kroah-Hartman 	}
460ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "GET_PORT_MEM",
461ab4382d2SGreg Kroah-Hartman 	      (unsigned long) icom_port->statStg);
462ab4382d2SGreg Kroah-Hartman 
463ab4382d2SGreg Kroah-Hartman 	icom_port->xmitRestart =
464c3647f2fSChristophe JAILLET 	    dma_alloc_coherent(&dev->dev, 4096, &icom_port->xmitRestart_pci,
465c3647f2fSChristophe JAILLET 			       GFP_KERNEL);
466ab4382d2SGreg Kroah-Hartman 	if (!icom_port->xmitRestart) {
467ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev,
468ab4382d2SGreg Kroah-Hartman 			"Can not allocate xmit Restart buffer\n");
469ab4382d2SGreg Kroah-Hartman 		free_port_memory(icom_port);
470ab4382d2SGreg Kroah-Hartman 		return -ENOMEM;
471ab4382d2SGreg Kroah-Hartman 	}
472ab4382d2SGreg Kroah-Hartman 
473ab4382d2SGreg Kroah-Hartman 	/* FODs: Frame Out Descriptor Queue, this is a FIFO queue that
474ab4382d2SGreg Kroah-Hartman            indicates that frames are to be transmitted
475ab4382d2SGreg Kroah-Hartman 	*/
476ab4382d2SGreg Kroah-Hartman 
477ab4382d2SGreg Kroah-Hartman 	stgAddr = (unsigned long) icom_port->statStg;
478ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < NUM_XBUFFS; index++) {
479ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "FOD_ADDR", stgAddr);
480ab4382d2SGreg Kroah-Hartman 		stgAddr = stgAddr + sizeof(icom_port->statStg->xmit[0]);
481ab4382d2SGreg Kroah-Hartman 		if (index < (NUM_XBUFFS - 1)) {
482ab4382d2SGreg Kroah-Hartman 			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
483ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->xmit[index].leLengthASD =
4847a5f86e8SJiri Slaby 			    cpu_to_le16(XMIT_BUFF_SZ);
485ab4382d2SGreg Kroah-Hartman 			trace(icom_port, "FOD_ADDR", stgAddr);
486ab4382d2SGreg Kroah-Hartman 			trace(icom_port, "FOD_XBUFF",
487ab4382d2SGreg Kroah-Hartman 			      (unsigned long) icom_port->xmit_buf);
488ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->xmit[index].leBuffer =
489ab4382d2SGreg Kroah-Hartman 			    cpu_to_le32(icom_port->xmit_buf_pci);
490ab4382d2SGreg Kroah-Hartman 		} else if (index == (NUM_XBUFFS - 1)) {
491ab4382d2SGreg Kroah-Hartman 			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
492ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->xmit[index].leLengthASD =
4937a5f86e8SJiri Slaby 			    cpu_to_le16(XMIT_BUFF_SZ);
494ab4382d2SGreg Kroah-Hartman 			trace(icom_port, "FOD_XBUFF",
495ab4382d2SGreg Kroah-Hartman 			      (unsigned long) icom_port->xmit_buf);
496ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->xmit[index].leBuffer =
497ab4382d2SGreg Kroah-Hartman 			    cpu_to_le32(icom_port->xmit_buf_pci);
498ab4382d2SGreg Kroah-Hartman 		} else {
499ab4382d2SGreg Kroah-Hartman 			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
500ab4382d2SGreg Kroah-Hartman 		}
501ab4382d2SGreg Kroah-Hartman 	}
502ab4382d2SGreg Kroah-Hartman 	/* FIDs */
503ab4382d2SGreg Kroah-Hartman 	startStgAddr = stgAddr;
504ab4382d2SGreg Kroah-Hartman 
505ab4382d2SGreg Kroah-Hartman 	/* fill in every entry, even if no buffer */
506ab4382d2SGreg Kroah-Hartman 	for (index = 0; index <  NUM_RBUFFS; index++) {
507ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "FID_ADDR", stgAddr);
508ab4382d2SGreg Kroah-Hartman 		stgAddr = stgAddr + sizeof(icom_port->statStg->rcv[0]);
509ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->rcv[index].leLength = 0;
510ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->rcv[index].WorkingLength =
5117a5f86e8SJiri Slaby 		    cpu_to_le16(RCV_BUFF_SZ);
512ab4382d2SGreg Kroah-Hartman 		if (index < (NUM_RBUFFS - 1) ) {
513ab4382d2SGreg Kroah-Hartman 			offset = stgAddr - (unsigned long) icom_port->statStg;
514ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->rcv[index].leNext =
515ab4382d2SGreg Kroah-Hartman 			      cpu_to_le32(icom_port-> statStg_pci + offset);
516ab4382d2SGreg Kroah-Hartman 			trace(icom_port, "FID_RBUFF",
517ab4382d2SGreg Kroah-Hartman 			      (unsigned long) icom_port->recv_buf);
518ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->rcv[index].leBuffer =
519ab4382d2SGreg Kroah-Hartman 			    cpu_to_le32(icom_port->recv_buf_pci);
520ab4382d2SGreg Kroah-Hartman 		} else if (index == (NUM_RBUFFS -1) ) {
521ab4382d2SGreg Kroah-Hartman 			offset = startStgAddr - (unsigned long) icom_port->statStg;
522ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->rcv[index].leNext =
523ab4382d2SGreg Kroah-Hartman 			    cpu_to_le32(icom_port-> statStg_pci + offset);
524ab4382d2SGreg Kroah-Hartman 			trace(icom_port, "FID_RBUFF",
525ab4382d2SGreg Kroah-Hartman 			      (unsigned long) icom_port->recv_buf + 2048);
526ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->rcv[index].leBuffer =
527ab4382d2SGreg Kroah-Hartman 			    cpu_to_le32(icom_port->recv_buf_pci + 2048);
528ab4382d2SGreg Kroah-Hartman 		} else {
529ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->rcv[index].leNext = 0;
530ab4382d2SGreg Kroah-Hartman 			icom_port->statStg->rcv[index].leBuffer = 0;
531ab4382d2SGreg Kroah-Hartman 		}
532ab4382d2SGreg Kroah-Hartman 	}
533ab4382d2SGreg Kroah-Hartman 
534ab4382d2SGreg Kroah-Hartman 	return 0;
535ab4382d2SGreg Kroah-Hartman }
536ab4382d2SGreg Kroah-Hartman 
537ab4382d2SGreg Kroah-Hartman static void stop_processor(struct icom_port *icom_port)
538ab4382d2SGreg Kroah-Hartman {
539ab4382d2SGreg Kroah-Hartman 	unsigned long temp;
540ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
541ab4382d2SGreg Kroah-Hartman 	int port;
542ab4382d2SGreg Kroah-Hartman 
543ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&icom_lock, flags);
544ab4382d2SGreg Kroah-Hartman 
545ab4382d2SGreg Kroah-Hartman 	port = icom_port->port;
5464f03ffcdSDan Carpenter 	if (port >= ARRAY_SIZE(stop_proc)) {
5474f03ffcdSDan Carpenter 		dev_err(&icom_port->adapter->pci_dev->dev,
5484f03ffcdSDan Carpenter 			"Invalid port assignment\n");
5494f03ffcdSDan Carpenter 		goto unlock;
5504f03ffcdSDan Carpenter 	}
5514f03ffcdSDan Carpenter 
552ab4382d2SGreg Kroah-Hartman 	if (port == 0 || port == 1)
553ab4382d2SGreg Kroah-Hartman 		stop_proc[port].global_control_reg = &icom_port->global_reg->control;
554ab4382d2SGreg Kroah-Hartman 	else
555ab4382d2SGreg Kroah-Hartman 		stop_proc[port].global_control_reg = &icom_port->global_reg->control_2;
556ab4382d2SGreg Kroah-Hartman 
557ab4382d2SGreg Kroah-Hartman 	temp = readl(stop_proc[port].global_control_reg);
5584f03ffcdSDan Carpenter 	temp = (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id;
559ab4382d2SGreg Kroah-Hartman 	writel(temp, stop_proc[port].global_control_reg);
560ab4382d2SGreg Kroah-Hartman 
561ab4382d2SGreg Kroah-Hartman 	/* write flush */
562ab4382d2SGreg Kroah-Hartman 	readl(stop_proc[port].global_control_reg);
563ab4382d2SGreg Kroah-Hartman 
5644f03ffcdSDan Carpenter unlock:
565ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&icom_lock, flags);
566ab4382d2SGreg Kroah-Hartman }
567ab4382d2SGreg Kroah-Hartman 
568ab4382d2SGreg Kroah-Hartman static void start_processor(struct icom_port *icom_port)
569ab4382d2SGreg Kroah-Hartman {
570ab4382d2SGreg Kroah-Hartman 	unsigned long temp;
571ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
572ab4382d2SGreg Kroah-Hartman 	int port;
573ab4382d2SGreg Kroah-Hartman 
574ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&icom_lock, flags);
575ab4382d2SGreg Kroah-Hartman 
576ab4382d2SGreg Kroah-Hartman 	port = icom_port->port;
5774f03ffcdSDan Carpenter 	if (port >= ARRAY_SIZE(start_proc)) {
5784f03ffcdSDan Carpenter 		dev_err(&icom_port->adapter->pci_dev->dev,
5794f03ffcdSDan Carpenter 			"Invalid port assignment\n");
5804f03ffcdSDan Carpenter 		goto unlock;
5814f03ffcdSDan Carpenter 	}
5824f03ffcdSDan Carpenter 
583ab4382d2SGreg Kroah-Hartman 	if (port == 0 || port == 1)
584ab4382d2SGreg Kroah-Hartman 		start_proc[port].global_control_reg = &icom_port->global_reg->control;
585ab4382d2SGreg Kroah-Hartman 	else
586ab4382d2SGreg Kroah-Hartman 		start_proc[port].global_control_reg = &icom_port->global_reg->control_2;
5874f03ffcdSDan Carpenter 
588ab4382d2SGreg Kroah-Hartman 	temp = readl(start_proc[port].global_control_reg);
5894f03ffcdSDan Carpenter 	temp = (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id;
590ab4382d2SGreg Kroah-Hartman 	writel(temp, start_proc[port].global_control_reg);
591ab4382d2SGreg Kroah-Hartman 
592ab4382d2SGreg Kroah-Hartman 	/* write flush */
593ab4382d2SGreg Kroah-Hartman 	readl(start_proc[port].global_control_reg);
594ab4382d2SGreg Kroah-Hartman 
5954f03ffcdSDan Carpenter unlock:
596ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&icom_lock, flags);
597ab4382d2SGreg Kroah-Hartman }
598ab4382d2SGreg Kroah-Hartman 
599ab4382d2SGreg Kroah-Hartman static void load_code(struct icom_port *icom_port)
600ab4382d2SGreg Kroah-Hartman {
601ab4382d2SGreg Kroah-Hartman 	const struct firmware *fw;
602ab4382d2SGreg Kroah-Hartman 	char __iomem *iram_ptr;
603ab4382d2SGreg Kroah-Hartman 	int index;
604ab4382d2SGreg Kroah-Hartman 	int status = 0;
605ab4382d2SGreg Kroah-Hartman 	void __iomem *dram_ptr = icom_port->dram;
606ab4382d2SGreg Kroah-Hartman 	dma_addr_t temp_pci;
607ab4382d2SGreg Kroah-Hartman 	unsigned char *new_page = NULL;
608ab4382d2SGreg Kroah-Hartman 	unsigned char cable_id = NO_CABLE;
609ab4382d2SGreg Kroah-Hartman 	struct pci_dev *dev = icom_port->adapter->pci_dev;
610ab4382d2SGreg Kroah-Hartman 
611ab4382d2SGreg Kroah-Hartman 	/* Clear out any pending interrupts */
612ab4382d2SGreg Kroah-Hartman 	writew(0x3FFF, icom_port->int_reg);
613ab4382d2SGreg Kroah-Hartman 
614ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "CLEAR_INTERRUPTS", 0);
615ab4382d2SGreg Kroah-Hartman 
616ab4382d2SGreg Kroah-Hartman 	/* Stop processor */
617ab4382d2SGreg Kroah-Hartman 	stop_processor(icom_port);
618ab4382d2SGreg Kroah-Hartman 
619ab4382d2SGreg Kroah-Hartman 	/* Zero out DRAM */
620ab4382d2SGreg Kroah-Hartman 	memset_io(dram_ptr, 0, 512);
621ab4382d2SGreg Kroah-Hartman 
622ab4382d2SGreg Kroah-Hartman 	/* Load Call Setup into Adapter */
623ab4382d2SGreg Kroah-Hartman 	if (request_firmware(&fw, "icom_call_setup.bin", &dev->dev) < 0) {
624ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev,"Unable to load icom_call_setup.bin firmware image\n");
625ab4382d2SGreg Kroah-Hartman 		status = -1;
626ab4382d2SGreg Kroah-Hartman 		goto load_code_exit;
627ab4382d2SGreg Kroah-Hartman 	}
628ab4382d2SGreg Kroah-Hartman 
629ab4382d2SGreg Kroah-Hartman 	if (fw->size > ICOM_DCE_IRAM_OFFSET) {
630ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Invalid firmware image for icom_call_setup.bin found.\n");
631ab4382d2SGreg Kroah-Hartman 		release_firmware(fw);
632ab4382d2SGreg Kroah-Hartman 		status = -1;
633ab4382d2SGreg Kroah-Hartman 		goto load_code_exit;
634ab4382d2SGreg Kroah-Hartman 	}
635ab4382d2SGreg Kroah-Hartman 
636ab4382d2SGreg Kroah-Hartman 	iram_ptr = (char __iomem *)icom_port->dram + ICOM_IRAM_OFFSET;
637ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < fw->size; index++)
638ab4382d2SGreg Kroah-Hartman 		writeb(fw->data[index], &iram_ptr[index]);
639ab4382d2SGreg Kroah-Hartman 
640ab4382d2SGreg Kroah-Hartman 	release_firmware(fw);
641ab4382d2SGreg Kroah-Hartman 
642ab4382d2SGreg Kroah-Hartman 	/* Load Resident DCE portion of Adapter */
643ab4382d2SGreg Kroah-Hartman 	if (request_firmware(&fw, "icom_res_dce.bin", &dev->dev) < 0) {
644ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev,"Unable to load icom_res_dce.bin firmware image\n");
645ab4382d2SGreg Kroah-Hartman 		status = -1;
646ab4382d2SGreg Kroah-Hartman 		goto load_code_exit;
647ab4382d2SGreg Kroah-Hartman 	}
648ab4382d2SGreg Kroah-Hartman 
649ab4382d2SGreg Kroah-Hartman 	if (fw->size > ICOM_IRAM_SIZE) {
650ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Invalid firmware image for icom_res_dce.bin found.\n");
651ab4382d2SGreg Kroah-Hartman 		release_firmware(fw);
652ab4382d2SGreg Kroah-Hartman 		status = -1;
653ab4382d2SGreg Kroah-Hartman 		goto load_code_exit;
654ab4382d2SGreg Kroah-Hartman 	}
655ab4382d2SGreg Kroah-Hartman 
656ab4382d2SGreg Kroah-Hartman 	iram_ptr = (char __iomem *) icom_port->dram + ICOM_IRAM_OFFSET;
657ab4382d2SGreg Kroah-Hartman 	for (index = ICOM_DCE_IRAM_OFFSET; index < fw->size; index++)
658ab4382d2SGreg Kroah-Hartman 		writeb(fw->data[index], &iram_ptr[index]);
659ab4382d2SGreg Kroah-Hartman 
660ab4382d2SGreg Kroah-Hartman 	release_firmware(fw);
661ab4382d2SGreg Kroah-Hartman 
662ab4382d2SGreg Kroah-Hartman 	/* Set Hardware level */
663ab4382d2SGreg Kroah-Hartman 	if (icom_port->adapter->version == ADAPTER_V2)
664ab4382d2SGreg Kroah-Hartman 		writeb(V2_HARDWARE, &(icom_port->dram->misc_flags));
665ab4382d2SGreg Kroah-Hartman 
666ab4382d2SGreg Kroah-Hartman 	/* Start the processor in Adapter */
667ab4382d2SGreg Kroah-Hartman 	start_processor(icom_port);
668ab4382d2SGreg Kroah-Hartman 
669ab4382d2SGreg Kroah-Hartman 	writeb((HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL),
670ab4382d2SGreg Kroah-Hartman 	       &(icom_port->dram->HDLCConfigReg));
671ab4382d2SGreg Kroah-Hartman 	writeb(0x04, &(icom_port->dram->FlagFillIdleTimer));	/* 0.5 seconds */
672ab4382d2SGreg Kroah-Hartman 	writeb(0x00, &(icom_port->dram->CmdReg));
673ab4382d2SGreg Kroah-Hartman 	writeb(0x10, &(icom_port->dram->async_config3));
674ab4382d2SGreg Kroah-Hartman 	writeb((ICOM_ACFG_DRIVE1 | ICOM_ACFG_NO_PARITY | ICOM_ACFG_8BPC |
675ab4382d2SGreg Kroah-Hartman 		ICOM_ACFG_1STOP_BIT), &(icom_port->dram->async_config2));
676ab4382d2SGreg Kroah-Hartman 
677ab4382d2SGreg Kroah-Hartman 	/*Set up data in icom DRAM to indicate where personality
678ab4382d2SGreg Kroah-Hartman 	 *code is located and its length.
679ab4382d2SGreg Kroah-Hartman 	 */
680c3647f2fSChristophe JAILLET 	new_page = dma_alloc_coherent(&dev->dev, 4096, &temp_pci, GFP_KERNEL);
681ab4382d2SGreg Kroah-Hartman 
682ab4382d2SGreg Kroah-Hartman 	if (!new_page) {
683ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Can not allocate DMA buffer\n");
684ab4382d2SGreg Kroah-Hartman 		status = -1;
685ab4382d2SGreg Kroah-Hartman 		goto load_code_exit;
686ab4382d2SGreg Kroah-Hartman 	}
687ab4382d2SGreg Kroah-Hartman 
688ab4382d2SGreg Kroah-Hartman 	if (request_firmware(&fw, "icom_asc.bin", &dev->dev) < 0) {
689ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev,"Unable to load icom_asc.bin firmware image\n");
690ab4382d2SGreg Kroah-Hartman 		status = -1;
691ab4382d2SGreg Kroah-Hartman 		goto load_code_exit;
692ab4382d2SGreg Kroah-Hartman 	}
693ab4382d2SGreg Kroah-Hartman 
694ab4382d2SGreg Kroah-Hartman 	if (fw->size > ICOM_DCE_IRAM_OFFSET) {
695ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Invalid firmware image for icom_asc.bin found.\n");
696ab4382d2SGreg Kroah-Hartman 		release_firmware(fw);
697ab4382d2SGreg Kroah-Hartman 		status = -1;
698ab4382d2SGreg Kroah-Hartman 		goto load_code_exit;
699ab4382d2SGreg Kroah-Hartman 	}
700ab4382d2SGreg Kroah-Hartman 
701ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < fw->size; index++)
702ab4382d2SGreg Kroah-Hartman 		new_page[index] = fw->data[index];
703ab4382d2SGreg Kroah-Hartman 
704ab4382d2SGreg Kroah-Hartman 	writeb((char) ((fw->size + 16)/16), &icom_port->dram->mac_length);
705ab4382d2SGreg Kroah-Hartman 	writel(temp_pci, &icom_port->dram->mac_load_addr);
706ab4382d2SGreg Kroah-Hartman 
707d6a62b3bSDan Carpenter 	release_firmware(fw);
708d6a62b3bSDan Carpenter 
709ab4382d2SGreg Kroah-Hartman 	/*Setting the syncReg to 0x80 causes adapter to start downloading
710ab4382d2SGreg Kroah-Hartman 	   the personality code into adapter instruction RAM.
711ab4382d2SGreg Kroah-Hartman 	   Once code is loaded, it will begin executing and, based on
712ab4382d2SGreg Kroah-Hartman 	   information provided above, will start DMAing data from
713ab4382d2SGreg Kroah-Hartman 	   shared memory to adapter DRAM.
714ab4382d2SGreg Kroah-Hartman 	 */
715ab4382d2SGreg Kroah-Hartman 	/* the wait loop below verifies this write operation has been done
716ab4382d2SGreg Kroah-Hartman 	   and processed
717ab4382d2SGreg Kroah-Hartman 	*/
718ab4382d2SGreg Kroah-Hartman 	writeb(START_DOWNLOAD, &icom_port->dram->sync);
719ab4382d2SGreg Kroah-Hartman 
720ab4382d2SGreg Kroah-Hartman 	/* Wait max 1 Sec for data download and processor to start */
721ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < 10; index++) {
722ab4382d2SGreg Kroah-Hartman 		msleep(100);
723ab4382d2SGreg Kroah-Hartman 		if (readb(&icom_port->dram->misc_flags) & ICOM_HDW_ACTIVE)
724ab4382d2SGreg Kroah-Hartman 			break;
725ab4382d2SGreg Kroah-Hartman 	}
726ab4382d2SGreg Kroah-Hartman 
727ab4382d2SGreg Kroah-Hartman 	if (index == 10)
728ab4382d2SGreg Kroah-Hartman 		status = -1;
729ab4382d2SGreg Kroah-Hartman 
730ab4382d2SGreg Kroah-Hartman 	/*
731ab4382d2SGreg Kroah-Hartman 	 * check Cable ID
732ab4382d2SGreg Kroah-Hartman 	 */
733ab4382d2SGreg Kroah-Hartman 	cable_id = readb(&icom_port->dram->cable_id);
734ab4382d2SGreg Kroah-Hartman 
735ab4382d2SGreg Kroah-Hartman 	if (cable_id & ICOM_CABLE_ID_VALID) {
736ab4382d2SGreg Kroah-Hartman 		/* Get cable ID into the lower 4 bits (standard form) */
737ab4382d2SGreg Kroah-Hartman 		cable_id = (cable_id & ICOM_CABLE_ID_MASK) >> 4;
738ab4382d2SGreg Kroah-Hartman 		icom_port->cable_id = cable_id;
739ab4382d2SGreg Kroah-Hartman 	} else {
740ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev,"Invalid or no cable attached\n");
741ab4382d2SGreg Kroah-Hartman 		icom_port->cable_id = NO_CABLE;
742ab4382d2SGreg Kroah-Hartman 	}
743ab4382d2SGreg Kroah-Hartman 
744ab4382d2SGreg Kroah-Hartman       load_code_exit:
745ab4382d2SGreg Kroah-Hartman 
746ab4382d2SGreg Kroah-Hartman 	if (status != 0) {
747ab4382d2SGreg Kroah-Hartman 		/* Clear out any pending interrupts */
748ab4382d2SGreg Kroah-Hartman 		writew(0x3FFF, icom_port->int_reg);
749ab4382d2SGreg Kroah-Hartman 
750ab4382d2SGreg Kroah-Hartman 		/* Turn off port */
751ab4382d2SGreg Kroah-Hartman 		writeb(ICOM_DISABLE, &(icom_port->dram->disable));
752ab4382d2SGreg Kroah-Hartman 
753ab4382d2SGreg Kroah-Hartman 		/* Stop processor */
754ab4382d2SGreg Kroah-Hartman 		stop_processor(icom_port);
755ab4382d2SGreg Kroah-Hartman 
75646e99c4aSMasanari Iida 		dev_err(&icom_port->adapter->pci_dev->dev,"Port not operational\n");
757ab4382d2SGreg Kroah-Hartman 	}
758ab4382d2SGreg Kroah-Hartman 
759ab4382d2SGreg Kroah-Hartman 	if (new_page != NULL)
760c3647f2fSChristophe JAILLET 		dma_free_coherent(&dev->dev, 4096, new_page, temp_pci);
761ab4382d2SGreg Kroah-Hartman }
762ab4382d2SGreg Kroah-Hartman 
763ab4382d2SGreg Kroah-Hartman static int startup(struct icom_port *icom_port)
764ab4382d2SGreg Kroah-Hartman {
765ab4382d2SGreg Kroah-Hartman 	unsigned long temp;
766ab4382d2SGreg Kroah-Hartman 	unsigned char cable_id, raw_cable_id;
767ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
768ab4382d2SGreg Kroah-Hartman 	int port;
769ab4382d2SGreg Kroah-Hartman 
770ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "STARTUP", 0);
771ab4382d2SGreg Kroah-Hartman 
772ab4382d2SGreg Kroah-Hartman 	if (!icom_port->dram) {
773ab4382d2SGreg Kroah-Hartman 		/* should NEVER be NULL */
774ab4382d2SGreg Kroah-Hartman 		dev_err(&icom_port->adapter->pci_dev->dev,
775ab4382d2SGreg Kroah-Hartman 			"Unusable Port, port configuration missing\n");
776ab4382d2SGreg Kroah-Hartman 		return -ENODEV;
777ab4382d2SGreg Kroah-Hartman 	}
778ab4382d2SGreg Kroah-Hartman 
779ab4382d2SGreg Kroah-Hartman 	/*
780ab4382d2SGreg Kroah-Hartman 	 * check Cable ID
781ab4382d2SGreg Kroah-Hartman 	 */
782ab4382d2SGreg Kroah-Hartman 	raw_cable_id = readb(&icom_port->dram->cable_id);
783ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "CABLE_ID", raw_cable_id);
784ab4382d2SGreg Kroah-Hartman 
785ab4382d2SGreg Kroah-Hartman 	/* Get cable ID into the lower 4 bits (standard form) */
786ab4382d2SGreg Kroah-Hartman 	cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4;
787ab4382d2SGreg Kroah-Hartman 
788ab4382d2SGreg Kroah-Hartman 	/* Check for valid Cable ID */
789ab4382d2SGreg Kroah-Hartman 	if (!(raw_cable_id & ICOM_CABLE_ID_VALID) ||
790ab4382d2SGreg Kroah-Hartman 	    (cable_id != icom_port->cable_id)) {
791ab4382d2SGreg Kroah-Hartman 
792ab4382d2SGreg Kroah-Hartman 		/* reload adapter code, pick up any potential changes in cable id */
793ab4382d2SGreg Kroah-Hartman 		load_code(icom_port);
794ab4382d2SGreg Kroah-Hartman 
795ab4382d2SGreg Kroah-Hartman 		/* still no sign of cable, error out */
796ab4382d2SGreg Kroah-Hartman 		raw_cable_id = readb(&icom_port->dram->cable_id);
797ab4382d2SGreg Kroah-Hartman 		cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4;
798ab4382d2SGreg Kroah-Hartman 		if (!(raw_cable_id & ICOM_CABLE_ID_VALID) ||
799ab4382d2SGreg Kroah-Hartman 		    (icom_port->cable_id == NO_CABLE))
800ab4382d2SGreg Kroah-Hartman 			return -EIO;
801ab4382d2SGreg Kroah-Hartman 	}
802ab4382d2SGreg Kroah-Hartman 
803ab4382d2SGreg Kroah-Hartman 	/*
804ab4382d2SGreg Kroah-Hartman 	 * Finally, clear and  enable interrupts
805ab4382d2SGreg Kroah-Hartman 	 */
806ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&icom_lock, flags);
807ab4382d2SGreg Kroah-Hartman 	port = icom_port->port;
8084f03ffcdSDan Carpenter 	if (port >= ARRAY_SIZE(int_mask_tbl)) {
8094f03ffcdSDan Carpenter 		dev_err(&icom_port->adapter->pci_dev->dev,
8104f03ffcdSDan Carpenter 			"Invalid port assignment\n");
8114f03ffcdSDan Carpenter 		goto unlock;
8124f03ffcdSDan Carpenter 	}
8134f03ffcdSDan Carpenter 
814ab4382d2SGreg Kroah-Hartman 	if (port == 0 || port == 1)
815ab4382d2SGreg Kroah-Hartman 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
816ab4382d2SGreg Kroah-Hartman 	else
817ab4382d2SGreg Kroah-Hartman 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
818ab4382d2SGreg Kroah-Hartman 
819ab4382d2SGreg Kroah-Hartman 	if (port == 0 || port == 2)
820ab4382d2SGreg Kroah-Hartman 		writew(0x00FF, icom_port->int_reg);
821ab4382d2SGreg Kroah-Hartman 	else
822ab4382d2SGreg Kroah-Hartman 		writew(0x3F00, icom_port->int_reg);
8234f03ffcdSDan Carpenter 
824ab4382d2SGreg Kroah-Hartman 	temp = readl(int_mask_tbl[port].global_int_mask);
825ab4382d2SGreg Kroah-Hartman 	writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
826ab4382d2SGreg Kroah-Hartman 
827ab4382d2SGreg Kroah-Hartman 	/* write flush */
828ab4382d2SGreg Kroah-Hartman 	readl(int_mask_tbl[port].global_int_mask);
829ab4382d2SGreg Kroah-Hartman 
8304f03ffcdSDan Carpenter unlock:
831ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&icom_lock, flags);
832ab4382d2SGreg Kroah-Hartman 	return 0;
833ab4382d2SGreg Kroah-Hartman }
834ab4382d2SGreg Kroah-Hartman 
835ab4382d2SGreg Kroah-Hartman static void shutdown(struct icom_port *icom_port)
836ab4382d2SGreg Kroah-Hartman {
837ab4382d2SGreg Kroah-Hartman 	unsigned long temp;
838ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
839ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
840ab4382d2SGreg Kroah-Hartman 	int port;
841ab4382d2SGreg Kroah-Hartman 
842ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&icom_lock, flags);
843ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "SHUTDOWN", 0);
844ab4382d2SGreg Kroah-Hartman 
845ab4382d2SGreg Kroah-Hartman 	/*
846ab4382d2SGreg Kroah-Hartman 	 * disable all interrupts
847ab4382d2SGreg Kroah-Hartman 	 */
848ab4382d2SGreg Kroah-Hartman 	port = icom_port->port;
8494f03ffcdSDan Carpenter 	if (port >= ARRAY_SIZE(int_mask_tbl)) {
8504f03ffcdSDan Carpenter 		dev_err(&icom_port->adapter->pci_dev->dev,
8514f03ffcdSDan Carpenter 			"Invalid port assignment\n");
8524f03ffcdSDan Carpenter 		goto unlock;
8534f03ffcdSDan Carpenter 	}
854ab4382d2SGreg Kroah-Hartman 	if (port == 0 || port == 1)
855ab4382d2SGreg Kroah-Hartman 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
856ab4382d2SGreg Kroah-Hartman 	else
857ab4382d2SGreg Kroah-Hartman 		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
858ab4382d2SGreg Kroah-Hartman 
859ab4382d2SGreg Kroah-Hartman 	temp = readl(int_mask_tbl[port].global_int_mask);
860ab4382d2SGreg Kroah-Hartman 	writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
861ab4382d2SGreg Kroah-Hartman 
862ab4382d2SGreg Kroah-Hartman 	/* write flush */
863ab4382d2SGreg Kroah-Hartman 	readl(int_mask_tbl[port].global_int_mask);
8644f03ffcdSDan Carpenter 
8654f03ffcdSDan Carpenter unlock:
866ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&icom_lock, flags);
867ab4382d2SGreg Kroah-Hartman 
868ab4382d2SGreg Kroah-Hartman 	/*
869ab4382d2SGreg Kroah-Hartman 	 * disable break condition
870ab4382d2SGreg Kroah-Hartman 	 */
871ab4382d2SGreg Kroah-Hartman 	cmdReg = readb(&icom_port->dram->CmdReg);
872ab4382d2SGreg Kroah-Hartman 	if (cmdReg & CMD_SND_BREAK) {
873ab4382d2SGreg Kroah-Hartman 		writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg);
874ab4382d2SGreg Kroah-Hartman 	}
875ab4382d2SGreg Kroah-Hartman }
876ab4382d2SGreg Kroah-Hartman 
877ab4382d2SGreg Kroah-Hartman static int icom_write(struct uart_port *port)
878ab4382d2SGreg Kroah-Hartman {
879f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
880*1788cf6aSJiri Slaby (SUSE) 	struct tty_port *tport = &port->state->port;
881ab4382d2SGreg Kroah-Hartman 	unsigned long data_count;
882ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
883ab4382d2SGreg Kroah-Hartman 	unsigned long offset;
884ab4382d2SGreg Kroah-Hartman 
885f73989f5SJiri Slaby 	trace(icom_port, "WRITE", 0);
886ab4382d2SGreg Kroah-Hartman 
8877a5f86e8SJiri Slaby 	if (le16_to_cpu(icom_port->statStg->xmit[0].flags) &
888ab4382d2SGreg Kroah-Hartman 	    SA_FLAGS_READY_TO_XMIT) {
889f73989f5SJiri Slaby 		trace(icom_port, "WRITE_FULL", 0);
890ab4382d2SGreg Kroah-Hartman 		return 0;
891ab4382d2SGreg Kroah-Hartman 	}
892ab4382d2SGreg Kroah-Hartman 
893*1788cf6aSJiri Slaby (SUSE) 	data_count = kfifo_out_peek(&tport->xmit_fifo, icom_port->xmit_buf,
894*1788cf6aSJiri Slaby (SUSE) 			XMIT_BUFF_SZ);
895ab4382d2SGreg Kroah-Hartman 
896ab4382d2SGreg Kroah-Hartman 	if (data_count) {
897f73989f5SJiri Slaby 		icom_port->statStg->xmit[0].flags =
898ab4382d2SGreg Kroah-Hartman 		    cpu_to_le16(SA_FLAGS_READY_TO_XMIT);
899f73989f5SJiri Slaby 		icom_port->statStg->xmit[0].leLength =
900ab4382d2SGreg Kroah-Hartman 		    cpu_to_le16(data_count);
901ab4382d2SGreg Kroah-Hartman 		offset =
902f73989f5SJiri Slaby 		    (unsigned long) &icom_port->statStg->xmit[0] -
903f73989f5SJiri Slaby 		    (unsigned long) icom_port->statStg;
904f73989f5SJiri Slaby 		*icom_port->xmitRestart =
905f73989f5SJiri Slaby 		    cpu_to_le32(icom_port->statStg_pci + offset);
906f73989f5SJiri Slaby 		cmdReg = readb(&icom_port->dram->CmdReg);
907ab4382d2SGreg Kroah-Hartman 		writeb(cmdReg | CMD_XMIT_RCV_ENABLE,
908f73989f5SJiri Slaby 		       &icom_port->dram->CmdReg);
909f73989f5SJiri Slaby 		writeb(START_XMIT, &icom_port->dram->StartXmitCmd);
910f73989f5SJiri Slaby 		trace(icom_port, "WRITE_START", data_count);
911ab4382d2SGreg Kroah-Hartman 		/* write flush */
912f73989f5SJiri Slaby 		readb(&icom_port->dram->StartXmitCmd);
913ab4382d2SGreg Kroah-Hartman 	}
914ab4382d2SGreg Kroah-Hartman 
915ab4382d2SGreg Kroah-Hartman 	return data_count;
916ab4382d2SGreg Kroah-Hartman }
917ab4382d2SGreg Kroah-Hartman 
918ab4382d2SGreg Kroah-Hartman static inline void check_modem_status(struct icom_port *icom_port)
919ab4382d2SGreg Kroah-Hartman {
920ab4382d2SGreg Kroah-Hartman 	static char old_status = 0;
921ab4382d2SGreg Kroah-Hartman 	char delta_status;
922ab4382d2SGreg Kroah-Hartman 	unsigned char status;
923ab4382d2SGreg Kroah-Hartman 
924b4c7ba24SThomas Gleixner 	uart_port_lock(&icom_port->uart_port);
925ab4382d2SGreg Kroah-Hartman 
926ab4382d2SGreg Kroah-Hartman 	/*modem input register */
927ab4382d2SGreg Kroah-Hartman 	status = readb(&icom_port->dram->isr);
928ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "CHECK_MODEM", status);
929ab4382d2SGreg Kroah-Hartman 	delta_status = status ^ old_status;
930ab4382d2SGreg Kroah-Hartman 	if (delta_status) {
931ab4382d2SGreg Kroah-Hartman 		if (delta_status & ICOM_RI)
932ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.icount.rng++;
933ab4382d2SGreg Kroah-Hartman 		if (delta_status & ICOM_DSR)
934ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.icount.dsr++;
935ab4382d2SGreg Kroah-Hartman 		if (delta_status & ICOM_DCD)
936ab4382d2SGreg Kroah-Hartman 			uart_handle_dcd_change(&icom_port->uart_port,
937ab4382d2SGreg Kroah-Hartman 					       delta_status & ICOM_DCD);
938ab4382d2SGreg Kroah-Hartman 		if (delta_status & ICOM_CTS)
939ab4382d2SGreg Kroah-Hartman 			uart_handle_cts_change(&icom_port->uart_port,
940ab4382d2SGreg Kroah-Hartman 					       delta_status & ICOM_CTS);
941ab4382d2SGreg Kroah-Hartman 
942ab4382d2SGreg Kroah-Hartman 		wake_up_interruptible(&icom_port->uart_port.state->
943ab4382d2SGreg Kroah-Hartman 				      port.delta_msr_wait);
944ab4382d2SGreg Kroah-Hartman 		old_status = status;
945ab4382d2SGreg Kroah-Hartman 	}
946b4c7ba24SThomas Gleixner 	uart_port_unlock(&icom_port->uart_port);
947ab4382d2SGreg Kroah-Hartman }
948ab4382d2SGreg Kroah-Hartman 
949ab4382d2SGreg Kroah-Hartman static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
950ab4382d2SGreg Kroah-Hartman {
951*1788cf6aSJiri Slaby (SUSE) 	struct tty_port *tport = &icom_port->uart_port.state->port;
952*1788cf6aSJiri Slaby (SUSE) 	u16 count;
953ab4382d2SGreg Kroah-Hartman 
954ab4382d2SGreg Kroah-Hartman 	if (port_int_reg & (INT_XMIT_COMPLETED)) {
955ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "XMIT_COMPLETE", 0);
956ab4382d2SGreg Kroah-Hartman 
957ab4382d2SGreg Kroah-Hartman 		/* clear buffer in use bit */
958ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->xmit[0].flags &=
959ab4382d2SGreg Kroah-Hartman 			cpu_to_le16(~SA_FLAGS_READY_TO_XMIT);
960ab4382d2SGreg Kroah-Hartman 
9617a5f86e8SJiri Slaby 		count = le16_to_cpu(icom_port->statStg->xmit[0].leLength);
962ab4382d2SGreg Kroah-Hartman 		icom_port->uart_port.icount.tx += count;
963ab4382d2SGreg Kroah-Hartman 
964*1788cf6aSJiri Slaby (SUSE) 		kfifo_skip_count(&tport->xmit_fifo, count);
965ab4382d2SGreg Kroah-Hartman 
966ab4382d2SGreg Kroah-Hartman 		if (!icom_write(&icom_port->uart_port))
967ab4382d2SGreg Kroah-Hartman 			/* activate write queue */
968ab4382d2SGreg Kroah-Hartman 			uart_write_wakeup(&icom_port->uart_port);
969ab4382d2SGreg Kroah-Hartman 	} else
970ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "XMIT_DISABLED", 0);
971ab4382d2SGreg Kroah-Hartman }
972ab4382d2SGreg Kroah-Hartman 
973ab4382d2SGreg Kroah-Hartman static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port)
974ab4382d2SGreg Kroah-Hartman {
975ab4382d2SGreg Kroah-Hartman 	short int count, rcv_buff;
97692a19f9cSJiri Slaby 	struct tty_port *port = &icom_port->uart_port.state->port;
9777a5f86e8SJiri Slaby 	u16 status;
978ab4382d2SGreg Kroah-Hartman 	struct uart_icount *icount;
979ab4382d2SGreg Kroah-Hartman 	unsigned long offset;
980ab4382d2SGreg Kroah-Hartman 	unsigned char flag;
981ab4382d2SGreg Kroah-Hartman 
982ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "RCV_COMPLETE", 0);
983ab4382d2SGreg Kroah-Hartman 	rcv_buff = icom_port->next_rcv;
984ab4382d2SGreg Kroah-Hartman 
9857a5f86e8SJiri Slaby 	status = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].flags);
986ab4382d2SGreg Kroah-Hartman 	while (status & SA_FL_RCV_DONE) {
987ab4382d2SGreg Kroah-Hartman 		int first = -1;
988ab4382d2SGreg Kroah-Hartman 
989ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "FID_STATUS", status);
9907a5f86e8SJiri Slaby 		count = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].leLength);
991ab4382d2SGreg Kroah-Hartman 
992ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "RCV_COUNT", count);
993ab4382d2SGreg Kroah-Hartman 
994ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "REAL_COUNT", count);
995ab4382d2SGreg Kroah-Hartman 
9967a5f86e8SJiri Slaby 		offset = le32_to_cpu(icom_port->statStg->rcv[rcv_buff].leBuffer) -
997ab4382d2SGreg Kroah-Hartman 			icom_port->recv_buf_pci;
998ab4382d2SGreg Kroah-Hartman 
999ab4382d2SGreg Kroah-Hartman 		/* Block copy all but the last byte as this may have status */
1000ab4382d2SGreg Kroah-Hartman 		if (count > 0) {
1001ab4382d2SGreg Kroah-Hartman 			first = icom_port->recv_buf[offset];
100205c7cd39SJiri Slaby 			tty_insert_flip_string(port, icom_port->recv_buf + offset, count - 1);
1003ab4382d2SGreg Kroah-Hartman 		}
1004ab4382d2SGreg Kroah-Hartman 
1005ab4382d2SGreg Kroah-Hartman 		icount = &icom_port->uart_port.icount;
1006ab4382d2SGreg Kroah-Hartman 		icount->rx += count;
1007ab4382d2SGreg Kroah-Hartman 
1008ab4382d2SGreg Kroah-Hartman 		/* Break detect logic */
1009ab4382d2SGreg Kroah-Hartman 		if ((status & SA_FLAGS_FRAME_ERROR)
1010ab4382d2SGreg Kroah-Hartman 		    && first == 0) {
1011ab4382d2SGreg Kroah-Hartman 			status &= ~SA_FLAGS_FRAME_ERROR;
1012ab4382d2SGreg Kroah-Hartman 			status |= SA_FLAGS_BREAK_DET;
1013ab4382d2SGreg Kroah-Hartman 			trace(icom_port, "BREAK_DET", 0);
1014ab4382d2SGreg Kroah-Hartman 		}
1015ab4382d2SGreg Kroah-Hartman 
1016ab4382d2SGreg Kroah-Hartman 		flag = TTY_NORMAL;
1017ab4382d2SGreg Kroah-Hartman 
1018ab4382d2SGreg Kroah-Hartman 		if (status &
1019ab4382d2SGreg Kroah-Hartman 		    (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR |
1020ab4382d2SGreg Kroah-Hartman 		     SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) {
1021ab4382d2SGreg Kroah-Hartman 
1022ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_BREAK_DET)
1023ab4382d2SGreg Kroah-Hartman 				icount->brk++;
1024ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_PARITY_ERROR)
1025ab4382d2SGreg Kroah-Hartman 				icount->parity++;
1026ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_FRAME_ERROR)
1027ab4382d2SGreg Kroah-Hartman 				icount->frame++;
1028ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_OVERRUN)
1029ab4382d2SGreg Kroah-Hartman 				icount->overrun++;
1030ab4382d2SGreg Kroah-Hartman 
1031ab4382d2SGreg Kroah-Hartman 			/*
1032ab4382d2SGreg Kroah-Hartman 			 * Now check to see if character should be
1033ab4382d2SGreg Kroah-Hartman 			 * ignored, and mask off conditions which
1034ab4382d2SGreg Kroah-Hartman 			 * should be ignored.
1035ab4382d2SGreg Kroah-Hartman 			 */
1036ab4382d2SGreg Kroah-Hartman 			if (status & icom_port->ignore_status_mask) {
1037ab4382d2SGreg Kroah-Hartman 				trace(icom_port, "IGNORE_CHAR", 0);
1038ab4382d2SGreg Kroah-Hartman 				goto ignore_char;
1039ab4382d2SGreg Kroah-Hartman 			}
1040ab4382d2SGreg Kroah-Hartman 
1041ab4382d2SGreg Kroah-Hartman 			status &= icom_port->read_status_mask;
1042ab4382d2SGreg Kroah-Hartman 
1043ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_BREAK_DET) {
1044ab4382d2SGreg Kroah-Hartman 				flag = TTY_BREAK;
1045ab4382d2SGreg Kroah-Hartman 			} else if (status & SA_FLAGS_PARITY_ERROR) {
1046ab4382d2SGreg Kroah-Hartman 				trace(icom_port, "PARITY_ERROR", 0);
1047ab4382d2SGreg Kroah-Hartman 				flag = TTY_PARITY;
1048ab4382d2SGreg Kroah-Hartman 			} else if (status & SA_FLAGS_FRAME_ERROR)
1049ab4382d2SGreg Kroah-Hartman 				flag = TTY_FRAME;
1050ab4382d2SGreg Kroah-Hartman 
1051ab4382d2SGreg Kroah-Hartman 		}
1052ab4382d2SGreg Kroah-Hartman 
105392a19f9cSJiri Slaby 		tty_insert_flip_char(port, *(icom_port->recv_buf + offset + count - 1), flag);
1054ab4382d2SGreg Kroah-Hartman 
1055ab4382d2SGreg Kroah-Hartman 		if (status & SA_FLAGS_OVERRUN)
1056ab4382d2SGreg Kroah-Hartman 			/*
1057ab4382d2SGreg Kroah-Hartman 			 * Overrun is special, since it's
1058ab4382d2SGreg Kroah-Hartman 			 * reported immediately, and doesn't
1059ab4382d2SGreg Kroah-Hartman 			 * affect the current character
1060ab4382d2SGreg Kroah-Hartman 			 */
106192a19f9cSJiri Slaby 			tty_insert_flip_char(port, 0, TTY_OVERRUN);
1062ab4382d2SGreg Kroah-Hartman ignore_char:
1063ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->rcv[rcv_buff].flags = 0;
1064ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->rcv[rcv_buff].leLength = 0;
1065ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->rcv[rcv_buff].WorkingLength =
10667a5f86e8SJiri Slaby 			cpu_to_le16(RCV_BUFF_SZ);
1067ab4382d2SGreg Kroah-Hartman 
1068ab4382d2SGreg Kroah-Hartman 		rcv_buff++;
1069ab4382d2SGreg Kroah-Hartman 		if (rcv_buff == NUM_RBUFFS)
1070ab4382d2SGreg Kroah-Hartman 			rcv_buff = 0;
1071ab4382d2SGreg Kroah-Hartman 
10727a5f86e8SJiri Slaby 		status = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].flags);
1073ab4382d2SGreg Kroah-Hartman 	}
1074ab4382d2SGreg Kroah-Hartman 	icom_port->next_rcv = rcv_buff;
10755faf75d7SViresh Kumar 
10762e124b4aSJiri Slaby 	tty_flip_buffer_push(port);
1077ab4382d2SGreg Kroah-Hartman }
1078ab4382d2SGreg Kroah-Hartman 
1079ab4382d2SGreg Kroah-Hartman static void process_interrupt(u16 port_int_reg,
1080ab4382d2SGreg Kroah-Hartman 			      struct icom_port *icom_port)
1081ab4382d2SGreg Kroah-Hartman {
1082ab4382d2SGreg Kroah-Hartman 
1083b4c7ba24SThomas Gleixner 	uart_port_lock(&icom_port->uart_port);
1084ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "INTERRUPT", port_int_reg);
1085ab4382d2SGreg Kroah-Hartman 
1086ab4382d2SGreg Kroah-Hartman 	if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED))
1087ab4382d2SGreg Kroah-Hartman 		xmit_interrupt(port_int_reg, icom_port);
1088ab4382d2SGreg Kroah-Hartman 
1089ab4382d2SGreg Kroah-Hartman 	if (port_int_reg & INT_RCV_COMPLETED)
1090ab4382d2SGreg Kroah-Hartman 		recv_interrupt(port_int_reg, icom_port);
1091ab4382d2SGreg Kroah-Hartman 
1092b4c7ba24SThomas Gleixner 	uart_port_unlock(&icom_port->uart_port);
1093ab4382d2SGreg Kroah-Hartman }
1094ab4382d2SGreg Kroah-Hartman 
1095ab4382d2SGreg Kroah-Hartman static irqreturn_t icom_interrupt(int irq, void *dev_id)
1096ab4382d2SGreg Kroah-Hartman {
1097ab4382d2SGreg Kroah-Hartman 	void __iomem * int_reg;
1098ab4382d2SGreg Kroah-Hartman 	u32 adapter_interrupts;
1099ab4382d2SGreg Kroah-Hartman 	u16 port_int_reg;
1100ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *icom_adapter;
1101ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1102ab4382d2SGreg Kroah-Hartman 
1103ab4382d2SGreg Kroah-Hartman 	/* find icom_port for this interrupt */
1104ab4382d2SGreg Kroah-Hartman 	icom_adapter = (struct icom_adapter *) dev_id;
1105ab4382d2SGreg Kroah-Hartman 
1106ab4382d2SGreg Kroah-Hartman 	if (icom_adapter->version == ADAPTER_V2) {
1107ab4382d2SGreg Kroah-Hartman 		int_reg = icom_adapter->base_addr + 0x8024;
1108ab4382d2SGreg Kroah-Hartman 
1109ab4382d2SGreg Kroah-Hartman 		adapter_interrupts = readl(int_reg);
1110ab4382d2SGreg Kroah-Hartman 
1111ab4382d2SGreg Kroah-Hartman 		if (adapter_interrupts & 0x00003FFF) {
1112ab4382d2SGreg Kroah-Hartman 			/* port 2 interrupt,  NOTE:  for all ADAPTER_V2, port 2 will be active */
1113ab4382d2SGreg Kroah-Hartman 			icom_port = &icom_adapter->port_info[2];
1114ab4382d2SGreg Kroah-Hartman 			port_int_reg = (u16) adapter_interrupts;
1115ab4382d2SGreg Kroah-Hartman 			process_interrupt(port_int_reg, icom_port);
1116ab4382d2SGreg Kroah-Hartman 			check_modem_status(icom_port);
1117ab4382d2SGreg Kroah-Hartman 		}
1118ab4382d2SGreg Kroah-Hartman 		if (adapter_interrupts & 0x3FFF0000) {
1119ab4382d2SGreg Kroah-Hartman 			/* port 3 interrupt */
1120ab4382d2SGreg Kroah-Hartman 			icom_port = &icom_adapter->port_info[3];
1121ab4382d2SGreg Kroah-Hartman 			if (icom_port->status == ICOM_PORT_ACTIVE) {
1122ab4382d2SGreg Kroah-Hartman 				port_int_reg =
1123ab4382d2SGreg Kroah-Hartman 				    (u16) (adapter_interrupts >> 16);
1124ab4382d2SGreg Kroah-Hartman 				process_interrupt(port_int_reg, icom_port);
1125ab4382d2SGreg Kroah-Hartman 				check_modem_status(icom_port);
1126ab4382d2SGreg Kroah-Hartman 			}
1127ab4382d2SGreg Kroah-Hartman 		}
1128ab4382d2SGreg Kroah-Hartman 
1129ab4382d2SGreg Kroah-Hartman 		/* Clear out any pending interrupts */
1130ab4382d2SGreg Kroah-Hartman 		writel(adapter_interrupts, int_reg);
1131ab4382d2SGreg Kroah-Hartman 
1132ab4382d2SGreg Kroah-Hartman 		int_reg = icom_adapter->base_addr + 0x8004;
1133ab4382d2SGreg Kroah-Hartman 	} else {
1134ab4382d2SGreg Kroah-Hartman 		int_reg = icom_adapter->base_addr + 0x4004;
1135ab4382d2SGreg Kroah-Hartman 	}
1136ab4382d2SGreg Kroah-Hartman 
1137ab4382d2SGreg Kroah-Hartman 	adapter_interrupts = readl(int_reg);
1138ab4382d2SGreg Kroah-Hartman 
1139ab4382d2SGreg Kroah-Hartman 	if (adapter_interrupts & 0x00003FFF) {
1140ab4382d2SGreg Kroah-Hartman 		/* port 0 interrupt, NOTE:  for all adapters, port 0 will be active */
1141ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[0];
1142ab4382d2SGreg Kroah-Hartman 		port_int_reg = (u16) adapter_interrupts;
1143ab4382d2SGreg Kroah-Hartman 		process_interrupt(port_int_reg, icom_port);
1144ab4382d2SGreg Kroah-Hartman 		check_modem_status(icom_port);
1145ab4382d2SGreg Kroah-Hartman 	}
1146ab4382d2SGreg Kroah-Hartman 	if (adapter_interrupts & 0x3FFF0000) {
1147ab4382d2SGreg Kroah-Hartman 		/* port 1 interrupt */
1148ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[1];
1149ab4382d2SGreg Kroah-Hartman 		if (icom_port->status == ICOM_PORT_ACTIVE) {
1150ab4382d2SGreg Kroah-Hartman 			port_int_reg = (u16) (adapter_interrupts >> 16);
1151ab4382d2SGreg Kroah-Hartman 			process_interrupt(port_int_reg, icom_port);
1152ab4382d2SGreg Kroah-Hartman 			check_modem_status(icom_port);
1153ab4382d2SGreg Kroah-Hartman 		}
1154ab4382d2SGreg Kroah-Hartman 	}
1155ab4382d2SGreg Kroah-Hartman 
1156ab4382d2SGreg Kroah-Hartman 	/* Clear out any pending interrupts */
1157ab4382d2SGreg Kroah-Hartman 	writel(adapter_interrupts, int_reg);
1158ab4382d2SGreg Kroah-Hartman 
1159ab4382d2SGreg Kroah-Hartman 	/* flush the write */
1160ab4382d2SGreg Kroah-Hartman 	adapter_interrupts = readl(int_reg);
1161ab4382d2SGreg Kroah-Hartman 
1162ab4382d2SGreg Kroah-Hartman 	return IRQ_HANDLED;
1163ab4382d2SGreg Kroah-Hartman }
1164ab4382d2SGreg Kroah-Hartman 
1165ab4382d2SGreg Kroah-Hartman /*
1166ab4382d2SGreg Kroah-Hartman  * ------------------------------------------------------------------
1167ab4382d2SGreg Kroah-Hartman  * Begin serial-core API
1168ab4382d2SGreg Kroah-Hartman  * ------------------------------------------------------------------
1169ab4382d2SGreg Kroah-Hartman  */
1170ab4382d2SGreg Kroah-Hartman static unsigned int icom_tx_empty(struct uart_port *port)
1171ab4382d2SGreg Kroah-Hartman {
1172f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1173ab4382d2SGreg Kroah-Hartman 	int ret;
1174ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1175ab4382d2SGreg Kroah-Hartman 
1176b4c7ba24SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
11777a5f86e8SJiri Slaby 	if (le16_to_cpu(icom_port->statStg->xmit[0].flags) &
1178ab4382d2SGreg Kroah-Hartman 	    SA_FLAGS_READY_TO_XMIT)
1179ab4382d2SGreg Kroah-Hartman 		ret = TIOCSER_TEMT;
1180ab4382d2SGreg Kroah-Hartman 	else
1181ab4382d2SGreg Kroah-Hartman 		ret = 0;
1182ab4382d2SGreg Kroah-Hartman 
1183b4c7ba24SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
1184ab4382d2SGreg Kroah-Hartman 	return ret;
1185ab4382d2SGreg Kroah-Hartman }
1186ab4382d2SGreg Kroah-Hartman 
1187ab4382d2SGreg Kroah-Hartman static void icom_set_mctrl(struct uart_port *port, unsigned int mctrl)
1188ab4382d2SGreg Kroah-Hartman {
1189f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1190ab4382d2SGreg Kroah-Hartman 	unsigned char local_osr;
1191ab4382d2SGreg Kroah-Hartman 
1192f73989f5SJiri Slaby 	trace(icom_port, "SET_MODEM", 0);
1193f73989f5SJiri Slaby 	local_osr = readb(&icom_port->dram->osr);
1194ab4382d2SGreg Kroah-Hartman 
1195ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_RTS) {
1196f73989f5SJiri Slaby 		trace(icom_port, "RAISE_RTS", 0);
1197ab4382d2SGreg Kroah-Hartman 		local_osr |= ICOM_RTS;
1198ab4382d2SGreg Kroah-Hartman 	} else {
1199f73989f5SJiri Slaby 		trace(icom_port, "LOWER_RTS", 0);
1200ab4382d2SGreg Kroah-Hartman 		local_osr &= ~ICOM_RTS;
1201ab4382d2SGreg Kroah-Hartman 	}
1202ab4382d2SGreg Kroah-Hartman 
1203ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_DTR) {
1204f73989f5SJiri Slaby 		trace(icom_port, "RAISE_DTR", 0);
1205ab4382d2SGreg Kroah-Hartman 		local_osr |= ICOM_DTR;
1206ab4382d2SGreg Kroah-Hartman 	} else {
1207f73989f5SJiri Slaby 		trace(icom_port, "LOWER_DTR", 0);
1208ab4382d2SGreg Kroah-Hartman 		local_osr &= ~ICOM_DTR;
1209ab4382d2SGreg Kroah-Hartman 	}
1210ab4382d2SGreg Kroah-Hartman 
1211f73989f5SJiri Slaby 	writeb(local_osr, &icom_port->dram->osr);
1212ab4382d2SGreg Kroah-Hartman }
1213ab4382d2SGreg Kroah-Hartman 
1214ab4382d2SGreg Kroah-Hartman static unsigned int icom_get_mctrl(struct uart_port *port)
1215ab4382d2SGreg Kroah-Hartman {
1216f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1217ab4382d2SGreg Kroah-Hartman 	unsigned char status;
1218ab4382d2SGreg Kroah-Hartman 	unsigned int result;
1219ab4382d2SGreg Kroah-Hartman 
1220f73989f5SJiri Slaby 	trace(icom_port, "GET_MODEM", 0);
1221ab4382d2SGreg Kroah-Hartman 
1222f73989f5SJiri Slaby 	status = readb(&icom_port->dram->isr);
1223ab4382d2SGreg Kroah-Hartman 
1224ab4382d2SGreg Kroah-Hartman 	result = ((status & ICOM_DCD) ? TIOCM_CAR : 0)
1225ab4382d2SGreg Kroah-Hartman 	    | ((status & ICOM_RI) ? TIOCM_RNG : 0)
1226ab4382d2SGreg Kroah-Hartman 	    | ((status & ICOM_DSR) ? TIOCM_DSR : 0)
1227ab4382d2SGreg Kroah-Hartman 	    | ((status & ICOM_CTS) ? TIOCM_CTS : 0);
1228ab4382d2SGreg Kroah-Hartman 	return result;
1229ab4382d2SGreg Kroah-Hartman }
1230ab4382d2SGreg Kroah-Hartman 
1231ab4382d2SGreg Kroah-Hartman static void icom_stop_tx(struct uart_port *port)
1232ab4382d2SGreg Kroah-Hartman {
1233f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1234ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1235ab4382d2SGreg Kroah-Hartman 
1236f73989f5SJiri Slaby 	trace(icom_port, "STOP", 0);
1237f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1238f73989f5SJiri Slaby 	writeb(cmdReg | CMD_HOLD_XMIT, &icom_port->dram->CmdReg);
1239ab4382d2SGreg Kroah-Hartman }
1240ab4382d2SGreg Kroah-Hartman 
1241ab4382d2SGreg Kroah-Hartman static void icom_start_tx(struct uart_port *port)
1242ab4382d2SGreg Kroah-Hartman {
1243f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1244ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1245ab4382d2SGreg Kroah-Hartman 
1246f73989f5SJiri Slaby 	trace(icom_port, "START", 0);
1247f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1248ab4382d2SGreg Kroah-Hartman 	if ((cmdReg & CMD_HOLD_XMIT) == CMD_HOLD_XMIT)
1249ab4382d2SGreg Kroah-Hartman 		writeb(cmdReg & ~CMD_HOLD_XMIT,
1250f73989f5SJiri Slaby 		       &icom_port->dram->CmdReg);
1251ab4382d2SGreg Kroah-Hartman 
1252ab4382d2SGreg Kroah-Hartman 	icom_write(port);
1253ab4382d2SGreg Kroah-Hartman }
1254ab4382d2SGreg Kroah-Hartman 
1255ab4382d2SGreg Kroah-Hartman static void icom_send_xchar(struct uart_port *port, char ch)
1256ab4382d2SGreg Kroah-Hartman {
1257f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1258ab4382d2SGreg Kroah-Hartman 	unsigned char xdata;
1259ab4382d2SGreg Kroah-Hartman 	int index;
1260ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1261ab4382d2SGreg Kroah-Hartman 
1262f73989f5SJiri Slaby 	trace(icom_port, "SEND_XCHAR", ch);
1263ab4382d2SGreg Kroah-Hartman 
1264ab4382d2SGreg Kroah-Hartman 	/* wait .1 sec to send char */
1265ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < 10; index++) {
1266b4c7ba24SThomas Gleixner 		uart_port_lock_irqsave(port, &flags);
1267f73989f5SJiri Slaby 		xdata = readb(&icom_port->dram->xchar);
1268ab4382d2SGreg Kroah-Hartman 		if (xdata == 0x00) {
1269f73989f5SJiri Slaby 			trace(icom_port, "QUICK_WRITE", 0);
1270f73989f5SJiri Slaby 			writeb(ch, &icom_port->dram->xchar);
1271ab4382d2SGreg Kroah-Hartman 
1272ab4382d2SGreg Kroah-Hartman 			/* flush write operation */
1273f73989f5SJiri Slaby 			xdata = readb(&icom_port->dram->xchar);
1274b4c7ba24SThomas Gleixner 			uart_port_unlock_irqrestore(port, flags);
1275ab4382d2SGreg Kroah-Hartman 			break;
1276ab4382d2SGreg Kroah-Hartman 		}
1277b4c7ba24SThomas Gleixner 		uart_port_unlock_irqrestore(port, flags);
1278ab4382d2SGreg Kroah-Hartman 		msleep(10);
1279ab4382d2SGreg Kroah-Hartman 	}
1280ab4382d2SGreg Kroah-Hartman }
1281ab4382d2SGreg Kroah-Hartman 
1282ab4382d2SGreg Kroah-Hartman static void icom_stop_rx(struct uart_port *port)
1283ab4382d2SGreg Kroah-Hartman {
1284f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1285ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1286ab4382d2SGreg Kroah-Hartman 
1287f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1288f73989f5SJiri Slaby 	writeb(cmdReg & ~CMD_RCV_ENABLE, &icom_port->dram->CmdReg);
1289ab4382d2SGreg Kroah-Hartman }
1290ab4382d2SGreg Kroah-Hartman 
1291ab4382d2SGreg Kroah-Hartman static void icom_break(struct uart_port *port, int break_state)
1292ab4382d2SGreg Kroah-Hartman {
1293f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1294ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1295ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1296ab4382d2SGreg Kroah-Hartman 
1297b4c7ba24SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
1298f73989f5SJiri Slaby 	trace(icom_port, "BREAK", 0);
1299f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1300ab4382d2SGreg Kroah-Hartman 	if (break_state == -1) {
1301f73989f5SJiri Slaby 		writeb(cmdReg | CMD_SND_BREAK, &icom_port->dram->CmdReg);
1302ab4382d2SGreg Kroah-Hartman 	} else {
1303f73989f5SJiri Slaby 		writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg);
1304ab4382d2SGreg Kroah-Hartman 	}
1305b4c7ba24SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
1306ab4382d2SGreg Kroah-Hartman }
1307ab4382d2SGreg Kroah-Hartman 
1308ab4382d2SGreg Kroah-Hartman static int icom_open(struct uart_port *port)
1309ab4382d2SGreg Kroah-Hartman {
1310f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1311ab4382d2SGreg Kroah-Hartman 	int retval;
1312ab4382d2SGreg Kroah-Hartman 
1313f73989f5SJiri Slaby 	kref_get(&icom_port->adapter->kref);
1314f73989f5SJiri Slaby 	retval = startup(icom_port);
1315ab4382d2SGreg Kroah-Hartman 
1316ab4382d2SGreg Kroah-Hartman 	if (retval) {
1317f73989f5SJiri Slaby 		kref_put(&icom_port->adapter->kref, icom_kref_release);
1318f73989f5SJiri Slaby 		trace(icom_port, "STARTUP_ERROR", 0);
1319ab4382d2SGreg Kroah-Hartman 		return retval;
1320ab4382d2SGreg Kroah-Hartman 	}
1321ab4382d2SGreg Kroah-Hartman 
1322ab4382d2SGreg Kroah-Hartman 	return 0;
1323ab4382d2SGreg Kroah-Hartman }
1324ab4382d2SGreg Kroah-Hartman 
1325ab4382d2SGreg Kroah-Hartman static void icom_close(struct uart_port *port)
1326ab4382d2SGreg Kroah-Hartman {
1327f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1328ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1329ab4382d2SGreg Kroah-Hartman 
1330f73989f5SJiri Slaby 	trace(icom_port, "CLOSE", 0);
1331ab4382d2SGreg Kroah-Hartman 
1332ab4382d2SGreg Kroah-Hartman 	/* stop receiver */
1333f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1334f73989f5SJiri Slaby 	writeb(cmdReg & ~CMD_RCV_ENABLE, &icom_port->dram->CmdReg);
1335ab4382d2SGreg Kroah-Hartman 
1336f73989f5SJiri Slaby 	shutdown(icom_port);
1337ab4382d2SGreg Kroah-Hartman 
1338f73989f5SJiri Slaby 	kref_put(&icom_port->adapter->kref, icom_kref_release);
1339ab4382d2SGreg Kroah-Hartman }
1340ab4382d2SGreg Kroah-Hartman 
1341bec5b814SIlpo Järvinen static void icom_set_termios(struct uart_port *port, struct ktermios *termios,
1342bec5b814SIlpo Järvinen 			     const struct ktermios *old_termios)
1343ab4382d2SGreg Kroah-Hartman {
1344f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1345ab4382d2SGreg Kroah-Hartman 	int baud;
1346ab4382d2SGreg Kroah-Hartman 	unsigned cflag, iflag;
1347ab4382d2SGreg Kroah-Hartman 	char new_config2;
1348ab4382d2SGreg Kroah-Hartman 	char new_config3 = 0;
1349ab4382d2SGreg Kroah-Hartman 	char tmp_byte;
1350ab4382d2SGreg Kroah-Hartman 	int index;
1351ab4382d2SGreg Kroah-Hartman 	int rcv_buff, xmit_buff;
1352ab4382d2SGreg Kroah-Hartman 	unsigned long offset;
1353ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1354ab4382d2SGreg Kroah-Hartman 
1355b4c7ba24SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
1356f73989f5SJiri Slaby 	trace(icom_port, "CHANGE_SPEED", 0);
1357ab4382d2SGreg Kroah-Hartman 
1358ab4382d2SGreg Kroah-Hartman 	cflag = termios->c_cflag;
1359ab4382d2SGreg Kroah-Hartman 	iflag = termios->c_iflag;
1360ab4382d2SGreg Kroah-Hartman 
1361ab4382d2SGreg Kroah-Hartman 	new_config2 = ICOM_ACFG_DRIVE1;
1362ab4382d2SGreg Kroah-Hartman 
1363ab4382d2SGreg Kroah-Hartman 	/* byte size and parity */
1364ab4382d2SGreg Kroah-Hartman 	switch (cflag & CSIZE) {
1365ab4382d2SGreg Kroah-Hartman 	case CS5:		/* 5 bits/char */
1366ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_5BPC;
1367ab4382d2SGreg Kroah-Hartman 		break;
1368ab4382d2SGreg Kroah-Hartman 	case CS6:		/* 6 bits/char */
1369ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_6BPC;
1370ab4382d2SGreg Kroah-Hartman 		break;
1371ab4382d2SGreg Kroah-Hartman 	case CS7:		/* 7 bits/char */
1372ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_7BPC;
1373ab4382d2SGreg Kroah-Hartman 		break;
1374ab4382d2SGreg Kroah-Hartman 	case CS8:		/* 8 bits/char */
1375ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_8BPC;
1376ab4382d2SGreg Kroah-Hartman 		break;
1377ab4382d2SGreg Kroah-Hartman 	default:
1378ab4382d2SGreg Kroah-Hartman 		break;
1379ab4382d2SGreg Kroah-Hartman 	}
1380ab4382d2SGreg Kroah-Hartman 	if (cflag & CSTOPB) {
1381ab4382d2SGreg Kroah-Hartman 		/* 2 stop bits */
1382ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_2STOP_BIT;
1383ab4382d2SGreg Kroah-Hartman 	}
1384ab4382d2SGreg Kroah-Hartman 	if (cflag & PARENB) {
1385ab4382d2SGreg Kroah-Hartman 		/* parity bit enabled */
1386ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_PARITY_ENAB;
1387f73989f5SJiri Slaby 		trace(icom_port, "PARENB", 0);
1388ab4382d2SGreg Kroah-Hartman 	}
1389ab4382d2SGreg Kroah-Hartman 	if (cflag & PARODD) {
1390ab4382d2SGreg Kroah-Hartman 		/* odd parity */
1391ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_PARITY_ODD;
1392f73989f5SJiri Slaby 		trace(icom_port, "PARODD", 0);
1393ab4382d2SGreg Kroah-Hartman 	}
1394ab4382d2SGreg Kroah-Hartman 
1395ab4382d2SGreg Kroah-Hartman 	/* Determine divisor based on baud rate */
1396ab4382d2SGreg Kroah-Hartman 	baud = uart_get_baud_rate(port, termios, old_termios,
1397ab4382d2SGreg Kroah-Hartman 				  icom_acfg_baud[0],
1398ab4382d2SGreg Kroah-Hartman 				  icom_acfg_baud[BAUD_TABLE_LIMIT]);
1399ab4382d2SGreg Kroah-Hartman 	if (!baud)
1400ab4382d2SGreg Kroah-Hartman 		baud = 9600;	/* B0 transition handled in rs_set_termios */
1401ab4382d2SGreg Kroah-Hartman 
1402ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < BAUD_TABLE_LIMIT; index++) {
1403ab4382d2SGreg Kroah-Hartman 		if (icom_acfg_baud[index] == baud) {
1404ab4382d2SGreg Kroah-Hartman 			new_config3 = index;
1405ab4382d2SGreg Kroah-Hartman 			break;
1406ab4382d2SGreg Kroah-Hartman 		}
1407ab4382d2SGreg Kroah-Hartman 	}
1408ab4382d2SGreg Kroah-Hartman 
1409ab4382d2SGreg Kroah-Hartman 	uart_update_timeout(port, cflag, baud);
1410ab4382d2SGreg Kroah-Hartman 
1411ab4382d2SGreg Kroah-Hartman 	/* CTS flow control flag and modem status interrupts */
1412f73989f5SJiri Slaby 	tmp_byte = readb(&(icom_port->dram->HDLCConfigReg));
1413ab4382d2SGreg Kroah-Hartman 	if (cflag & CRTSCTS)
1414ab4382d2SGreg Kroah-Hartman 		tmp_byte |= HDLC_HDW_FLOW;
1415ab4382d2SGreg Kroah-Hartman 	else
1416ab4382d2SGreg Kroah-Hartman 		tmp_byte &= ~HDLC_HDW_FLOW;
1417f73989f5SJiri Slaby 	writeb(tmp_byte, &(icom_port->dram->HDLCConfigReg));
1418ab4382d2SGreg Kroah-Hartman 
1419ab4382d2SGreg Kroah-Hartman 	/*
1420ab4382d2SGreg Kroah-Hartman 	 * Set up parity check flag
1421ab4382d2SGreg Kroah-Hartman 	 */
1422f73989f5SJiri Slaby 	icom_port->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE;
1423ab4382d2SGreg Kroah-Hartman 	if (iflag & INPCK)
1424f73989f5SJiri Slaby 		icom_port->read_status_mask |=
1425ab4382d2SGreg Kroah-Hartman 		    SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR;
1426ab4382d2SGreg Kroah-Hartman 
1427ab4382d2SGreg Kroah-Hartman 	if ((iflag & BRKINT) || (iflag & PARMRK))
1428f73989f5SJiri Slaby 		icom_port->read_status_mask |= SA_FLAGS_BREAK_DET;
1429ab4382d2SGreg Kroah-Hartman 
1430ab4382d2SGreg Kroah-Hartman 	/*
1431ab4382d2SGreg Kroah-Hartman 	 * Characters to ignore
1432ab4382d2SGreg Kroah-Hartman 	 */
1433f73989f5SJiri Slaby 	icom_port->ignore_status_mask = 0;
1434ab4382d2SGreg Kroah-Hartman 	if (iflag & IGNPAR)
1435f73989f5SJiri Slaby 		icom_port->ignore_status_mask |=
1436ab4382d2SGreg Kroah-Hartman 		    SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR;
1437ab4382d2SGreg Kroah-Hartman 	if (iflag & IGNBRK) {
1438f73989f5SJiri Slaby 		icom_port->ignore_status_mask |= SA_FLAGS_BREAK_DET;
1439ab4382d2SGreg Kroah-Hartman 		/*
1440ab4382d2SGreg Kroah-Hartman 		 * If we're ignore parity and break indicators, ignore
1441ab4382d2SGreg Kroah-Hartman 		 * overruns too.  (For real raw support).
1442ab4382d2SGreg Kroah-Hartman 		 */
1443ab4382d2SGreg Kroah-Hartman 		if (iflag & IGNPAR)
1444f73989f5SJiri Slaby 			icom_port->ignore_status_mask |= SA_FLAGS_OVERRUN;
1445ab4382d2SGreg Kroah-Hartman 	}
1446ab4382d2SGreg Kroah-Hartman 
1447ab4382d2SGreg Kroah-Hartman 	/*
1448ab4382d2SGreg Kroah-Hartman 	 * !!! ignore all characters if CREAD is not set
1449ab4382d2SGreg Kroah-Hartman 	 */
1450ab4382d2SGreg Kroah-Hartman 	if ((cflag & CREAD) == 0)
1451f73989f5SJiri Slaby 		icom_port->ignore_status_mask |= SA_FL_RCV_DONE;
1452ab4382d2SGreg Kroah-Hartman 
1453ab4382d2SGreg Kroah-Hartman 	/* Turn off Receiver to prepare for reset */
1454f73989f5SJiri Slaby 	writeb(CMD_RCV_DISABLE, &icom_port->dram->CmdReg);
1455ab4382d2SGreg Kroah-Hartman 
1456ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < 10; index++) {
1457f73989f5SJiri Slaby 		if (readb(&icom_port->dram->PrevCmdReg) == 0x00) {
1458ab4382d2SGreg Kroah-Hartman 			break;
1459ab4382d2SGreg Kroah-Hartman 		}
1460ab4382d2SGreg Kroah-Hartman 	}
1461ab4382d2SGreg Kroah-Hartman 
1462ab4382d2SGreg Kroah-Hartman 	/* clear all current buffers of data */
1463ab4382d2SGreg Kroah-Hartman 	for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) {
1464f73989f5SJiri Slaby 		icom_port->statStg->rcv[rcv_buff].flags = 0;
1465f73989f5SJiri Slaby 		icom_port->statStg->rcv[rcv_buff].leLength = 0;
1466f73989f5SJiri Slaby 		icom_port->statStg->rcv[rcv_buff].WorkingLength =
14677a5f86e8SJiri Slaby 		    cpu_to_le16(RCV_BUFF_SZ);
1468ab4382d2SGreg Kroah-Hartman 	}
1469ab4382d2SGreg Kroah-Hartman 
1470ab4382d2SGreg Kroah-Hartman 	for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) {
1471f73989f5SJiri Slaby 		icom_port->statStg->xmit[xmit_buff].flags = 0;
1472ab4382d2SGreg Kroah-Hartman 	}
1473ab4382d2SGreg Kroah-Hartman 
1474ab4382d2SGreg Kroah-Hartman 	/* activate changes and start xmit and receiver here */
1475ab4382d2SGreg Kroah-Hartman 	/* Enable the receiver */
1476f73989f5SJiri Slaby 	writeb(new_config3, &(icom_port->dram->async_config3));
1477f73989f5SJiri Slaby 	writeb(new_config2, &(icom_port->dram->async_config2));
1478f73989f5SJiri Slaby 	tmp_byte = readb(&(icom_port->dram->HDLCConfigReg));
1479ab4382d2SGreg Kroah-Hartman 	tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL;
1480f73989f5SJiri Slaby 	writeb(tmp_byte, &(icom_port->dram->HDLCConfigReg));
1481f73989f5SJiri Slaby 	writeb(0x04, &(icom_port->dram->FlagFillIdleTimer));	/* 0.5 seconds */
1482f73989f5SJiri Slaby 	writeb(0xFF, &(icom_port->dram->ier));	/* enable modem signal interrupts */
1483ab4382d2SGreg Kroah-Hartman 
1484ab4382d2SGreg Kroah-Hartman 	/* reset processor */
1485f73989f5SJiri Slaby 	writeb(CMD_RESTART, &icom_port->dram->CmdReg);
1486ab4382d2SGreg Kroah-Hartman 
1487ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < 10; index++) {
1488f73989f5SJiri Slaby 		if (readb(&icom_port->dram->CmdReg) == 0x00) {
1489ab4382d2SGreg Kroah-Hartman 			break;
1490ab4382d2SGreg Kroah-Hartman 		}
1491ab4382d2SGreg Kroah-Hartman 	}
1492ab4382d2SGreg Kroah-Hartman 
149325985edcSLucas De Marchi 	/* Enable Transmitter and Receiver */
1494ab4382d2SGreg Kroah-Hartman 	offset =
1495f73989f5SJiri Slaby 	    (unsigned long) &icom_port->statStg->rcv[0] -
1496f73989f5SJiri Slaby 	    (unsigned long) icom_port->statStg;
1497f73989f5SJiri Slaby 	writel(icom_port->statStg_pci + offset,
1498f73989f5SJiri Slaby 	       &icom_port->dram->RcvStatusAddr);
1499f73989f5SJiri Slaby 	icom_port->next_rcv = 0;
1500f73989f5SJiri Slaby 	*icom_port->xmitRestart = 0;
1501f73989f5SJiri Slaby 	writel(icom_port->xmitRestart_pci,
1502f73989f5SJiri Slaby 	       &icom_port->dram->XmitStatusAddr);
1503f73989f5SJiri Slaby 	trace(icom_port, "XR_ENAB", 0);
1504f73989f5SJiri Slaby 	writeb(CMD_XMIT_RCV_ENABLE, &icom_port->dram->CmdReg);
1505ab4382d2SGreg Kroah-Hartman 
1506b4c7ba24SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
1507ab4382d2SGreg Kroah-Hartman }
1508ab4382d2SGreg Kroah-Hartman 
1509ab4382d2SGreg Kroah-Hartman static const char *icom_type(struct uart_port *port)
1510ab4382d2SGreg Kroah-Hartman {
1511ab4382d2SGreg Kroah-Hartman 	return "icom";
1512ab4382d2SGreg Kroah-Hartman }
1513ab4382d2SGreg Kroah-Hartman 
1514ab4382d2SGreg Kroah-Hartman static void icom_config_port(struct uart_port *port, int flags)
1515ab4382d2SGreg Kroah-Hartman {
1516ab4382d2SGreg Kroah-Hartman 	port->type = PORT_ICOM;
1517ab4382d2SGreg Kroah-Hartman }
1518ab4382d2SGreg Kroah-Hartman 
15192331e068SBhumika Goyal static const struct uart_ops icom_ops = {
1520ab4382d2SGreg Kroah-Hartman 	.tx_empty = icom_tx_empty,
1521ab4382d2SGreg Kroah-Hartman 	.set_mctrl = icom_set_mctrl,
1522ab4382d2SGreg Kroah-Hartman 	.get_mctrl = icom_get_mctrl,
1523ab4382d2SGreg Kroah-Hartman 	.stop_tx = icom_stop_tx,
1524ab4382d2SGreg Kroah-Hartman 	.start_tx = icom_start_tx,
1525ab4382d2SGreg Kroah-Hartman 	.send_xchar = icom_send_xchar,
1526ab4382d2SGreg Kroah-Hartman 	.stop_rx = icom_stop_rx,
1527ab4382d2SGreg Kroah-Hartman 	.break_ctl = icom_break,
1528ab4382d2SGreg Kroah-Hartman 	.startup = icom_open,
1529ab4382d2SGreg Kroah-Hartman 	.shutdown = icom_close,
1530ab4382d2SGreg Kroah-Hartman 	.set_termios = icom_set_termios,
1531ab4382d2SGreg Kroah-Hartman 	.type = icom_type,
1532ab4382d2SGreg Kroah-Hartman 	.config_port = icom_config_port,
1533ab4382d2SGreg Kroah-Hartman };
1534ab4382d2SGreg Kroah-Hartman 
1535ab4382d2SGreg Kroah-Hartman #define ICOM_CONSOLE NULL
1536ab4382d2SGreg Kroah-Hartman 
1537ab4382d2SGreg Kroah-Hartman static struct uart_driver icom_uart_driver = {
1538ab4382d2SGreg Kroah-Hartman 	.owner = THIS_MODULE,
1539ab4382d2SGreg Kroah-Hartman 	.driver_name = ICOM_DRIVER_NAME,
1540ab4382d2SGreg Kroah-Hartman 	.dev_name = "ttyA",
1541ab4382d2SGreg Kroah-Hartman 	.major = ICOM_MAJOR,
1542ab4382d2SGreg Kroah-Hartman 	.minor = ICOM_MINOR_START,
1543ab4382d2SGreg Kroah-Hartman 	.nr = NR_PORTS,
1544ab4382d2SGreg Kroah-Hartman 	.cons = ICOM_CONSOLE,
1545ab4382d2SGreg Kroah-Hartman };
1546ab4382d2SGreg Kroah-Hartman 
15479671f099SBill Pemberton static int icom_init_ports(struct icom_adapter *icom_adapter)
1548ab4382d2SGreg Kroah-Hartman {
1549ab4382d2SGreg Kroah-Hartman 	u32 subsystem_id = icom_adapter->subsystem_id;
1550ab4382d2SGreg Kroah-Hartman 	int i;
1551ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1552ab4382d2SGreg Kroah-Hartman 
1553ab4382d2SGreg Kroah-Hartman 	if (icom_adapter->version == ADAPTER_V1) {
1554ab4382d2SGreg Kroah-Hartman 		icom_adapter->numb_ports = 2;
1555ab4382d2SGreg Kroah-Hartman 
1556ab4382d2SGreg Kroah-Hartman 		for (i = 0; i < 2; i++) {
1557ab4382d2SGreg Kroah-Hartman 			icom_port = &icom_adapter->port_info[i];
1558ab4382d2SGreg Kroah-Hartman 			icom_port->port = i;
1559ab4382d2SGreg Kroah-Hartman 			icom_port->status = ICOM_PORT_ACTIVE;
1560ab4382d2SGreg Kroah-Hartman 		}
1561ab4382d2SGreg Kroah-Hartman 	} else {
1562ab4382d2SGreg Kroah-Hartman 		if (subsystem_id == PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL) {
1563ab4382d2SGreg Kroah-Hartman 			icom_adapter->numb_ports = 4;
1564ab4382d2SGreg Kroah-Hartman 
1565ab4382d2SGreg Kroah-Hartman 			for (i = 0; i < 4; i++) {
1566ab4382d2SGreg Kroah-Hartman 				icom_port = &icom_adapter->port_info[i];
1567ab4382d2SGreg Kroah-Hartman 
1568ab4382d2SGreg Kroah-Hartman 				icom_port->port = i;
1569ab4382d2SGreg Kroah-Hartman 				icom_port->status = ICOM_PORT_ACTIVE;
1570ab4382d2SGreg Kroah-Hartman 			}
1571ab4382d2SGreg Kroah-Hartman 		} else {
1572ab4382d2SGreg Kroah-Hartman 			icom_adapter->numb_ports = 4;
1573ab4382d2SGreg Kroah-Hartman 
1574ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[0].port = 0;
1575ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[0].status = ICOM_PORT_ACTIVE;
1576ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[1].status = ICOM_PORT_OFF;
1577ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[2].port = 2;
1578ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[2].status = ICOM_PORT_ACTIVE;
1579ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[3].status = ICOM_PORT_OFF;
1580ab4382d2SGreg Kroah-Hartman 		}
1581ab4382d2SGreg Kroah-Hartman 	}
1582ab4382d2SGreg Kroah-Hartman 
1583ab4382d2SGreg Kroah-Hartman 	return 0;
1584ab4382d2SGreg Kroah-Hartman }
1585ab4382d2SGreg Kroah-Hartman 
1586ab4382d2SGreg Kroah-Hartman static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *icom_adapter, int port_num)
1587ab4382d2SGreg Kroah-Hartman {
1588ab4382d2SGreg Kroah-Hartman 	if (icom_adapter->version == ADAPTER_V1) {
1589ab4382d2SGreg Kroah-Hartman 		icom_port->global_reg = icom_adapter->base_addr + 0x4000;
1590ab4382d2SGreg Kroah-Hartman 		icom_port->int_reg = icom_adapter->base_addr +
1591ab4382d2SGreg Kroah-Hartman 		    0x4004 + 2 - 2 * port_num;
1592ab4382d2SGreg Kroah-Hartman 	} else {
1593ab4382d2SGreg Kroah-Hartman 		icom_port->global_reg = icom_adapter->base_addr + 0x8000;
1594ab4382d2SGreg Kroah-Hartman 		if (icom_port->port < 2)
1595ab4382d2SGreg Kroah-Hartman 			icom_port->int_reg = icom_adapter->base_addr +
1596ab4382d2SGreg Kroah-Hartman 			    0x8004 + 2 - 2 * icom_port->port;
1597ab4382d2SGreg Kroah-Hartman 		else
1598ab4382d2SGreg Kroah-Hartman 			icom_port->int_reg = icom_adapter->base_addr +
1599ab4382d2SGreg Kroah-Hartman 			    0x8024 + 2 - 2 * (icom_port->port - 2);
1600ab4382d2SGreg Kroah-Hartman 	}
1601ab4382d2SGreg Kroah-Hartman }
16029671f099SBill Pemberton static int icom_load_ports(struct icom_adapter *icom_adapter)
1603ab4382d2SGreg Kroah-Hartman {
1604ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1605ab4382d2SGreg Kroah-Hartman 	int port_num;
1606ab4382d2SGreg Kroah-Hartman 
1607ab4382d2SGreg Kroah-Hartman 	for (port_num = 0; port_num < icom_adapter->numb_ports; port_num++) {
1608ab4382d2SGreg Kroah-Hartman 
1609ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[port_num];
1610ab4382d2SGreg Kroah-Hartman 
1611ab4382d2SGreg Kroah-Hartman 		if (icom_port->status == ICOM_PORT_ACTIVE) {
1612ab4382d2SGreg Kroah-Hartman 			icom_port_active(icom_port, icom_adapter, port_num);
1613ab4382d2SGreg Kroah-Hartman 			icom_port->dram = icom_adapter->base_addr +
1614ab4382d2SGreg Kroah-Hartman 					0x2000 * icom_port->port;
1615ab4382d2SGreg Kroah-Hartman 
1616ab4382d2SGreg Kroah-Hartman 			icom_port->adapter = icom_adapter;
1617ab4382d2SGreg Kroah-Hartman 
1618ab4382d2SGreg Kroah-Hartman 			/* get port memory */
1619ab4382d2SGreg Kroah-Hartman 			if (get_port_memory(icom_port) != 0) {
1620ab4382d2SGreg Kroah-Hartman 				dev_err(&icom_port->adapter->pci_dev->dev,
1621ab4382d2SGreg Kroah-Hartman 					"Memory allocation for port FAILED\n");
1622ab4382d2SGreg Kroah-Hartman 			}
1623ab4382d2SGreg Kroah-Hartman 		}
1624ab4382d2SGreg Kroah-Hartman 	}
1625ab4382d2SGreg Kroah-Hartman 	return 0;
1626ab4382d2SGreg Kroah-Hartman }
1627ab4382d2SGreg Kroah-Hartman 
16289671f099SBill Pemberton static int icom_alloc_adapter(struct icom_adapter
1629ab4382d2SGreg Kroah-Hartman 					**icom_adapter_ref)
1630ab4382d2SGreg Kroah-Hartman {
1631ab4382d2SGreg Kroah-Hartman 	int adapter_count = 0;
1632ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *icom_adapter;
1633ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *cur_adapter_entry;
1634ab4382d2SGreg Kroah-Hartman 
1635b9a129f4SZhang Yanfei 	icom_adapter = kzalloc(sizeof(struct icom_adapter), GFP_KERNEL);
1636ab4382d2SGreg Kroah-Hartman 
1637ab4382d2SGreg Kroah-Hartman 	if (!icom_adapter) {
1638ab4382d2SGreg Kroah-Hartman 		return -ENOMEM;
1639ab4382d2SGreg Kroah-Hartman 	}
1640ab4382d2SGreg Kroah-Hartman 
1641e391e325SJiri Slaby 	list_for_each_entry(cur_adapter_entry, &icom_adapter_head,
1642e391e325SJiri Slaby 			icom_adapter_entry) {
1643ab4382d2SGreg Kroah-Hartman 		if (cur_adapter_entry->index != adapter_count) {
1644ab4382d2SGreg Kroah-Hartman 			break;
1645ab4382d2SGreg Kroah-Hartman 		}
1646ab4382d2SGreg Kroah-Hartman 		adapter_count++;
1647ab4382d2SGreg Kroah-Hartman 	}
1648ab4382d2SGreg Kroah-Hartman 
1649ab4382d2SGreg Kroah-Hartman 	icom_adapter->index = adapter_count;
1650e391e325SJiri Slaby 	list_add_tail(&icom_adapter->icom_adapter_entry,
1651e391e325SJiri Slaby 			&cur_adapter_entry->icom_adapter_entry);
1652ab4382d2SGreg Kroah-Hartman 
1653ab4382d2SGreg Kroah-Hartman 	*icom_adapter_ref = icom_adapter;
1654ab4382d2SGreg Kroah-Hartman 	return 0;
1655ab4382d2SGreg Kroah-Hartman }
1656ab4382d2SGreg Kroah-Hartman 
1657ab4382d2SGreg Kroah-Hartman static void icom_free_adapter(struct icom_adapter *icom_adapter)
1658ab4382d2SGreg Kroah-Hartman {
1659ab4382d2SGreg Kroah-Hartman 	list_del(&icom_adapter->icom_adapter_entry);
1660ab4382d2SGreg Kroah-Hartman 	kfree(icom_adapter);
1661ab4382d2SGreg Kroah-Hartman }
1662ab4382d2SGreg Kroah-Hartman 
16632c334f12SJiri Slaby static void icom_kref_release(struct kref *kref)
1664ab4382d2SGreg Kroah-Hartman {
16652c334f12SJiri Slaby 	struct icom_adapter *icom_adapter = container_of(kref,
16662c334f12SJiri Slaby 			struct icom_adapter, kref);
1667ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1668ab4382d2SGreg Kroah-Hartman 	int index;
1669ab4382d2SGreg Kroah-Hartman 
1670ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < icom_adapter->numb_ports; index++) {
1671ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[index];
1672ab4382d2SGreg Kroah-Hartman 
1673ab4382d2SGreg Kroah-Hartman 		if (icom_port->status == ICOM_PORT_ACTIVE) {
1674ab4382d2SGreg Kroah-Hartman 			dev_info(&icom_adapter->pci_dev->dev,
1675ab4382d2SGreg Kroah-Hartman 				 "Device removed\n");
1676ab4382d2SGreg Kroah-Hartman 
1677ab4382d2SGreg Kroah-Hartman 			uart_remove_one_port(&icom_uart_driver,
1678ab4382d2SGreg Kroah-Hartman 					     &icom_port->uart_port);
1679ab4382d2SGreg Kroah-Hartman 
1680ab4382d2SGreg Kroah-Hartman 			/* be sure that DTR and RTS are dropped */
1681ab4382d2SGreg Kroah-Hartman 			writeb(0x00, &icom_port->dram->osr);
1682ab4382d2SGreg Kroah-Hartman 
1683ab4382d2SGreg Kroah-Hartman 			/* Wait 0.1 Sec for simple Init to complete */
1684ab4382d2SGreg Kroah-Hartman 			msleep(100);
1685ab4382d2SGreg Kroah-Hartman 
1686ab4382d2SGreg Kroah-Hartman 			/* Stop proccessor */
1687ab4382d2SGreg Kroah-Hartman 			stop_processor(icom_port);
1688ab4382d2SGreg Kroah-Hartman 
1689ab4382d2SGreg Kroah-Hartman 			free_port_memory(icom_port);
1690ab4382d2SGreg Kroah-Hartman 		}
1691ab4382d2SGreg Kroah-Hartman 	}
1692ab4382d2SGreg Kroah-Hartman 
1693ab4382d2SGreg Kroah-Hartman 	free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter);
1694ab4382d2SGreg Kroah-Hartman 	iounmap(icom_adapter->base_addr);
1695ab4382d2SGreg Kroah-Hartman 	pci_release_regions(icom_adapter->pci_dev);
1696ab4382d2SGreg Kroah-Hartman 	icom_free_adapter(icom_adapter);
1697ab4382d2SGreg Kroah-Hartman }
1698ab4382d2SGreg Kroah-Hartman 
16999671f099SBill Pemberton static int icom_probe(struct pci_dev *dev,
1700ab4382d2SGreg Kroah-Hartman 				const struct pci_device_id *ent)
1701ab4382d2SGreg Kroah-Hartman {
1702ab4382d2SGreg Kroah-Hartman 	int index;
1703ab4382d2SGreg Kroah-Hartman 	unsigned int command_reg;
1704ab4382d2SGreg Kroah-Hartman 	int retval;
1705ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *icom_adapter;
1706ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1707ab4382d2SGreg Kroah-Hartman 
1708ab4382d2SGreg Kroah-Hartman 	retval = pci_enable_device(dev);
1709ab4382d2SGreg Kroah-Hartman 	if (retval) {
1710ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Device enable FAILED\n");
1711ab4382d2SGreg Kroah-Hartman 		return retval;
1712ab4382d2SGreg Kroah-Hartman 	}
1713ab4382d2SGreg Kroah-Hartman 
171401e51df5SGreg Kroah-Hartman 	retval = pci_request_regions(dev, "icom");
171501e51df5SGreg Kroah-Hartman 	if (retval) {
1716ab4382d2SGreg Kroah-Hartman 		 dev_err(&dev->dev, "pci_request_regions FAILED\n");
1717ab4382d2SGreg Kroah-Hartman 		 pci_disable_device(dev);
1718ab4382d2SGreg Kroah-Hartman 		 return retval;
1719ab4382d2SGreg Kroah-Hartman 	 }
1720ab4382d2SGreg Kroah-Hartman 
1721ab4382d2SGreg Kroah-Hartman 	pci_set_master(dev);
1722ab4382d2SGreg Kroah-Hartman 
172301e51df5SGreg Kroah-Hartman 	retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg);
172401e51df5SGreg Kroah-Hartman 	if (retval) {
1725ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "PCI Config read FAILED\n");
1726ee157a79SHuang Guobin 		goto probe_exit0;
1727ab4382d2SGreg Kroah-Hartman 	}
1728ab4382d2SGreg Kroah-Hartman 
1729ab4382d2SGreg Kroah-Hartman 	pci_write_config_dword(dev, PCI_COMMAND,
1730ab4382d2SGreg Kroah-Hartman 		command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
1731ab4382d2SGreg Kroah-Hartman  		| PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
1732ab4382d2SGreg Kroah-Hartman 
1733ab4382d2SGreg Kroah-Hartman 	if (ent->driver_data == ADAPTER_V1) {
1734ab4382d2SGreg Kroah-Hartman 		pci_write_config_dword(dev, 0x44, 0x8300830A);
1735ab4382d2SGreg Kroah-Hartman 	} else {
1736ab4382d2SGreg Kroah-Hartman 		pci_write_config_dword(dev, 0x44, 0x42004200);
1737ab4382d2SGreg Kroah-Hartman 		pci_write_config_dword(dev, 0x48, 0x42004200);
1738ab4382d2SGreg Kroah-Hartman 	}
1739ab4382d2SGreg Kroah-Hartman 
1740ab4382d2SGreg Kroah-Hartman 
1741ab4382d2SGreg Kroah-Hartman 	retval = icom_alloc_adapter(&icom_adapter);
1742ab4382d2SGreg Kroah-Hartman 	if (retval) {
1743ab4382d2SGreg Kroah-Hartman 		 dev_err(&dev->dev, "icom_alloc_adapter FAILED\n");
1744ab4382d2SGreg Kroah-Hartman 		 retval = -EIO;
1745ab4382d2SGreg Kroah-Hartman 		 goto probe_exit0;
1746ab4382d2SGreg Kroah-Hartman 	}
1747ab4382d2SGreg Kroah-Hartman 
1748ab4382d2SGreg Kroah-Hartman 	icom_adapter->base_addr_pci = pci_resource_start(dev, 0);
1749ab4382d2SGreg Kroah-Hartman 	icom_adapter->pci_dev = dev;
1750ab4382d2SGreg Kroah-Hartman 	icom_adapter->version = ent->driver_data;
1751ab4382d2SGreg Kroah-Hartman 	icom_adapter->subsystem_id = ent->subdevice;
1752ab4382d2SGreg Kroah-Hartman 
1753ab4382d2SGreg Kroah-Hartman 
1754ab4382d2SGreg Kroah-Hartman 	retval = icom_init_ports(icom_adapter);
1755ab4382d2SGreg Kroah-Hartman 	if (retval) {
1756ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Port configuration failed\n");
1757ab4382d2SGreg Kroah-Hartman 		goto probe_exit1;
1758ab4382d2SGreg Kroah-Hartman 	}
1759ab4382d2SGreg Kroah-Hartman 
1760ab4382d2SGreg Kroah-Hartman 	icom_adapter->base_addr = pci_ioremap_bar(dev, 0);
1761ab4382d2SGreg Kroah-Hartman 
1762ddcbad92SJulia Lawall 	if (!icom_adapter->base_addr) {
1763ddcbad92SJulia Lawall 		retval = -ENOMEM;
1764ab4382d2SGreg Kroah-Hartman 		goto probe_exit1;
1765ddcbad92SJulia Lawall 	}
1766ab4382d2SGreg Kroah-Hartman 
1767ab4382d2SGreg Kroah-Hartman 	 /* save off irq and request irq line */
176801e51df5SGreg Kroah-Hartman 	 retval = request_irq(dev->irq, icom_interrupt, IRQF_SHARED, ICOM_DRIVER_NAME, (void *)icom_adapter);
176901e51df5SGreg Kroah-Hartman 	 if (retval) {
1770ab4382d2SGreg Kroah-Hartman 		  goto probe_exit2;
1771ab4382d2SGreg Kroah-Hartman 	 }
1772ab4382d2SGreg Kroah-Hartman 
1773ab4382d2SGreg Kroah-Hartman 	retval = icom_load_ports(icom_adapter);
1774ab4382d2SGreg Kroah-Hartman 
1775ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < icom_adapter->numb_ports; index++) {
1776ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[index];
1777ab4382d2SGreg Kroah-Hartman 
1778ab4382d2SGreg Kroah-Hartman 		if (icom_port->status == ICOM_PORT_ACTIVE) {
1779ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.irq = icom_port->adapter->pci_dev->irq;
1780ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.type = PORT_ICOM;
1781ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.iotype = UPIO_MEM;
1782ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.membase =
17835a7daed8SJingoo Han 				(unsigned char __iomem *)icom_adapter->base_addr_pci;
1784ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.fifosize = 16;
1785ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.ops = &icom_ops;
1786ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.line =
1787ab4382d2SGreg Kroah-Hartman 		        icom_port->port + icom_adapter->index * 4;
1788ab4382d2SGreg Kroah-Hartman 			if (uart_add_one_port (&icom_uart_driver, &icom_port->uart_port)) {
1789ab4382d2SGreg Kroah-Hartman 				icom_port->status = ICOM_PORT_OFF;
1790ab4382d2SGreg Kroah-Hartman 				dev_err(&dev->dev, "Device add failed\n");
1791ab4382d2SGreg Kroah-Hartman 			 } else
1792ab4382d2SGreg Kroah-Hartman 				dev_info(&dev->dev, "Device added\n");
1793ab4382d2SGreg Kroah-Hartman 		}
1794ab4382d2SGreg Kroah-Hartman 	}
1795ab4382d2SGreg Kroah-Hartman 
1796ab4382d2SGreg Kroah-Hartman 	kref_init(&icom_adapter->kref);
1797ab4382d2SGreg Kroah-Hartman 	return 0;
1798ab4382d2SGreg Kroah-Hartman 
1799ab4382d2SGreg Kroah-Hartman probe_exit2:
1800ab4382d2SGreg Kroah-Hartman 	iounmap(icom_adapter->base_addr);
1801ab4382d2SGreg Kroah-Hartman probe_exit1:
1802ab4382d2SGreg Kroah-Hartman 	icom_free_adapter(icom_adapter);
1803ab4382d2SGreg Kroah-Hartman 
1804ab4382d2SGreg Kroah-Hartman probe_exit0:
1805ab4382d2SGreg Kroah-Hartman 	pci_release_regions(dev);
1806ab4382d2SGreg Kroah-Hartman 	pci_disable_device(dev);
1807ab4382d2SGreg Kroah-Hartman 
1808ab4382d2SGreg Kroah-Hartman 	return retval;
1809ab4382d2SGreg Kroah-Hartman }
1810ab4382d2SGreg Kroah-Hartman 
1811ae8d8a14SBill Pemberton static void icom_remove(struct pci_dev *dev)
1812ab4382d2SGreg Kroah-Hartman {
1813ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *icom_adapter;
1814ab4382d2SGreg Kroah-Hartman 
1815e391e325SJiri Slaby 	list_for_each_entry(icom_adapter, &icom_adapter_head,
1816e391e325SJiri Slaby 			icom_adapter_entry) {
1817ab4382d2SGreg Kroah-Hartman 		if (icom_adapter->pci_dev == dev) {
1818ab4382d2SGreg Kroah-Hartman 			kref_put(&icom_adapter->kref, icom_kref_release);
1819ab4382d2SGreg Kroah-Hartman 			return;
1820ab4382d2SGreg Kroah-Hartman 		}
1821ab4382d2SGreg Kroah-Hartman 	}
1822ab4382d2SGreg Kroah-Hartman 
1823ab4382d2SGreg Kroah-Hartman 	dev_err(&dev->dev, "Unable to find device to remove\n");
1824ab4382d2SGreg Kroah-Hartman }
1825ab4382d2SGreg Kroah-Hartman 
1826ab4382d2SGreg Kroah-Hartman static struct pci_driver icom_pci_driver = {
1827ab4382d2SGreg Kroah-Hartman 	.name = ICOM_DRIVER_NAME,
1828ab4382d2SGreg Kroah-Hartman 	.id_table = icom_pci_table,
1829ab4382d2SGreg Kroah-Hartman 	.probe = icom_probe,
18302d47b716SBill Pemberton 	.remove = icom_remove,
1831ab4382d2SGreg Kroah-Hartman };
1832ab4382d2SGreg Kroah-Hartman 
1833ab4382d2SGreg Kroah-Hartman static int __init icom_init(void)
1834ab4382d2SGreg Kroah-Hartman {
1835ab4382d2SGreg Kroah-Hartman 	int ret;
1836ab4382d2SGreg Kroah-Hartman 
1837ab4382d2SGreg Kroah-Hartman 	ret = uart_register_driver(&icom_uart_driver);
1838ab4382d2SGreg Kroah-Hartman 	if (ret)
1839ab4382d2SGreg Kroah-Hartman 		return ret;
1840ab4382d2SGreg Kroah-Hartman 
1841ab4382d2SGreg Kroah-Hartman 	ret = pci_register_driver(&icom_pci_driver);
1842ab4382d2SGreg Kroah-Hartman 
1843ab4382d2SGreg Kroah-Hartman 	if (ret < 0)
1844ab4382d2SGreg Kroah-Hartman 		uart_unregister_driver(&icom_uart_driver);
1845ab4382d2SGreg Kroah-Hartman 
1846ab4382d2SGreg Kroah-Hartman 	return ret;
1847ab4382d2SGreg Kroah-Hartman }
1848ab4382d2SGreg Kroah-Hartman 
1849ab4382d2SGreg Kroah-Hartman static void __exit icom_exit(void)
1850ab4382d2SGreg Kroah-Hartman {
1851ab4382d2SGreg Kroah-Hartman 	pci_unregister_driver(&icom_pci_driver);
1852ab4382d2SGreg Kroah-Hartman 	uart_unregister_driver(&icom_uart_driver);
1853ab4382d2SGreg Kroah-Hartman }
1854ab4382d2SGreg Kroah-Hartman 
1855ab4382d2SGreg Kroah-Hartman module_init(icom_init);
1856ab4382d2SGreg Kroah-Hartman module_exit(icom_exit);
1857ab4382d2SGreg Kroah-Hartman 
1858ab4382d2SGreg Kroah-Hartman MODULE_AUTHOR("Michael Anderson <mjanders@us.ibm.com>");
1859ab4382d2SGreg Kroah-Hartman MODULE_DESCRIPTION("IBM iSeries Serial IOA driver");
1860ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL");
1861ab4382d2SGreg Kroah-Hartman MODULE_FIRMWARE("icom_call_setup.bin");
1862ab4382d2SGreg Kroah-Hartman MODULE_FIRMWARE("icom_res_dce.bin");
1863ab4382d2SGreg Kroah-Hartman MODULE_FIRMWARE("icom_asc.bin");
1864