xref: /openbmc/linux/drivers/tty/serial/icom.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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 
to_icom_port(struct uart_port * port)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
trace(struct icom_port * icom_port,char * trace_pt,unsigned long trace_data)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
trace(struct icom_port * icom_port,char * trace_pt,unsigned long trace_data)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 
free_port_memory(struct icom_port * icom_port)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 
get_port_memory(struct icom_port * icom_port)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 
stop_processor(struct icom_port * icom_port)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 
start_processor(struct icom_port * icom_port)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 
load_code(struct icom_port * icom_port)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 
startup(struct icom_port * icom_port)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 
shutdown(struct icom_port * icom_port)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 
icom_write(struct uart_port * port)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);
880ab4382d2SGreg Kroah-Hartman 	unsigned long data_count;
881ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
882ab4382d2SGreg Kroah-Hartman 	unsigned long offset;
883ab4382d2SGreg Kroah-Hartman 	int temp_tail = port->state->xmit.tail;
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 
893ab4382d2SGreg Kroah-Hartman 	data_count = 0;
894ab4382d2SGreg Kroah-Hartman 	while ((port->state->xmit.head != temp_tail) &&
895ab4382d2SGreg Kroah-Hartman 	       (data_count <= XMIT_BUFF_SZ)) {
896ab4382d2SGreg Kroah-Hartman 
897f73989f5SJiri Slaby 		icom_port->xmit_buf[data_count++] =
898ab4382d2SGreg Kroah-Hartman 		    port->state->xmit.buf[temp_tail];
899ab4382d2SGreg Kroah-Hartman 
900ab4382d2SGreg Kroah-Hartman 		temp_tail++;
901ab4382d2SGreg Kroah-Hartman 		temp_tail &= (UART_XMIT_SIZE - 1);
902ab4382d2SGreg Kroah-Hartman 	}
903ab4382d2SGreg Kroah-Hartman 
904ab4382d2SGreg Kroah-Hartman 	if (data_count) {
905f73989f5SJiri Slaby 		icom_port->statStg->xmit[0].flags =
906ab4382d2SGreg Kroah-Hartman 		    cpu_to_le16(SA_FLAGS_READY_TO_XMIT);
907f73989f5SJiri Slaby 		icom_port->statStg->xmit[0].leLength =
908ab4382d2SGreg Kroah-Hartman 		    cpu_to_le16(data_count);
909ab4382d2SGreg Kroah-Hartman 		offset =
910f73989f5SJiri Slaby 		    (unsigned long) &icom_port->statStg->xmit[0] -
911f73989f5SJiri Slaby 		    (unsigned long) icom_port->statStg;
912f73989f5SJiri Slaby 		*icom_port->xmitRestart =
913f73989f5SJiri Slaby 		    cpu_to_le32(icom_port->statStg_pci + offset);
914f73989f5SJiri Slaby 		cmdReg = readb(&icom_port->dram->CmdReg);
915ab4382d2SGreg Kroah-Hartman 		writeb(cmdReg | CMD_XMIT_RCV_ENABLE,
916f73989f5SJiri Slaby 		       &icom_port->dram->CmdReg);
917f73989f5SJiri Slaby 		writeb(START_XMIT, &icom_port->dram->StartXmitCmd);
918f73989f5SJiri Slaby 		trace(icom_port, "WRITE_START", data_count);
919ab4382d2SGreg Kroah-Hartman 		/* write flush */
920f73989f5SJiri Slaby 		readb(&icom_port->dram->StartXmitCmd);
921ab4382d2SGreg Kroah-Hartman 	}
922ab4382d2SGreg Kroah-Hartman 
923ab4382d2SGreg Kroah-Hartman 	return data_count;
924ab4382d2SGreg Kroah-Hartman }
925ab4382d2SGreg Kroah-Hartman 
check_modem_status(struct icom_port * icom_port)926ab4382d2SGreg Kroah-Hartman static inline void check_modem_status(struct icom_port *icom_port)
927ab4382d2SGreg Kroah-Hartman {
928ab4382d2SGreg Kroah-Hartman 	static char old_status = 0;
929ab4382d2SGreg Kroah-Hartman 	char delta_status;
930ab4382d2SGreg Kroah-Hartman 	unsigned char status;
931ab4382d2SGreg Kroah-Hartman 
932ab4382d2SGreg Kroah-Hartman 	spin_lock(&icom_port->uart_port.lock);
933ab4382d2SGreg Kroah-Hartman 
934ab4382d2SGreg Kroah-Hartman 	/*modem input register */
935ab4382d2SGreg Kroah-Hartman 	status = readb(&icom_port->dram->isr);
936ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "CHECK_MODEM", status);
937ab4382d2SGreg Kroah-Hartman 	delta_status = status ^ old_status;
938ab4382d2SGreg Kroah-Hartman 	if (delta_status) {
939ab4382d2SGreg Kroah-Hartman 		if (delta_status & ICOM_RI)
940ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.icount.rng++;
941ab4382d2SGreg Kroah-Hartman 		if (delta_status & ICOM_DSR)
942ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.icount.dsr++;
943ab4382d2SGreg Kroah-Hartman 		if (delta_status & ICOM_DCD)
944ab4382d2SGreg Kroah-Hartman 			uart_handle_dcd_change(&icom_port->uart_port,
945ab4382d2SGreg Kroah-Hartman 					       delta_status & ICOM_DCD);
946ab4382d2SGreg Kroah-Hartman 		if (delta_status & ICOM_CTS)
947ab4382d2SGreg Kroah-Hartman 			uart_handle_cts_change(&icom_port->uart_port,
948ab4382d2SGreg Kroah-Hartman 					       delta_status & ICOM_CTS);
949ab4382d2SGreg Kroah-Hartman 
950ab4382d2SGreg Kroah-Hartman 		wake_up_interruptible(&icom_port->uart_port.state->
951ab4382d2SGreg Kroah-Hartman 				      port.delta_msr_wait);
952ab4382d2SGreg Kroah-Hartman 		old_status = status;
953ab4382d2SGreg Kroah-Hartman 	}
954ab4382d2SGreg Kroah-Hartman 	spin_unlock(&icom_port->uart_port.lock);
955ab4382d2SGreg Kroah-Hartman }
956ab4382d2SGreg Kroah-Hartman 
xmit_interrupt(u16 port_int_reg,struct icom_port * icom_port)957ab4382d2SGreg Kroah-Hartman static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
958ab4382d2SGreg Kroah-Hartman {
9597a5f86e8SJiri Slaby 	u16 count, i;
960ab4382d2SGreg Kroah-Hartman 
961ab4382d2SGreg Kroah-Hartman 	if (port_int_reg & (INT_XMIT_COMPLETED)) {
962ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "XMIT_COMPLETE", 0);
963ab4382d2SGreg Kroah-Hartman 
964ab4382d2SGreg Kroah-Hartman 		/* clear buffer in use bit */
965ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->xmit[0].flags &=
966ab4382d2SGreg Kroah-Hartman 			cpu_to_le16(~SA_FLAGS_READY_TO_XMIT);
967ab4382d2SGreg Kroah-Hartman 
9687a5f86e8SJiri Slaby 		count = le16_to_cpu(icom_port->statStg->xmit[0].leLength);
969ab4382d2SGreg Kroah-Hartman 		icom_port->uart_port.icount.tx += count;
970ab4382d2SGreg Kroah-Hartman 
971ab4382d2SGreg Kroah-Hartman 		for (i=0; i<count &&
972ab4382d2SGreg Kroah-Hartman 			!uart_circ_empty(&icom_port->uart_port.state->xmit); i++) {
973ab4382d2SGreg Kroah-Hartman 
974ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.state->xmit.tail++;
975ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.state->xmit.tail &=
976ab4382d2SGreg Kroah-Hartman 				(UART_XMIT_SIZE - 1);
977ab4382d2SGreg Kroah-Hartman 		}
978ab4382d2SGreg Kroah-Hartman 
979ab4382d2SGreg Kroah-Hartman 		if (!icom_write(&icom_port->uart_port))
980ab4382d2SGreg Kroah-Hartman 			/* activate write queue */
981ab4382d2SGreg Kroah-Hartman 			uart_write_wakeup(&icom_port->uart_port);
982ab4382d2SGreg Kroah-Hartman 	} else
983ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "XMIT_DISABLED", 0);
984ab4382d2SGreg Kroah-Hartman }
985ab4382d2SGreg Kroah-Hartman 
recv_interrupt(u16 port_int_reg,struct icom_port * icom_port)986ab4382d2SGreg Kroah-Hartman static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port)
987ab4382d2SGreg Kroah-Hartman {
988ab4382d2SGreg Kroah-Hartman 	short int count, rcv_buff;
98992a19f9cSJiri Slaby 	struct tty_port *port = &icom_port->uart_port.state->port;
9907a5f86e8SJiri Slaby 	u16 status;
991ab4382d2SGreg Kroah-Hartman 	struct uart_icount *icount;
992ab4382d2SGreg Kroah-Hartman 	unsigned long offset;
993ab4382d2SGreg Kroah-Hartman 	unsigned char flag;
994ab4382d2SGreg Kroah-Hartman 
995ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "RCV_COMPLETE", 0);
996ab4382d2SGreg Kroah-Hartman 	rcv_buff = icom_port->next_rcv;
997ab4382d2SGreg Kroah-Hartman 
9987a5f86e8SJiri Slaby 	status = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].flags);
999ab4382d2SGreg Kroah-Hartman 	while (status & SA_FL_RCV_DONE) {
1000ab4382d2SGreg Kroah-Hartman 		int first = -1;
1001ab4382d2SGreg Kroah-Hartman 
1002ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "FID_STATUS", status);
10037a5f86e8SJiri Slaby 		count = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].leLength);
1004ab4382d2SGreg Kroah-Hartman 
1005ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "RCV_COUNT", count);
1006ab4382d2SGreg Kroah-Hartman 
1007ab4382d2SGreg Kroah-Hartman 		trace(icom_port, "REAL_COUNT", count);
1008ab4382d2SGreg Kroah-Hartman 
10097a5f86e8SJiri Slaby 		offset = le32_to_cpu(icom_port->statStg->rcv[rcv_buff].leBuffer) -
1010ab4382d2SGreg Kroah-Hartman 			icom_port->recv_buf_pci;
1011ab4382d2SGreg Kroah-Hartman 
1012ab4382d2SGreg Kroah-Hartman 		/* Block copy all but the last byte as this may have status */
1013ab4382d2SGreg Kroah-Hartman 		if (count > 0) {
1014ab4382d2SGreg Kroah-Hartman 			first = icom_port->recv_buf[offset];
101505c7cd39SJiri Slaby 			tty_insert_flip_string(port, icom_port->recv_buf + offset, count - 1);
1016ab4382d2SGreg Kroah-Hartman 		}
1017ab4382d2SGreg Kroah-Hartman 
1018ab4382d2SGreg Kroah-Hartman 		icount = &icom_port->uart_port.icount;
1019ab4382d2SGreg Kroah-Hartman 		icount->rx += count;
1020ab4382d2SGreg Kroah-Hartman 
1021ab4382d2SGreg Kroah-Hartman 		/* Break detect logic */
1022ab4382d2SGreg Kroah-Hartman 		if ((status & SA_FLAGS_FRAME_ERROR)
1023ab4382d2SGreg Kroah-Hartman 		    && first == 0) {
1024ab4382d2SGreg Kroah-Hartman 			status &= ~SA_FLAGS_FRAME_ERROR;
1025ab4382d2SGreg Kroah-Hartman 			status |= SA_FLAGS_BREAK_DET;
1026ab4382d2SGreg Kroah-Hartman 			trace(icom_port, "BREAK_DET", 0);
1027ab4382d2SGreg Kroah-Hartman 		}
1028ab4382d2SGreg Kroah-Hartman 
1029ab4382d2SGreg Kroah-Hartman 		flag = TTY_NORMAL;
1030ab4382d2SGreg Kroah-Hartman 
1031ab4382d2SGreg Kroah-Hartman 		if (status &
1032ab4382d2SGreg Kroah-Hartman 		    (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR |
1033ab4382d2SGreg Kroah-Hartman 		     SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) {
1034ab4382d2SGreg Kroah-Hartman 
1035ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_BREAK_DET)
1036ab4382d2SGreg Kroah-Hartman 				icount->brk++;
1037ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_PARITY_ERROR)
1038ab4382d2SGreg Kroah-Hartman 				icount->parity++;
1039ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_FRAME_ERROR)
1040ab4382d2SGreg Kroah-Hartman 				icount->frame++;
1041ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_OVERRUN)
1042ab4382d2SGreg Kroah-Hartman 				icount->overrun++;
1043ab4382d2SGreg Kroah-Hartman 
1044ab4382d2SGreg Kroah-Hartman 			/*
1045ab4382d2SGreg Kroah-Hartman 			 * Now check to see if character should be
1046ab4382d2SGreg Kroah-Hartman 			 * ignored, and mask off conditions which
1047ab4382d2SGreg Kroah-Hartman 			 * should be ignored.
1048ab4382d2SGreg Kroah-Hartman 			 */
1049ab4382d2SGreg Kroah-Hartman 			if (status & icom_port->ignore_status_mask) {
1050ab4382d2SGreg Kroah-Hartman 				trace(icom_port, "IGNORE_CHAR", 0);
1051ab4382d2SGreg Kroah-Hartman 				goto ignore_char;
1052ab4382d2SGreg Kroah-Hartman 			}
1053ab4382d2SGreg Kroah-Hartman 
1054ab4382d2SGreg Kroah-Hartman 			status &= icom_port->read_status_mask;
1055ab4382d2SGreg Kroah-Hartman 
1056ab4382d2SGreg Kroah-Hartman 			if (status & SA_FLAGS_BREAK_DET) {
1057ab4382d2SGreg Kroah-Hartman 				flag = TTY_BREAK;
1058ab4382d2SGreg Kroah-Hartman 			} else if (status & SA_FLAGS_PARITY_ERROR) {
1059ab4382d2SGreg Kroah-Hartman 				trace(icom_port, "PARITY_ERROR", 0);
1060ab4382d2SGreg Kroah-Hartman 				flag = TTY_PARITY;
1061ab4382d2SGreg Kroah-Hartman 			} else if (status & SA_FLAGS_FRAME_ERROR)
1062ab4382d2SGreg Kroah-Hartman 				flag = TTY_FRAME;
1063ab4382d2SGreg Kroah-Hartman 
1064ab4382d2SGreg Kroah-Hartman 		}
1065ab4382d2SGreg Kroah-Hartman 
106692a19f9cSJiri Slaby 		tty_insert_flip_char(port, *(icom_port->recv_buf + offset + count - 1), flag);
1067ab4382d2SGreg Kroah-Hartman 
1068ab4382d2SGreg Kroah-Hartman 		if (status & SA_FLAGS_OVERRUN)
1069ab4382d2SGreg Kroah-Hartman 			/*
1070ab4382d2SGreg Kroah-Hartman 			 * Overrun is special, since it's
1071ab4382d2SGreg Kroah-Hartman 			 * reported immediately, and doesn't
1072ab4382d2SGreg Kroah-Hartman 			 * affect the current character
1073ab4382d2SGreg Kroah-Hartman 			 */
107492a19f9cSJiri Slaby 			tty_insert_flip_char(port, 0, TTY_OVERRUN);
1075ab4382d2SGreg Kroah-Hartman ignore_char:
1076ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->rcv[rcv_buff].flags = 0;
1077ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->rcv[rcv_buff].leLength = 0;
1078ab4382d2SGreg Kroah-Hartman 		icom_port->statStg->rcv[rcv_buff].WorkingLength =
10797a5f86e8SJiri Slaby 			cpu_to_le16(RCV_BUFF_SZ);
1080ab4382d2SGreg Kroah-Hartman 
1081ab4382d2SGreg Kroah-Hartman 		rcv_buff++;
1082ab4382d2SGreg Kroah-Hartman 		if (rcv_buff == NUM_RBUFFS)
1083ab4382d2SGreg Kroah-Hartman 			rcv_buff = 0;
1084ab4382d2SGreg Kroah-Hartman 
10857a5f86e8SJiri Slaby 		status = le16_to_cpu(icom_port->statStg->rcv[rcv_buff].flags);
1086ab4382d2SGreg Kroah-Hartman 	}
1087ab4382d2SGreg Kroah-Hartman 	icom_port->next_rcv = rcv_buff;
10885faf75d7SViresh Kumar 
10892e124b4aSJiri Slaby 	tty_flip_buffer_push(port);
1090ab4382d2SGreg Kroah-Hartman }
1091ab4382d2SGreg Kroah-Hartman 
process_interrupt(u16 port_int_reg,struct icom_port * icom_port)1092ab4382d2SGreg Kroah-Hartman static void process_interrupt(u16 port_int_reg,
1093ab4382d2SGreg Kroah-Hartman 			      struct icom_port *icom_port)
1094ab4382d2SGreg Kroah-Hartman {
1095ab4382d2SGreg Kroah-Hartman 
1096ab4382d2SGreg Kroah-Hartman 	spin_lock(&icom_port->uart_port.lock);
1097ab4382d2SGreg Kroah-Hartman 	trace(icom_port, "INTERRUPT", port_int_reg);
1098ab4382d2SGreg Kroah-Hartman 
1099ab4382d2SGreg Kroah-Hartman 	if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED))
1100ab4382d2SGreg Kroah-Hartman 		xmit_interrupt(port_int_reg, icom_port);
1101ab4382d2SGreg Kroah-Hartman 
1102ab4382d2SGreg Kroah-Hartman 	if (port_int_reg & INT_RCV_COMPLETED)
1103ab4382d2SGreg Kroah-Hartman 		recv_interrupt(port_int_reg, icom_port);
1104ab4382d2SGreg Kroah-Hartman 
1105ab4382d2SGreg Kroah-Hartman 	spin_unlock(&icom_port->uart_port.lock);
1106ab4382d2SGreg Kroah-Hartman }
1107ab4382d2SGreg Kroah-Hartman 
icom_interrupt(int irq,void * dev_id)1108ab4382d2SGreg Kroah-Hartman static irqreturn_t icom_interrupt(int irq, void *dev_id)
1109ab4382d2SGreg Kroah-Hartman {
1110ab4382d2SGreg Kroah-Hartman 	void __iomem * int_reg;
1111ab4382d2SGreg Kroah-Hartman 	u32 adapter_interrupts;
1112ab4382d2SGreg Kroah-Hartman 	u16 port_int_reg;
1113ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *icom_adapter;
1114ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1115ab4382d2SGreg Kroah-Hartman 
1116ab4382d2SGreg Kroah-Hartman 	/* find icom_port for this interrupt */
1117ab4382d2SGreg Kroah-Hartman 	icom_adapter = (struct icom_adapter *) dev_id;
1118ab4382d2SGreg Kroah-Hartman 
1119ab4382d2SGreg Kroah-Hartman 	if (icom_adapter->version == ADAPTER_V2) {
1120ab4382d2SGreg Kroah-Hartman 		int_reg = icom_adapter->base_addr + 0x8024;
1121ab4382d2SGreg Kroah-Hartman 
1122ab4382d2SGreg Kroah-Hartman 		adapter_interrupts = readl(int_reg);
1123ab4382d2SGreg Kroah-Hartman 
1124ab4382d2SGreg Kroah-Hartman 		if (adapter_interrupts & 0x00003FFF) {
1125ab4382d2SGreg Kroah-Hartman 			/* port 2 interrupt,  NOTE:  for all ADAPTER_V2, port 2 will be active */
1126ab4382d2SGreg Kroah-Hartman 			icom_port = &icom_adapter->port_info[2];
1127ab4382d2SGreg Kroah-Hartman 			port_int_reg = (u16) adapter_interrupts;
1128ab4382d2SGreg Kroah-Hartman 			process_interrupt(port_int_reg, icom_port);
1129ab4382d2SGreg Kroah-Hartman 			check_modem_status(icom_port);
1130ab4382d2SGreg Kroah-Hartman 		}
1131ab4382d2SGreg Kroah-Hartman 		if (adapter_interrupts & 0x3FFF0000) {
1132ab4382d2SGreg Kroah-Hartman 			/* port 3 interrupt */
1133ab4382d2SGreg Kroah-Hartman 			icom_port = &icom_adapter->port_info[3];
1134ab4382d2SGreg Kroah-Hartman 			if (icom_port->status == ICOM_PORT_ACTIVE) {
1135ab4382d2SGreg Kroah-Hartman 				port_int_reg =
1136ab4382d2SGreg Kroah-Hartman 				    (u16) (adapter_interrupts >> 16);
1137ab4382d2SGreg Kroah-Hartman 				process_interrupt(port_int_reg, icom_port);
1138ab4382d2SGreg Kroah-Hartman 				check_modem_status(icom_port);
1139ab4382d2SGreg Kroah-Hartman 			}
1140ab4382d2SGreg Kroah-Hartman 		}
1141ab4382d2SGreg Kroah-Hartman 
1142ab4382d2SGreg Kroah-Hartman 		/* Clear out any pending interrupts */
1143ab4382d2SGreg Kroah-Hartman 		writel(adapter_interrupts, int_reg);
1144ab4382d2SGreg Kroah-Hartman 
1145ab4382d2SGreg Kroah-Hartman 		int_reg = icom_adapter->base_addr + 0x8004;
1146ab4382d2SGreg Kroah-Hartman 	} else {
1147ab4382d2SGreg Kroah-Hartman 		int_reg = icom_adapter->base_addr + 0x4004;
1148ab4382d2SGreg Kroah-Hartman 	}
1149ab4382d2SGreg Kroah-Hartman 
1150ab4382d2SGreg Kroah-Hartman 	adapter_interrupts = readl(int_reg);
1151ab4382d2SGreg Kroah-Hartman 
1152ab4382d2SGreg Kroah-Hartman 	if (adapter_interrupts & 0x00003FFF) {
1153ab4382d2SGreg Kroah-Hartman 		/* port 0 interrupt, NOTE:  for all adapters, port 0 will be active */
1154ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[0];
1155ab4382d2SGreg Kroah-Hartman 		port_int_reg = (u16) adapter_interrupts;
1156ab4382d2SGreg Kroah-Hartman 		process_interrupt(port_int_reg, icom_port);
1157ab4382d2SGreg Kroah-Hartman 		check_modem_status(icom_port);
1158ab4382d2SGreg Kroah-Hartman 	}
1159ab4382d2SGreg Kroah-Hartman 	if (adapter_interrupts & 0x3FFF0000) {
1160ab4382d2SGreg Kroah-Hartman 		/* port 1 interrupt */
1161ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[1];
1162ab4382d2SGreg Kroah-Hartman 		if (icom_port->status == ICOM_PORT_ACTIVE) {
1163ab4382d2SGreg Kroah-Hartman 			port_int_reg = (u16) (adapter_interrupts >> 16);
1164ab4382d2SGreg Kroah-Hartman 			process_interrupt(port_int_reg, icom_port);
1165ab4382d2SGreg Kroah-Hartman 			check_modem_status(icom_port);
1166ab4382d2SGreg Kroah-Hartman 		}
1167ab4382d2SGreg Kroah-Hartman 	}
1168ab4382d2SGreg Kroah-Hartman 
1169ab4382d2SGreg Kroah-Hartman 	/* Clear out any pending interrupts */
1170ab4382d2SGreg Kroah-Hartman 	writel(adapter_interrupts, int_reg);
1171ab4382d2SGreg Kroah-Hartman 
1172ab4382d2SGreg Kroah-Hartman 	/* flush the write */
1173ab4382d2SGreg Kroah-Hartman 	adapter_interrupts = readl(int_reg);
1174ab4382d2SGreg Kroah-Hartman 
1175ab4382d2SGreg Kroah-Hartman 	return IRQ_HANDLED;
1176ab4382d2SGreg Kroah-Hartman }
1177ab4382d2SGreg Kroah-Hartman 
1178ab4382d2SGreg Kroah-Hartman /*
1179ab4382d2SGreg Kroah-Hartman  * ------------------------------------------------------------------
1180ab4382d2SGreg Kroah-Hartman  * Begin serial-core API
1181ab4382d2SGreg Kroah-Hartman  * ------------------------------------------------------------------
1182ab4382d2SGreg Kroah-Hartman  */
icom_tx_empty(struct uart_port * port)1183ab4382d2SGreg Kroah-Hartman static unsigned int icom_tx_empty(struct uart_port *port)
1184ab4382d2SGreg Kroah-Hartman {
1185f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1186ab4382d2SGreg Kroah-Hartman 	int ret;
1187ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1188ab4382d2SGreg Kroah-Hartman 
1189ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&port->lock, flags);
11907a5f86e8SJiri Slaby 	if (le16_to_cpu(icom_port->statStg->xmit[0].flags) &
1191ab4382d2SGreg Kroah-Hartman 	    SA_FLAGS_READY_TO_XMIT)
1192ab4382d2SGreg Kroah-Hartman 		ret = TIOCSER_TEMT;
1193ab4382d2SGreg Kroah-Hartman 	else
1194ab4382d2SGreg Kroah-Hartman 		ret = 0;
1195ab4382d2SGreg Kroah-Hartman 
1196ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&port->lock, flags);
1197ab4382d2SGreg Kroah-Hartman 	return ret;
1198ab4382d2SGreg Kroah-Hartman }
1199ab4382d2SGreg Kroah-Hartman 
icom_set_mctrl(struct uart_port * port,unsigned int mctrl)1200ab4382d2SGreg Kroah-Hartman static void icom_set_mctrl(struct uart_port *port, unsigned int mctrl)
1201ab4382d2SGreg Kroah-Hartman {
1202f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1203ab4382d2SGreg Kroah-Hartman 	unsigned char local_osr;
1204ab4382d2SGreg Kroah-Hartman 
1205f73989f5SJiri Slaby 	trace(icom_port, "SET_MODEM", 0);
1206f73989f5SJiri Slaby 	local_osr = readb(&icom_port->dram->osr);
1207ab4382d2SGreg Kroah-Hartman 
1208ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_RTS) {
1209f73989f5SJiri Slaby 		trace(icom_port, "RAISE_RTS", 0);
1210ab4382d2SGreg Kroah-Hartman 		local_osr |= ICOM_RTS;
1211ab4382d2SGreg Kroah-Hartman 	} else {
1212f73989f5SJiri Slaby 		trace(icom_port, "LOWER_RTS", 0);
1213ab4382d2SGreg Kroah-Hartman 		local_osr &= ~ICOM_RTS;
1214ab4382d2SGreg Kroah-Hartman 	}
1215ab4382d2SGreg Kroah-Hartman 
1216ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_DTR) {
1217f73989f5SJiri Slaby 		trace(icom_port, "RAISE_DTR", 0);
1218ab4382d2SGreg Kroah-Hartman 		local_osr |= ICOM_DTR;
1219ab4382d2SGreg Kroah-Hartman 	} else {
1220f73989f5SJiri Slaby 		trace(icom_port, "LOWER_DTR", 0);
1221ab4382d2SGreg Kroah-Hartman 		local_osr &= ~ICOM_DTR;
1222ab4382d2SGreg Kroah-Hartman 	}
1223ab4382d2SGreg Kroah-Hartman 
1224f73989f5SJiri Slaby 	writeb(local_osr, &icom_port->dram->osr);
1225ab4382d2SGreg Kroah-Hartman }
1226ab4382d2SGreg Kroah-Hartman 
icom_get_mctrl(struct uart_port * port)1227ab4382d2SGreg Kroah-Hartman static unsigned int icom_get_mctrl(struct uart_port *port)
1228ab4382d2SGreg Kroah-Hartman {
1229f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1230ab4382d2SGreg Kroah-Hartman 	unsigned char status;
1231ab4382d2SGreg Kroah-Hartman 	unsigned int result;
1232ab4382d2SGreg Kroah-Hartman 
1233f73989f5SJiri Slaby 	trace(icom_port, "GET_MODEM", 0);
1234ab4382d2SGreg Kroah-Hartman 
1235f73989f5SJiri Slaby 	status = readb(&icom_port->dram->isr);
1236ab4382d2SGreg Kroah-Hartman 
1237ab4382d2SGreg Kroah-Hartman 	result = ((status & ICOM_DCD) ? TIOCM_CAR : 0)
1238ab4382d2SGreg Kroah-Hartman 	    | ((status & ICOM_RI) ? TIOCM_RNG : 0)
1239ab4382d2SGreg Kroah-Hartman 	    | ((status & ICOM_DSR) ? TIOCM_DSR : 0)
1240ab4382d2SGreg Kroah-Hartman 	    | ((status & ICOM_CTS) ? TIOCM_CTS : 0);
1241ab4382d2SGreg Kroah-Hartman 	return result;
1242ab4382d2SGreg Kroah-Hartman }
1243ab4382d2SGreg Kroah-Hartman 
icom_stop_tx(struct uart_port * port)1244ab4382d2SGreg Kroah-Hartman static void icom_stop_tx(struct uart_port *port)
1245ab4382d2SGreg Kroah-Hartman {
1246f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1247ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1248ab4382d2SGreg Kroah-Hartman 
1249f73989f5SJiri Slaby 	trace(icom_port, "STOP", 0);
1250f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1251f73989f5SJiri Slaby 	writeb(cmdReg | CMD_HOLD_XMIT, &icom_port->dram->CmdReg);
1252ab4382d2SGreg Kroah-Hartman }
1253ab4382d2SGreg Kroah-Hartman 
icom_start_tx(struct uart_port * port)1254ab4382d2SGreg Kroah-Hartman static void icom_start_tx(struct uart_port *port)
1255ab4382d2SGreg Kroah-Hartman {
1256f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1257ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1258ab4382d2SGreg Kroah-Hartman 
1259f73989f5SJiri Slaby 	trace(icom_port, "START", 0);
1260f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1261ab4382d2SGreg Kroah-Hartman 	if ((cmdReg & CMD_HOLD_XMIT) == CMD_HOLD_XMIT)
1262ab4382d2SGreg Kroah-Hartman 		writeb(cmdReg & ~CMD_HOLD_XMIT,
1263f73989f5SJiri Slaby 		       &icom_port->dram->CmdReg);
1264ab4382d2SGreg Kroah-Hartman 
1265ab4382d2SGreg Kroah-Hartman 	icom_write(port);
1266ab4382d2SGreg Kroah-Hartman }
1267ab4382d2SGreg Kroah-Hartman 
icom_send_xchar(struct uart_port * port,char ch)1268ab4382d2SGreg Kroah-Hartman static void icom_send_xchar(struct uart_port *port, char ch)
1269ab4382d2SGreg Kroah-Hartman {
1270f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1271ab4382d2SGreg Kroah-Hartman 	unsigned char xdata;
1272ab4382d2SGreg Kroah-Hartman 	int index;
1273ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1274ab4382d2SGreg Kroah-Hartman 
1275f73989f5SJiri Slaby 	trace(icom_port, "SEND_XCHAR", ch);
1276ab4382d2SGreg Kroah-Hartman 
1277ab4382d2SGreg Kroah-Hartman 	/* wait .1 sec to send char */
1278ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < 10; index++) {
1279ab4382d2SGreg Kroah-Hartman 		spin_lock_irqsave(&port->lock, flags);
1280f73989f5SJiri Slaby 		xdata = readb(&icom_port->dram->xchar);
1281ab4382d2SGreg Kroah-Hartman 		if (xdata == 0x00) {
1282f73989f5SJiri Slaby 			trace(icom_port, "QUICK_WRITE", 0);
1283f73989f5SJiri Slaby 			writeb(ch, &icom_port->dram->xchar);
1284ab4382d2SGreg Kroah-Hartman 
1285ab4382d2SGreg Kroah-Hartman 			/* flush write operation */
1286f73989f5SJiri Slaby 			xdata = readb(&icom_port->dram->xchar);
1287ab4382d2SGreg Kroah-Hartman 			spin_unlock_irqrestore(&port->lock, flags);
1288ab4382d2SGreg Kroah-Hartman 			break;
1289ab4382d2SGreg Kroah-Hartman 		}
1290ab4382d2SGreg Kroah-Hartman 		spin_unlock_irqrestore(&port->lock, flags);
1291ab4382d2SGreg Kroah-Hartman 		msleep(10);
1292ab4382d2SGreg Kroah-Hartman 	}
1293ab4382d2SGreg Kroah-Hartman }
1294ab4382d2SGreg Kroah-Hartman 
icom_stop_rx(struct uart_port * port)1295ab4382d2SGreg Kroah-Hartman static void icom_stop_rx(struct uart_port *port)
1296ab4382d2SGreg Kroah-Hartman {
1297f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1298ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1299ab4382d2SGreg Kroah-Hartman 
1300f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1301f73989f5SJiri Slaby 	writeb(cmdReg & ~CMD_RCV_ENABLE, &icom_port->dram->CmdReg);
1302ab4382d2SGreg Kroah-Hartman }
1303ab4382d2SGreg Kroah-Hartman 
icom_break(struct uart_port * port,int break_state)1304ab4382d2SGreg Kroah-Hartman static void icom_break(struct uart_port *port, int break_state)
1305ab4382d2SGreg Kroah-Hartman {
1306f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1307ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1308ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1309ab4382d2SGreg Kroah-Hartman 
1310ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&port->lock, flags);
1311f73989f5SJiri Slaby 	trace(icom_port, "BREAK", 0);
1312f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1313ab4382d2SGreg Kroah-Hartman 	if (break_state == -1) {
1314f73989f5SJiri Slaby 		writeb(cmdReg | CMD_SND_BREAK, &icom_port->dram->CmdReg);
1315ab4382d2SGreg Kroah-Hartman 	} else {
1316f73989f5SJiri Slaby 		writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg);
1317ab4382d2SGreg Kroah-Hartman 	}
1318ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&port->lock, flags);
1319ab4382d2SGreg Kroah-Hartman }
1320ab4382d2SGreg Kroah-Hartman 
icom_open(struct uart_port * port)1321ab4382d2SGreg Kroah-Hartman static int icom_open(struct uart_port *port)
1322ab4382d2SGreg Kroah-Hartman {
1323f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1324ab4382d2SGreg Kroah-Hartman 	int retval;
1325ab4382d2SGreg Kroah-Hartman 
1326f73989f5SJiri Slaby 	kref_get(&icom_port->adapter->kref);
1327f73989f5SJiri Slaby 	retval = startup(icom_port);
1328ab4382d2SGreg Kroah-Hartman 
1329ab4382d2SGreg Kroah-Hartman 	if (retval) {
1330f73989f5SJiri Slaby 		kref_put(&icom_port->adapter->kref, icom_kref_release);
1331f73989f5SJiri Slaby 		trace(icom_port, "STARTUP_ERROR", 0);
1332ab4382d2SGreg Kroah-Hartman 		return retval;
1333ab4382d2SGreg Kroah-Hartman 	}
1334ab4382d2SGreg Kroah-Hartman 
1335ab4382d2SGreg Kroah-Hartman 	return 0;
1336ab4382d2SGreg Kroah-Hartman }
1337ab4382d2SGreg Kroah-Hartman 
icom_close(struct uart_port * port)1338ab4382d2SGreg Kroah-Hartman static void icom_close(struct uart_port *port)
1339ab4382d2SGreg Kroah-Hartman {
1340f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1341ab4382d2SGreg Kroah-Hartman 	unsigned char cmdReg;
1342ab4382d2SGreg Kroah-Hartman 
1343f73989f5SJiri Slaby 	trace(icom_port, "CLOSE", 0);
1344ab4382d2SGreg Kroah-Hartman 
1345ab4382d2SGreg Kroah-Hartman 	/* stop receiver */
1346f73989f5SJiri Slaby 	cmdReg = readb(&icom_port->dram->CmdReg);
1347f73989f5SJiri Slaby 	writeb(cmdReg & ~CMD_RCV_ENABLE, &icom_port->dram->CmdReg);
1348ab4382d2SGreg Kroah-Hartman 
1349f73989f5SJiri Slaby 	shutdown(icom_port);
1350ab4382d2SGreg Kroah-Hartman 
1351f73989f5SJiri Slaby 	kref_put(&icom_port->adapter->kref, icom_kref_release);
1352ab4382d2SGreg Kroah-Hartman }
1353ab4382d2SGreg Kroah-Hartman 
icom_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old_termios)1354*bec5b814SIlpo Järvinen static void icom_set_termios(struct uart_port *port, struct ktermios *termios,
1355*bec5b814SIlpo Järvinen 			     const struct ktermios *old_termios)
1356ab4382d2SGreg Kroah-Hartman {
1357f73989f5SJiri Slaby 	struct icom_port *icom_port = to_icom_port(port);
1358ab4382d2SGreg Kroah-Hartman 	int baud;
1359ab4382d2SGreg Kroah-Hartman 	unsigned cflag, iflag;
1360ab4382d2SGreg Kroah-Hartman 	char new_config2;
1361ab4382d2SGreg Kroah-Hartman 	char new_config3 = 0;
1362ab4382d2SGreg Kroah-Hartman 	char tmp_byte;
1363ab4382d2SGreg Kroah-Hartman 	int index;
1364ab4382d2SGreg Kroah-Hartman 	int rcv_buff, xmit_buff;
1365ab4382d2SGreg Kroah-Hartman 	unsigned long offset;
1366ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1367ab4382d2SGreg Kroah-Hartman 
1368ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&port->lock, flags);
1369f73989f5SJiri Slaby 	trace(icom_port, "CHANGE_SPEED", 0);
1370ab4382d2SGreg Kroah-Hartman 
1371ab4382d2SGreg Kroah-Hartman 	cflag = termios->c_cflag;
1372ab4382d2SGreg Kroah-Hartman 	iflag = termios->c_iflag;
1373ab4382d2SGreg Kroah-Hartman 
1374ab4382d2SGreg Kroah-Hartman 	new_config2 = ICOM_ACFG_DRIVE1;
1375ab4382d2SGreg Kroah-Hartman 
1376ab4382d2SGreg Kroah-Hartman 	/* byte size and parity */
1377ab4382d2SGreg Kroah-Hartman 	switch (cflag & CSIZE) {
1378ab4382d2SGreg Kroah-Hartman 	case CS5:		/* 5 bits/char */
1379ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_5BPC;
1380ab4382d2SGreg Kroah-Hartman 		break;
1381ab4382d2SGreg Kroah-Hartman 	case CS6:		/* 6 bits/char */
1382ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_6BPC;
1383ab4382d2SGreg Kroah-Hartman 		break;
1384ab4382d2SGreg Kroah-Hartman 	case CS7:		/* 7 bits/char */
1385ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_7BPC;
1386ab4382d2SGreg Kroah-Hartman 		break;
1387ab4382d2SGreg Kroah-Hartman 	case CS8:		/* 8 bits/char */
1388ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_8BPC;
1389ab4382d2SGreg Kroah-Hartman 		break;
1390ab4382d2SGreg Kroah-Hartman 	default:
1391ab4382d2SGreg Kroah-Hartman 		break;
1392ab4382d2SGreg Kroah-Hartman 	}
1393ab4382d2SGreg Kroah-Hartman 	if (cflag & CSTOPB) {
1394ab4382d2SGreg Kroah-Hartman 		/* 2 stop bits */
1395ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_2STOP_BIT;
1396ab4382d2SGreg Kroah-Hartman 	}
1397ab4382d2SGreg Kroah-Hartman 	if (cflag & PARENB) {
1398ab4382d2SGreg Kroah-Hartman 		/* parity bit enabled */
1399ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_PARITY_ENAB;
1400f73989f5SJiri Slaby 		trace(icom_port, "PARENB", 0);
1401ab4382d2SGreg Kroah-Hartman 	}
1402ab4382d2SGreg Kroah-Hartman 	if (cflag & PARODD) {
1403ab4382d2SGreg Kroah-Hartman 		/* odd parity */
1404ab4382d2SGreg Kroah-Hartman 		new_config2 |= ICOM_ACFG_PARITY_ODD;
1405f73989f5SJiri Slaby 		trace(icom_port, "PARODD", 0);
1406ab4382d2SGreg Kroah-Hartman 	}
1407ab4382d2SGreg Kroah-Hartman 
1408ab4382d2SGreg Kroah-Hartman 	/* Determine divisor based on baud rate */
1409ab4382d2SGreg Kroah-Hartman 	baud = uart_get_baud_rate(port, termios, old_termios,
1410ab4382d2SGreg Kroah-Hartman 				  icom_acfg_baud[0],
1411ab4382d2SGreg Kroah-Hartman 				  icom_acfg_baud[BAUD_TABLE_LIMIT]);
1412ab4382d2SGreg Kroah-Hartman 	if (!baud)
1413ab4382d2SGreg Kroah-Hartman 		baud = 9600;	/* B0 transition handled in rs_set_termios */
1414ab4382d2SGreg Kroah-Hartman 
1415ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < BAUD_TABLE_LIMIT; index++) {
1416ab4382d2SGreg Kroah-Hartman 		if (icom_acfg_baud[index] == baud) {
1417ab4382d2SGreg Kroah-Hartman 			new_config3 = index;
1418ab4382d2SGreg Kroah-Hartman 			break;
1419ab4382d2SGreg Kroah-Hartman 		}
1420ab4382d2SGreg Kroah-Hartman 	}
1421ab4382d2SGreg Kroah-Hartman 
1422ab4382d2SGreg Kroah-Hartman 	uart_update_timeout(port, cflag, baud);
1423ab4382d2SGreg Kroah-Hartman 
1424ab4382d2SGreg Kroah-Hartman 	/* CTS flow control flag and modem status interrupts */
1425f73989f5SJiri Slaby 	tmp_byte = readb(&(icom_port->dram->HDLCConfigReg));
1426ab4382d2SGreg Kroah-Hartman 	if (cflag & CRTSCTS)
1427ab4382d2SGreg Kroah-Hartman 		tmp_byte |= HDLC_HDW_FLOW;
1428ab4382d2SGreg Kroah-Hartman 	else
1429ab4382d2SGreg Kroah-Hartman 		tmp_byte &= ~HDLC_HDW_FLOW;
1430f73989f5SJiri Slaby 	writeb(tmp_byte, &(icom_port->dram->HDLCConfigReg));
1431ab4382d2SGreg Kroah-Hartman 
1432ab4382d2SGreg Kroah-Hartman 	/*
1433ab4382d2SGreg Kroah-Hartman 	 * Set up parity check flag
1434ab4382d2SGreg Kroah-Hartman 	 */
1435f73989f5SJiri Slaby 	icom_port->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE;
1436ab4382d2SGreg Kroah-Hartman 	if (iflag & INPCK)
1437f73989f5SJiri Slaby 		icom_port->read_status_mask |=
1438ab4382d2SGreg Kroah-Hartman 		    SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR;
1439ab4382d2SGreg Kroah-Hartman 
1440ab4382d2SGreg Kroah-Hartman 	if ((iflag & BRKINT) || (iflag & PARMRK))
1441f73989f5SJiri Slaby 		icom_port->read_status_mask |= SA_FLAGS_BREAK_DET;
1442ab4382d2SGreg Kroah-Hartman 
1443ab4382d2SGreg Kroah-Hartman 	/*
1444ab4382d2SGreg Kroah-Hartman 	 * Characters to ignore
1445ab4382d2SGreg Kroah-Hartman 	 */
1446f73989f5SJiri Slaby 	icom_port->ignore_status_mask = 0;
1447ab4382d2SGreg Kroah-Hartman 	if (iflag & IGNPAR)
1448f73989f5SJiri Slaby 		icom_port->ignore_status_mask |=
1449ab4382d2SGreg Kroah-Hartman 		    SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR;
1450ab4382d2SGreg Kroah-Hartman 	if (iflag & IGNBRK) {
1451f73989f5SJiri Slaby 		icom_port->ignore_status_mask |= SA_FLAGS_BREAK_DET;
1452ab4382d2SGreg Kroah-Hartman 		/*
1453ab4382d2SGreg Kroah-Hartman 		 * If we're ignore parity and break indicators, ignore
1454ab4382d2SGreg Kroah-Hartman 		 * overruns too.  (For real raw support).
1455ab4382d2SGreg Kroah-Hartman 		 */
1456ab4382d2SGreg Kroah-Hartman 		if (iflag & IGNPAR)
1457f73989f5SJiri Slaby 			icom_port->ignore_status_mask |= SA_FLAGS_OVERRUN;
1458ab4382d2SGreg Kroah-Hartman 	}
1459ab4382d2SGreg Kroah-Hartman 
1460ab4382d2SGreg Kroah-Hartman 	/*
1461ab4382d2SGreg Kroah-Hartman 	 * !!! ignore all characters if CREAD is not set
1462ab4382d2SGreg Kroah-Hartman 	 */
1463ab4382d2SGreg Kroah-Hartman 	if ((cflag & CREAD) == 0)
1464f73989f5SJiri Slaby 		icom_port->ignore_status_mask |= SA_FL_RCV_DONE;
1465ab4382d2SGreg Kroah-Hartman 
1466ab4382d2SGreg Kroah-Hartman 	/* Turn off Receiver to prepare for reset */
1467f73989f5SJiri Slaby 	writeb(CMD_RCV_DISABLE, &icom_port->dram->CmdReg);
1468ab4382d2SGreg Kroah-Hartman 
1469ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < 10; index++) {
1470f73989f5SJiri Slaby 		if (readb(&icom_port->dram->PrevCmdReg) == 0x00) {
1471ab4382d2SGreg Kroah-Hartman 			break;
1472ab4382d2SGreg Kroah-Hartman 		}
1473ab4382d2SGreg Kroah-Hartman 	}
1474ab4382d2SGreg Kroah-Hartman 
1475ab4382d2SGreg Kroah-Hartman 	/* clear all current buffers of data */
1476ab4382d2SGreg Kroah-Hartman 	for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) {
1477f73989f5SJiri Slaby 		icom_port->statStg->rcv[rcv_buff].flags = 0;
1478f73989f5SJiri Slaby 		icom_port->statStg->rcv[rcv_buff].leLength = 0;
1479f73989f5SJiri Slaby 		icom_port->statStg->rcv[rcv_buff].WorkingLength =
14807a5f86e8SJiri Slaby 		    cpu_to_le16(RCV_BUFF_SZ);
1481ab4382d2SGreg Kroah-Hartman 	}
1482ab4382d2SGreg Kroah-Hartman 
1483ab4382d2SGreg Kroah-Hartman 	for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) {
1484f73989f5SJiri Slaby 		icom_port->statStg->xmit[xmit_buff].flags = 0;
1485ab4382d2SGreg Kroah-Hartman 	}
1486ab4382d2SGreg Kroah-Hartman 
1487ab4382d2SGreg Kroah-Hartman 	/* activate changes and start xmit and receiver here */
1488ab4382d2SGreg Kroah-Hartman 	/* Enable the receiver */
1489f73989f5SJiri Slaby 	writeb(new_config3, &(icom_port->dram->async_config3));
1490f73989f5SJiri Slaby 	writeb(new_config2, &(icom_port->dram->async_config2));
1491f73989f5SJiri Slaby 	tmp_byte = readb(&(icom_port->dram->HDLCConfigReg));
1492ab4382d2SGreg Kroah-Hartman 	tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL;
1493f73989f5SJiri Slaby 	writeb(tmp_byte, &(icom_port->dram->HDLCConfigReg));
1494f73989f5SJiri Slaby 	writeb(0x04, &(icom_port->dram->FlagFillIdleTimer));	/* 0.5 seconds */
1495f73989f5SJiri Slaby 	writeb(0xFF, &(icom_port->dram->ier));	/* enable modem signal interrupts */
1496ab4382d2SGreg Kroah-Hartman 
1497ab4382d2SGreg Kroah-Hartman 	/* reset processor */
1498f73989f5SJiri Slaby 	writeb(CMD_RESTART, &icom_port->dram->CmdReg);
1499ab4382d2SGreg Kroah-Hartman 
1500ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < 10; index++) {
1501f73989f5SJiri Slaby 		if (readb(&icom_port->dram->CmdReg) == 0x00) {
1502ab4382d2SGreg Kroah-Hartman 			break;
1503ab4382d2SGreg Kroah-Hartman 		}
1504ab4382d2SGreg Kroah-Hartman 	}
1505ab4382d2SGreg Kroah-Hartman 
150625985edcSLucas De Marchi 	/* Enable Transmitter and Receiver */
1507ab4382d2SGreg Kroah-Hartman 	offset =
1508f73989f5SJiri Slaby 	    (unsigned long) &icom_port->statStg->rcv[0] -
1509f73989f5SJiri Slaby 	    (unsigned long) icom_port->statStg;
1510f73989f5SJiri Slaby 	writel(icom_port->statStg_pci + offset,
1511f73989f5SJiri Slaby 	       &icom_port->dram->RcvStatusAddr);
1512f73989f5SJiri Slaby 	icom_port->next_rcv = 0;
1513f73989f5SJiri Slaby 	*icom_port->xmitRestart = 0;
1514f73989f5SJiri Slaby 	writel(icom_port->xmitRestart_pci,
1515f73989f5SJiri Slaby 	       &icom_port->dram->XmitStatusAddr);
1516f73989f5SJiri Slaby 	trace(icom_port, "XR_ENAB", 0);
1517f73989f5SJiri Slaby 	writeb(CMD_XMIT_RCV_ENABLE, &icom_port->dram->CmdReg);
1518ab4382d2SGreg Kroah-Hartman 
1519ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&port->lock, flags);
1520ab4382d2SGreg Kroah-Hartman }
1521ab4382d2SGreg Kroah-Hartman 
icom_type(struct uart_port * port)1522ab4382d2SGreg Kroah-Hartman static const char *icom_type(struct uart_port *port)
1523ab4382d2SGreg Kroah-Hartman {
1524ab4382d2SGreg Kroah-Hartman 	return "icom";
1525ab4382d2SGreg Kroah-Hartman }
1526ab4382d2SGreg Kroah-Hartman 
icom_config_port(struct uart_port * port,int flags)1527ab4382d2SGreg Kroah-Hartman static void icom_config_port(struct uart_port *port, int flags)
1528ab4382d2SGreg Kroah-Hartman {
1529ab4382d2SGreg Kroah-Hartman 	port->type = PORT_ICOM;
1530ab4382d2SGreg Kroah-Hartman }
1531ab4382d2SGreg Kroah-Hartman 
15322331e068SBhumika Goyal static const struct uart_ops icom_ops = {
1533ab4382d2SGreg Kroah-Hartman 	.tx_empty = icom_tx_empty,
1534ab4382d2SGreg Kroah-Hartman 	.set_mctrl = icom_set_mctrl,
1535ab4382d2SGreg Kroah-Hartman 	.get_mctrl = icom_get_mctrl,
1536ab4382d2SGreg Kroah-Hartman 	.stop_tx = icom_stop_tx,
1537ab4382d2SGreg Kroah-Hartman 	.start_tx = icom_start_tx,
1538ab4382d2SGreg Kroah-Hartman 	.send_xchar = icom_send_xchar,
1539ab4382d2SGreg Kroah-Hartman 	.stop_rx = icom_stop_rx,
1540ab4382d2SGreg Kroah-Hartman 	.break_ctl = icom_break,
1541ab4382d2SGreg Kroah-Hartman 	.startup = icom_open,
1542ab4382d2SGreg Kroah-Hartman 	.shutdown = icom_close,
1543ab4382d2SGreg Kroah-Hartman 	.set_termios = icom_set_termios,
1544ab4382d2SGreg Kroah-Hartman 	.type = icom_type,
1545ab4382d2SGreg Kroah-Hartman 	.config_port = icom_config_port,
1546ab4382d2SGreg Kroah-Hartman };
1547ab4382d2SGreg Kroah-Hartman 
1548ab4382d2SGreg Kroah-Hartman #define ICOM_CONSOLE NULL
1549ab4382d2SGreg Kroah-Hartman 
1550ab4382d2SGreg Kroah-Hartman static struct uart_driver icom_uart_driver = {
1551ab4382d2SGreg Kroah-Hartman 	.owner = THIS_MODULE,
1552ab4382d2SGreg Kroah-Hartman 	.driver_name = ICOM_DRIVER_NAME,
1553ab4382d2SGreg Kroah-Hartman 	.dev_name = "ttyA",
1554ab4382d2SGreg Kroah-Hartman 	.major = ICOM_MAJOR,
1555ab4382d2SGreg Kroah-Hartman 	.minor = ICOM_MINOR_START,
1556ab4382d2SGreg Kroah-Hartman 	.nr = NR_PORTS,
1557ab4382d2SGreg Kroah-Hartman 	.cons = ICOM_CONSOLE,
1558ab4382d2SGreg Kroah-Hartman };
1559ab4382d2SGreg Kroah-Hartman 
icom_init_ports(struct icom_adapter * icom_adapter)15609671f099SBill Pemberton static int icom_init_ports(struct icom_adapter *icom_adapter)
1561ab4382d2SGreg Kroah-Hartman {
1562ab4382d2SGreg Kroah-Hartman 	u32 subsystem_id = icom_adapter->subsystem_id;
1563ab4382d2SGreg Kroah-Hartman 	int i;
1564ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1565ab4382d2SGreg Kroah-Hartman 
1566ab4382d2SGreg Kroah-Hartman 	if (icom_adapter->version == ADAPTER_V1) {
1567ab4382d2SGreg Kroah-Hartman 		icom_adapter->numb_ports = 2;
1568ab4382d2SGreg Kroah-Hartman 
1569ab4382d2SGreg Kroah-Hartman 		for (i = 0; i < 2; i++) {
1570ab4382d2SGreg Kroah-Hartman 			icom_port = &icom_adapter->port_info[i];
1571ab4382d2SGreg Kroah-Hartman 			icom_port->port = i;
1572ab4382d2SGreg Kroah-Hartman 			icom_port->status = ICOM_PORT_ACTIVE;
1573ab4382d2SGreg Kroah-Hartman 		}
1574ab4382d2SGreg Kroah-Hartman 	} else {
1575ab4382d2SGreg Kroah-Hartman 		if (subsystem_id == PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL) {
1576ab4382d2SGreg Kroah-Hartman 			icom_adapter->numb_ports = 4;
1577ab4382d2SGreg Kroah-Hartman 
1578ab4382d2SGreg Kroah-Hartman 			for (i = 0; i < 4; i++) {
1579ab4382d2SGreg Kroah-Hartman 				icom_port = &icom_adapter->port_info[i];
1580ab4382d2SGreg Kroah-Hartman 
1581ab4382d2SGreg Kroah-Hartman 				icom_port->port = i;
1582ab4382d2SGreg Kroah-Hartman 				icom_port->status = ICOM_PORT_ACTIVE;
1583ab4382d2SGreg Kroah-Hartman 			}
1584ab4382d2SGreg Kroah-Hartman 		} else {
1585ab4382d2SGreg Kroah-Hartman 			icom_adapter->numb_ports = 4;
1586ab4382d2SGreg Kroah-Hartman 
1587ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[0].port = 0;
1588ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[0].status = ICOM_PORT_ACTIVE;
1589ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[1].status = ICOM_PORT_OFF;
1590ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[2].port = 2;
1591ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[2].status = ICOM_PORT_ACTIVE;
1592ab4382d2SGreg Kroah-Hartman 			icom_adapter->port_info[3].status = ICOM_PORT_OFF;
1593ab4382d2SGreg Kroah-Hartman 		}
1594ab4382d2SGreg Kroah-Hartman 	}
1595ab4382d2SGreg Kroah-Hartman 
1596ab4382d2SGreg Kroah-Hartman 	return 0;
1597ab4382d2SGreg Kroah-Hartman }
1598ab4382d2SGreg Kroah-Hartman 
icom_port_active(struct icom_port * icom_port,struct icom_adapter * icom_adapter,int port_num)1599ab4382d2SGreg Kroah-Hartman static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *icom_adapter, int port_num)
1600ab4382d2SGreg Kroah-Hartman {
1601ab4382d2SGreg Kroah-Hartman 	if (icom_adapter->version == ADAPTER_V1) {
1602ab4382d2SGreg Kroah-Hartman 		icom_port->global_reg = icom_adapter->base_addr + 0x4000;
1603ab4382d2SGreg Kroah-Hartman 		icom_port->int_reg = icom_adapter->base_addr +
1604ab4382d2SGreg Kroah-Hartman 		    0x4004 + 2 - 2 * port_num;
1605ab4382d2SGreg Kroah-Hartman 	} else {
1606ab4382d2SGreg Kroah-Hartman 		icom_port->global_reg = icom_adapter->base_addr + 0x8000;
1607ab4382d2SGreg Kroah-Hartman 		if (icom_port->port < 2)
1608ab4382d2SGreg Kroah-Hartman 			icom_port->int_reg = icom_adapter->base_addr +
1609ab4382d2SGreg Kroah-Hartman 			    0x8004 + 2 - 2 * icom_port->port;
1610ab4382d2SGreg Kroah-Hartman 		else
1611ab4382d2SGreg Kroah-Hartman 			icom_port->int_reg = icom_adapter->base_addr +
1612ab4382d2SGreg Kroah-Hartman 			    0x8024 + 2 - 2 * (icom_port->port - 2);
1613ab4382d2SGreg Kroah-Hartman 	}
1614ab4382d2SGreg Kroah-Hartman }
icom_load_ports(struct icom_adapter * icom_adapter)16159671f099SBill Pemberton static int icom_load_ports(struct icom_adapter *icom_adapter)
1616ab4382d2SGreg Kroah-Hartman {
1617ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1618ab4382d2SGreg Kroah-Hartman 	int port_num;
1619ab4382d2SGreg Kroah-Hartman 
1620ab4382d2SGreg Kroah-Hartman 	for (port_num = 0; port_num < icom_adapter->numb_ports; port_num++) {
1621ab4382d2SGreg Kroah-Hartman 
1622ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[port_num];
1623ab4382d2SGreg Kroah-Hartman 
1624ab4382d2SGreg Kroah-Hartman 		if (icom_port->status == ICOM_PORT_ACTIVE) {
1625ab4382d2SGreg Kroah-Hartman 			icom_port_active(icom_port, icom_adapter, port_num);
1626ab4382d2SGreg Kroah-Hartman 			icom_port->dram = icom_adapter->base_addr +
1627ab4382d2SGreg Kroah-Hartman 					0x2000 * icom_port->port;
1628ab4382d2SGreg Kroah-Hartman 
1629ab4382d2SGreg Kroah-Hartman 			icom_port->adapter = icom_adapter;
1630ab4382d2SGreg Kroah-Hartman 
1631ab4382d2SGreg Kroah-Hartman 			/* get port memory */
1632ab4382d2SGreg Kroah-Hartman 			if (get_port_memory(icom_port) != 0) {
1633ab4382d2SGreg Kroah-Hartman 				dev_err(&icom_port->adapter->pci_dev->dev,
1634ab4382d2SGreg Kroah-Hartman 					"Memory allocation for port FAILED\n");
1635ab4382d2SGreg Kroah-Hartman 			}
1636ab4382d2SGreg Kroah-Hartman 		}
1637ab4382d2SGreg Kroah-Hartman 	}
1638ab4382d2SGreg Kroah-Hartman 	return 0;
1639ab4382d2SGreg Kroah-Hartman }
1640ab4382d2SGreg Kroah-Hartman 
icom_alloc_adapter(struct icom_adapter ** icom_adapter_ref)16419671f099SBill Pemberton static int icom_alloc_adapter(struct icom_adapter
1642ab4382d2SGreg Kroah-Hartman 					**icom_adapter_ref)
1643ab4382d2SGreg Kroah-Hartman {
1644ab4382d2SGreg Kroah-Hartman 	int adapter_count = 0;
1645ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *icom_adapter;
1646ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *cur_adapter_entry;
1647ab4382d2SGreg Kroah-Hartman 
1648b9a129f4SZhang Yanfei 	icom_adapter = kzalloc(sizeof(struct icom_adapter), GFP_KERNEL);
1649ab4382d2SGreg Kroah-Hartman 
1650ab4382d2SGreg Kroah-Hartman 	if (!icom_adapter) {
1651ab4382d2SGreg Kroah-Hartman 		return -ENOMEM;
1652ab4382d2SGreg Kroah-Hartman 	}
1653ab4382d2SGreg Kroah-Hartman 
1654e391e325SJiri Slaby 	list_for_each_entry(cur_adapter_entry, &icom_adapter_head,
1655e391e325SJiri Slaby 			icom_adapter_entry) {
1656ab4382d2SGreg Kroah-Hartman 		if (cur_adapter_entry->index != adapter_count) {
1657ab4382d2SGreg Kroah-Hartman 			break;
1658ab4382d2SGreg Kroah-Hartman 		}
1659ab4382d2SGreg Kroah-Hartman 		adapter_count++;
1660ab4382d2SGreg Kroah-Hartman 	}
1661ab4382d2SGreg Kroah-Hartman 
1662ab4382d2SGreg Kroah-Hartman 	icom_adapter->index = adapter_count;
1663e391e325SJiri Slaby 	list_add_tail(&icom_adapter->icom_adapter_entry,
1664e391e325SJiri Slaby 			&cur_adapter_entry->icom_adapter_entry);
1665ab4382d2SGreg Kroah-Hartman 
1666ab4382d2SGreg Kroah-Hartman 	*icom_adapter_ref = icom_adapter;
1667ab4382d2SGreg Kroah-Hartman 	return 0;
1668ab4382d2SGreg Kroah-Hartman }
1669ab4382d2SGreg Kroah-Hartman 
icom_free_adapter(struct icom_adapter * icom_adapter)1670ab4382d2SGreg Kroah-Hartman static void icom_free_adapter(struct icom_adapter *icom_adapter)
1671ab4382d2SGreg Kroah-Hartman {
1672ab4382d2SGreg Kroah-Hartman 	list_del(&icom_adapter->icom_adapter_entry);
1673ab4382d2SGreg Kroah-Hartman 	kfree(icom_adapter);
1674ab4382d2SGreg Kroah-Hartman }
1675ab4382d2SGreg Kroah-Hartman 
icom_kref_release(struct kref * kref)16762c334f12SJiri Slaby static void icom_kref_release(struct kref *kref)
1677ab4382d2SGreg Kroah-Hartman {
16782c334f12SJiri Slaby 	struct icom_adapter *icom_adapter = container_of(kref,
16792c334f12SJiri Slaby 			struct icom_adapter, kref);
1680ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1681ab4382d2SGreg Kroah-Hartman 	int index;
1682ab4382d2SGreg Kroah-Hartman 
1683ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < icom_adapter->numb_ports; index++) {
1684ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[index];
1685ab4382d2SGreg Kroah-Hartman 
1686ab4382d2SGreg Kroah-Hartman 		if (icom_port->status == ICOM_PORT_ACTIVE) {
1687ab4382d2SGreg Kroah-Hartman 			dev_info(&icom_adapter->pci_dev->dev,
1688ab4382d2SGreg Kroah-Hartman 				 "Device removed\n");
1689ab4382d2SGreg Kroah-Hartman 
1690ab4382d2SGreg Kroah-Hartman 			uart_remove_one_port(&icom_uart_driver,
1691ab4382d2SGreg Kroah-Hartman 					     &icom_port->uart_port);
1692ab4382d2SGreg Kroah-Hartman 
1693ab4382d2SGreg Kroah-Hartman 			/* be sure that DTR and RTS are dropped */
1694ab4382d2SGreg Kroah-Hartman 			writeb(0x00, &icom_port->dram->osr);
1695ab4382d2SGreg Kroah-Hartman 
1696ab4382d2SGreg Kroah-Hartman 			/* Wait 0.1 Sec for simple Init to complete */
1697ab4382d2SGreg Kroah-Hartman 			msleep(100);
1698ab4382d2SGreg Kroah-Hartman 
1699ab4382d2SGreg Kroah-Hartman 			/* Stop proccessor */
1700ab4382d2SGreg Kroah-Hartman 			stop_processor(icom_port);
1701ab4382d2SGreg Kroah-Hartman 
1702ab4382d2SGreg Kroah-Hartman 			free_port_memory(icom_port);
1703ab4382d2SGreg Kroah-Hartman 		}
1704ab4382d2SGreg Kroah-Hartman 	}
1705ab4382d2SGreg Kroah-Hartman 
1706ab4382d2SGreg Kroah-Hartman 	free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter);
1707ab4382d2SGreg Kroah-Hartman 	iounmap(icom_adapter->base_addr);
1708ab4382d2SGreg Kroah-Hartman 	pci_release_regions(icom_adapter->pci_dev);
1709ab4382d2SGreg Kroah-Hartman 	icom_free_adapter(icom_adapter);
1710ab4382d2SGreg Kroah-Hartman }
1711ab4382d2SGreg Kroah-Hartman 
icom_probe(struct pci_dev * dev,const struct pci_device_id * ent)17129671f099SBill Pemberton static int icom_probe(struct pci_dev *dev,
1713ab4382d2SGreg Kroah-Hartman 				const struct pci_device_id *ent)
1714ab4382d2SGreg Kroah-Hartman {
1715ab4382d2SGreg Kroah-Hartman 	int index;
1716ab4382d2SGreg Kroah-Hartman 	unsigned int command_reg;
1717ab4382d2SGreg Kroah-Hartman 	int retval;
1718ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *icom_adapter;
1719ab4382d2SGreg Kroah-Hartman 	struct icom_port *icom_port;
1720ab4382d2SGreg Kroah-Hartman 
1721ab4382d2SGreg Kroah-Hartman 	retval = pci_enable_device(dev);
1722ab4382d2SGreg Kroah-Hartman 	if (retval) {
1723ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Device enable FAILED\n");
1724ab4382d2SGreg Kroah-Hartman 		return retval;
1725ab4382d2SGreg Kroah-Hartman 	}
1726ab4382d2SGreg Kroah-Hartman 
172701e51df5SGreg Kroah-Hartman 	retval = pci_request_regions(dev, "icom");
172801e51df5SGreg Kroah-Hartman 	if (retval) {
1729ab4382d2SGreg Kroah-Hartman 		 dev_err(&dev->dev, "pci_request_regions FAILED\n");
1730ab4382d2SGreg Kroah-Hartman 		 pci_disable_device(dev);
1731ab4382d2SGreg Kroah-Hartman 		 return retval;
1732ab4382d2SGreg Kroah-Hartman 	 }
1733ab4382d2SGreg Kroah-Hartman 
1734ab4382d2SGreg Kroah-Hartman 	pci_set_master(dev);
1735ab4382d2SGreg Kroah-Hartman 
173601e51df5SGreg Kroah-Hartman 	retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg);
173701e51df5SGreg Kroah-Hartman 	if (retval) {
1738ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "PCI Config read FAILED\n");
1739ee157a79SHuang Guobin 		goto probe_exit0;
1740ab4382d2SGreg Kroah-Hartman 	}
1741ab4382d2SGreg Kroah-Hartman 
1742ab4382d2SGreg Kroah-Hartman 	pci_write_config_dword(dev, PCI_COMMAND,
1743ab4382d2SGreg Kroah-Hartman 		command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
1744ab4382d2SGreg Kroah-Hartman  		| PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
1745ab4382d2SGreg Kroah-Hartman 
1746ab4382d2SGreg Kroah-Hartman 	if (ent->driver_data == ADAPTER_V1) {
1747ab4382d2SGreg Kroah-Hartman 		pci_write_config_dword(dev, 0x44, 0x8300830A);
1748ab4382d2SGreg Kroah-Hartman 	} else {
1749ab4382d2SGreg Kroah-Hartman 		pci_write_config_dword(dev, 0x44, 0x42004200);
1750ab4382d2SGreg Kroah-Hartman 		pci_write_config_dword(dev, 0x48, 0x42004200);
1751ab4382d2SGreg Kroah-Hartman 	}
1752ab4382d2SGreg Kroah-Hartman 
1753ab4382d2SGreg Kroah-Hartman 
1754ab4382d2SGreg Kroah-Hartman 	retval = icom_alloc_adapter(&icom_adapter);
1755ab4382d2SGreg Kroah-Hartman 	if (retval) {
1756ab4382d2SGreg Kroah-Hartman 		 dev_err(&dev->dev, "icom_alloc_adapter FAILED\n");
1757ab4382d2SGreg Kroah-Hartman 		 retval = -EIO;
1758ab4382d2SGreg Kroah-Hartman 		 goto probe_exit0;
1759ab4382d2SGreg Kroah-Hartman 	}
1760ab4382d2SGreg Kroah-Hartman 
1761ab4382d2SGreg Kroah-Hartman 	icom_adapter->base_addr_pci = pci_resource_start(dev, 0);
1762ab4382d2SGreg Kroah-Hartman 	icom_adapter->pci_dev = dev;
1763ab4382d2SGreg Kroah-Hartman 	icom_adapter->version = ent->driver_data;
1764ab4382d2SGreg Kroah-Hartman 	icom_adapter->subsystem_id = ent->subdevice;
1765ab4382d2SGreg Kroah-Hartman 
1766ab4382d2SGreg Kroah-Hartman 
1767ab4382d2SGreg Kroah-Hartman 	retval = icom_init_ports(icom_adapter);
1768ab4382d2SGreg Kroah-Hartman 	if (retval) {
1769ab4382d2SGreg Kroah-Hartman 		dev_err(&dev->dev, "Port configuration failed\n");
1770ab4382d2SGreg Kroah-Hartman 		goto probe_exit1;
1771ab4382d2SGreg Kroah-Hartman 	}
1772ab4382d2SGreg Kroah-Hartman 
1773ab4382d2SGreg Kroah-Hartman 	icom_adapter->base_addr = pci_ioremap_bar(dev, 0);
1774ab4382d2SGreg Kroah-Hartman 
1775ddcbad92SJulia Lawall 	if (!icom_adapter->base_addr) {
1776ddcbad92SJulia Lawall 		retval = -ENOMEM;
1777ab4382d2SGreg Kroah-Hartman 		goto probe_exit1;
1778ddcbad92SJulia Lawall 	}
1779ab4382d2SGreg Kroah-Hartman 
1780ab4382d2SGreg Kroah-Hartman 	 /* save off irq and request irq line */
178101e51df5SGreg Kroah-Hartman 	 retval = request_irq(dev->irq, icom_interrupt, IRQF_SHARED, ICOM_DRIVER_NAME, (void *)icom_adapter);
178201e51df5SGreg Kroah-Hartman 	 if (retval) {
1783ab4382d2SGreg Kroah-Hartman 		  goto probe_exit2;
1784ab4382d2SGreg Kroah-Hartman 	 }
1785ab4382d2SGreg Kroah-Hartman 
1786ab4382d2SGreg Kroah-Hartman 	retval = icom_load_ports(icom_adapter);
1787ab4382d2SGreg Kroah-Hartman 
1788ab4382d2SGreg Kroah-Hartman 	for (index = 0; index < icom_adapter->numb_ports; index++) {
1789ab4382d2SGreg Kroah-Hartman 		icom_port = &icom_adapter->port_info[index];
1790ab4382d2SGreg Kroah-Hartman 
1791ab4382d2SGreg Kroah-Hartman 		if (icom_port->status == ICOM_PORT_ACTIVE) {
1792ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.irq = icom_port->adapter->pci_dev->irq;
1793ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.type = PORT_ICOM;
1794ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.iotype = UPIO_MEM;
1795ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.membase =
17965a7daed8SJingoo Han 				(unsigned char __iomem *)icom_adapter->base_addr_pci;
1797ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.fifosize = 16;
1798ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.ops = &icom_ops;
1799ab4382d2SGreg Kroah-Hartman 			icom_port->uart_port.line =
1800ab4382d2SGreg Kroah-Hartman 		        icom_port->port + icom_adapter->index * 4;
1801ab4382d2SGreg Kroah-Hartman 			if (uart_add_one_port (&icom_uart_driver, &icom_port->uart_port)) {
1802ab4382d2SGreg Kroah-Hartman 				icom_port->status = ICOM_PORT_OFF;
1803ab4382d2SGreg Kroah-Hartman 				dev_err(&dev->dev, "Device add failed\n");
1804ab4382d2SGreg Kroah-Hartman 			 } else
1805ab4382d2SGreg Kroah-Hartman 				dev_info(&dev->dev, "Device added\n");
1806ab4382d2SGreg Kroah-Hartman 		}
1807ab4382d2SGreg Kroah-Hartman 	}
1808ab4382d2SGreg Kroah-Hartman 
1809ab4382d2SGreg Kroah-Hartman 	kref_init(&icom_adapter->kref);
1810ab4382d2SGreg Kroah-Hartman 	return 0;
1811ab4382d2SGreg Kroah-Hartman 
1812ab4382d2SGreg Kroah-Hartman probe_exit2:
1813ab4382d2SGreg Kroah-Hartman 	iounmap(icom_adapter->base_addr);
1814ab4382d2SGreg Kroah-Hartman probe_exit1:
1815ab4382d2SGreg Kroah-Hartman 	icom_free_adapter(icom_adapter);
1816ab4382d2SGreg Kroah-Hartman 
1817ab4382d2SGreg Kroah-Hartman probe_exit0:
1818ab4382d2SGreg Kroah-Hartman 	pci_release_regions(dev);
1819ab4382d2SGreg Kroah-Hartman 	pci_disable_device(dev);
1820ab4382d2SGreg Kroah-Hartman 
1821ab4382d2SGreg Kroah-Hartman 	return retval;
1822ab4382d2SGreg Kroah-Hartman }
1823ab4382d2SGreg Kroah-Hartman 
icom_remove(struct pci_dev * dev)1824ae8d8a14SBill Pemberton static void icom_remove(struct pci_dev *dev)
1825ab4382d2SGreg Kroah-Hartman {
1826ab4382d2SGreg Kroah-Hartman 	struct icom_adapter *icom_adapter;
1827ab4382d2SGreg Kroah-Hartman 
1828e391e325SJiri Slaby 	list_for_each_entry(icom_adapter, &icom_adapter_head,
1829e391e325SJiri Slaby 			icom_adapter_entry) {
1830ab4382d2SGreg Kroah-Hartman 		if (icom_adapter->pci_dev == dev) {
1831ab4382d2SGreg Kroah-Hartman 			kref_put(&icom_adapter->kref, icom_kref_release);
1832ab4382d2SGreg Kroah-Hartman 			return;
1833ab4382d2SGreg Kroah-Hartman 		}
1834ab4382d2SGreg Kroah-Hartman 	}
1835ab4382d2SGreg Kroah-Hartman 
1836ab4382d2SGreg Kroah-Hartman 	dev_err(&dev->dev, "Unable to find device to remove\n");
1837ab4382d2SGreg Kroah-Hartman }
1838ab4382d2SGreg Kroah-Hartman 
1839ab4382d2SGreg Kroah-Hartman static struct pci_driver icom_pci_driver = {
1840ab4382d2SGreg Kroah-Hartman 	.name = ICOM_DRIVER_NAME,
1841ab4382d2SGreg Kroah-Hartman 	.id_table = icom_pci_table,
1842ab4382d2SGreg Kroah-Hartman 	.probe = icom_probe,
18432d47b716SBill Pemberton 	.remove = icom_remove,
1844ab4382d2SGreg Kroah-Hartman };
1845ab4382d2SGreg Kroah-Hartman 
icom_init(void)1846ab4382d2SGreg Kroah-Hartman static int __init icom_init(void)
1847ab4382d2SGreg Kroah-Hartman {
1848ab4382d2SGreg Kroah-Hartman 	int ret;
1849ab4382d2SGreg Kroah-Hartman 
1850ab4382d2SGreg Kroah-Hartman 	ret = uart_register_driver(&icom_uart_driver);
1851ab4382d2SGreg Kroah-Hartman 	if (ret)
1852ab4382d2SGreg Kroah-Hartman 		return ret;
1853ab4382d2SGreg Kroah-Hartman 
1854ab4382d2SGreg Kroah-Hartman 	ret = pci_register_driver(&icom_pci_driver);
1855ab4382d2SGreg Kroah-Hartman 
1856ab4382d2SGreg Kroah-Hartman 	if (ret < 0)
1857ab4382d2SGreg Kroah-Hartman 		uart_unregister_driver(&icom_uart_driver);
1858ab4382d2SGreg Kroah-Hartman 
1859ab4382d2SGreg Kroah-Hartman 	return ret;
1860ab4382d2SGreg Kroah-Hartman }
1861ab4382d2SGreg Kroah-Hartman 
icom_exit(void)1862ab4382d2SGreg Kroah-Hartman static void __exit icom_exit(void)
1863ab4382d2SGreg Kroah-Hartman {
1864ab4382d2SGreg Kroah-Hartman 	pci_unregister_driver(&icom_pci_driver);
1865ab4382d2SGreg Kroah-Hartman 	uart_unregister_driver(&icom_uart_driver);
1866ab4382d2SGreg Kroah-Hartman }
1867ab4382d2SGreg Kroah-Hartman 
1868ab4382d2SGreg Kroah-Hartman module_init(icom_init);
1869ab4382d2SGreg Kroah-Hartman module_exit(icom_exit);
1870ab4382d2SGreg Kroah-Hartman 
1871ab4382d2SGreg Kroah-Hartman MODULE_AUTHOR("Michael Anderson <mjanders@us.ibm.com>");
1872ab4382d2SGreg Kroah-Hartman MODULE_DESCRIPTION("IBM iSeries Serial IOA driver");
1873ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL");
1874ab4382d2SGreg Kroah-Hartman MODULE_FIRMWARE("icom_call_setup.bin");
1875ab4382d2SGreg Kroah-Hartman MODULE_FIRMWARE("icom_res_dce.bin");
1876ab4382d2SGreg Kroah-Hartman MODULE_FIRMWARE("icom_asc.bin");
1877