162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*****************************************************************************/
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci *           moxa.c  -- MOXA Intellio family multiport serial driver.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *      Copyright (C) 1999-2000  Moxa Technologies (support@moxa.com).
762306a36Sopenharmony_ci *      Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *      This code is loosely based on the Linux serial driver, written by
1062306a36Sopenharmony_ci *      Linus Torvalds, Theodore T'so and others.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci *    MOXA Intellio Series Driver
1562306a36Sopenharmony_ci *      for             : LINUX
1662306a36Sopenharmony_ci *      date            : 1999/1/7
1762306a36Sopenharmony_ci *      version         : 5.1
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/types.h>
2262306a36Sopenharmony_ci#include <linux/mm.h>
2362306a36Sopenharmony_ci#include <linux/ioport.h>
2462306a36Sopenharmony_ci#include <linux/errno.h>
2562306a36Sopenharmony_ci#include <linux/firmware.h>
2662306a36Sopenharmony_ci#include <linux/signal.h>
2762306a36Sopenharmony_ci#include <linux/sched.h>
2862306a36Sopenharmony_ci#include <linux/timer.h>
2962306a36Sopenharmony_ci#include <linux/interrupt.h>
3062306a36Sopenharmony_ci#include <linux/tty.h>
3162306a36Sopenharmony_ci#include <linux/tty_flip.h>
3262306a36Sopenharmony_ci#include <linux/major.h>
3362306a36Sopenharmony_ci#include <linux/string.h>
3462306a36Sopenharmony_ci#include <linux/fcntl.h>
3562306a36Sopenharmony_ci#include <linux/ptrace.h>
3662306a36Sopenharmony_ci#include <linux/serial.h>
3762306a36Sopenharmony_ci#include <linux/tty_driver.h>
3862306a36Sopenharmony_ci#include <linux/delay.h>
3962306a36Sopenharmony_ci#include <linux/pci.h>
4062306a36Sopenharmony_ci#include <linux/init.h>
4162306a36Sopenharmony_ci#include <linux/bitops.h>
4262306a36Sopenharmony_ci#include <linux/slab.h>
4362306a36Sopenharmony_ci#include <linux/ratelimit.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <asm/io.h>
4662306a36Sopenharmony_ci#include <linux/uaccess.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define	MOXA			0x400
4962306a36Sopenharmony_ci#define MOXA_GET_IQUEUE		(MOXA + 1)	/* get input buffered count */
5062306a36Sopenharmony_ci#define MOXA_GET_OQUEUE		(MOXA + 2)	/* get output buffered count */
5162306a36Sopenharmony_ci#define MOXA_GETDATACOUNT       (MOXA + 23)
5262306a36Sopenharmony_ci#define MOXA_GET_IOQUEUE	(MOXA + 27)
5362306a36Sopenharmony_ci#define MOXA_FLUSH_QUEUE	(MOXA + 28)
5462306a36Sopenharmony_ci#define MOXA_GETMSTATUS         (MOXA + 65)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci *    System Configuration
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define Magic_code	0x404
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci *    for C218 BIOS initialization
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_ci#define C218_ConfBase	0x800
6662306a36Sopenharmony_ci#define C218_status	(C218_ConfBase + 0)	/* BIOS running status    */
6762306a36Sopenharmony_ci#define C218_diag	(C218_ConfBase + 2)	/* diagnostic status      */
6862306a36Sopenharmony_ci#define C218_key	(C218_ConfBase + 4)	/* WORD (0x218 for C218) */
6962306a36Sopenharmony_ci#define C218DLoad_len	(C218_ConfBase + 6)	/* WORD           */
7062306a36Sopenharmony_ci#define C218check_sum	(C218_ConfBase + 8)	/* BYTE           */
7162306a36Sopenharmony_ci#define C218chksum_ok	(C218_ConfBase + 0x0a)	/* BYTE (1:ok)            */
7262306a36Sopenharmony_ci#define C218_TestRx	(C218_ConfBase + 0x10)	/* 8 bytes for 8 ports    */
7362306a36Sopenharmony_ci#define C218_TestTx	(C218_ConfBase + 0x18)	/* 8 bytes for 8 ports    */
7462306a36Sopenharmony_ci#define C218_RXerr	(C218_ConfBase + 0x20)	/* 8 bytes for 8 ports    */
7562306a36Sopenharmony_ci#define C218_ErrFlag	(C218_ConfBase + 0x28)	/* 8 bytes for 8 ports    */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define C218_LoadBuf	0x0F00
7862306a36Sopenharmony_ci#define C218_KeyCode	0x218
7962306a36Sopenharmony_ci#define CP204J_KeyCode	0x204
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/*
8262306a36Sopenharmony_ci *    for C320 BIOS initialization
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_ci#define C320_ConfBase	0x800
8562306a36Sopenharmony_ci#define C320_LoadBuf	0x0f00
8662306a36Sopenharmony_ci#define STS_init	0x05	/* for C320_status        */
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define C320_status	C320_ConfBase + 0	/* BIOS running status    */
8962306a36Sopenharmony_ci#define C320_diag	C320_ConfBase + 2	/* diagnostic status      */
9062306a36Sopenharmony_ci#define C320_key	C320_ConfBase + 4	/* WORD (0320H for C320) */
9162306a36Sopenharmony_ci#define C320DLoad_len	C320_ConfBase + 6	/* WORD           */
9262306a36Sopenharmony_ci#define C320check_sum	C320_ConfBase + 8	/* WORD           */
9362306a36Sopenharmony_ci#define C320chksum_ok	C320_ConfBase + 0x0a	/* WORD (1:ok)            */
9462306a36Sopenharmony_ci#define C320bapi_len	C320_ConfBase + 0x0c	/* WORD           */
9562306a36Sopenharmony_ci#define C320UART_no	C320_ConfBase + 0x0e	/* WORD           */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define C320_KeyCode	0x320
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define FixPage_addr	0x0000	/* starting addr of static page  */
10062306a36Sopenharmony_ci#define DynPage_addr	0x2000	/* starting addr of dynamic page */
10162306a36Sopenharmony_ci#define C218_start	0x3000	/* starting addr of C218 BIOS prg */
10262306a36Sopenharmony_ci#define Control_reg	0x1ff0	/* select page and reset control */
10362306a36Sopenharmony_ci#define HW_reset	0x80
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/*
10662306a36Sopenharmony_ci *    Function Codes
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_ci#define FC_CardReset	0x80
10962306a36Sopenharmony_ci#define FC_ChannelReset 1	/* C320 firmware not supported */
11062306a36Sopenharmony_ci#define FC_EnableCH	2
11162306a36Sopenharmony_ci#define FC_DisableCH	3
11262306a36Sopenharmony_ci#define FC_SetParam	4
11362306a36Sopenharmony_ci#define FC_SetMode	5
11462306a36Sopenharmony_ci#define FC_SetRate	6
11562306a36Sopenharmony_ci#define FC_LineControl	7
11662306a36Sopenharmony_ci#define FC_LineStatus	8
11762306a36Sopenharmony_ci#define FC_XmitControl	9
11862306a36Sopenharmony_ci#define FC_FlushQueue	10
11962306a36Sopenharmony_ci#define FC_SendBreak	11
12062306a36Sopenharmony_ci#define FC_StopBreak	12
12162306a36Sopenharmony_ci#define FC_LoopbackON	13
12262306a36Sopenharmony_ci#define FC_LoopbackOFF	14
12362306a36Sopenharmony_ci#define FC_ClrIrqTable	15
12462306a36Sopenharmony_ci#define FC_SendXon	16
12562306a36Sopenharmony_ci#define FC_SetTermIrq	17	/* C320 firmware not supported */
12662306a36Sopenharmony_ci#define FC_SetCntIrq	18	/* C320 firmware not supported */
12762306a36Sopenharmony_ci#define FC_SetBreakIrq	19
12862306a36Sopenharmony_ci#define FC_SetLineIrq	20
12962306a36Sopenharmony_ci#define FC_SetFlowCtl	21
13062306a36Sopenharmony_ci#define FC_GenIrq	22
13162306a36Sopenharmony_ci#define FC_InCD180	23
13262306a36Sopenharmony_ci#define FC_OutCD180	24
13362306a36Sopenharmony_ci#define FC_InUARTreg	23
13462306a36Sopenharmony_ci#define FC_OutUARTreg	24
13562306a36Sopenharmony_ci#define FC_SetXonXoff	25
13662306a36Sopenharmony_ci#define FC_OutCD180CCR	26
13762306a36Sopenharmony_ci#define FC_ExtIQueue	27
13862306a36Sopenharmony_ci#define FC_ExtOQueue	28
13962306a36Sopenharmony_ci#define FC_ClrLineIrq	29
14062306a36Sopenharmony_ci#define FC_HWFlowCtl	30
14162306a36Sopenharmony_ci#define FC_GetClockRate 35
14262306a36Sopenharmony_ci#define FC_SetBaud	36
14362306a36Sopenharmony_ci#define FC_SetDataMode  41
14462306a36Sopenharmony_ci#define FC_GetCCSR      43
14562306a36Sopenharmony_ci#define FC_GetDataError 45
14662306a36Sopenharmony_ci#define FC_RxControl	50
14762306a36Sopenharmony_ci#define FC_ImmSend	51
14862306a36Sopenharmony_ci#define FC_SetXonState	52
14962306a36Sopenharmony_ci#define FC_SetXoffState	53
15062306a36Sopenharmony_ci#define FC_SetRxFIFOTrig 54
15162306a36Sopenharmony_ci#define FC_SetTxFIFOCnt 55
15262306a36Sopenharmony_ci#define FC_UnixRate	56
15362306a36Sopenharmony_ci#define FC_UnixResetTimer 57
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#define	RxFIFOTrig1	0
15662306a36Sopenharmony_ci#define	RxFIFOTrig4	1
15762306a36Sopenharmony_ci#define	RxFIFOTrig8	2
15862306a36Sopenharmony_ci#define	RxFIFOTrig14	3
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/*
16162306a36Sopenharmony_ci *    Dual-Ported RAM
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ci#define DRAM_global	0
16462306a36Sopenharmony_ci#define INT_data	(DRAM_global + 0)
16562306a36Sopenharmony_ci#define Config_base	(DRAM_global + 0x108)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#define IRQindex	(INT_data + 0)
16862306a36Sopenharmony_ci#define IRQpending	(INT_data + 4)
16962306a36Sopenharmony_ci#define IRQtable	(INT_data + 8)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/*
17262306a36Sopenharmony_ci *    Interrupt Status
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_ci#define IntrRx		0x01	/* receiver data O.K.             */
17562306a36Sopenharmony_ci#define IntrTx		0x02	/* transmit buffer empty  */
17662306a36Sopenharmony_ci#define IntrFunc	0x04	/* function complete              */
17762306a36Sopenharmony_ci#define IntrBreak	0x08	/* received break         */
17862306a36Sopenharmony_ci#define IntrLine	0x10	/* line status change
17962306a36Sopenharmony_ci				   for transmitter                */
18062306a36Sopenharmony_ci#define IntrIntr	0x20	/* received INTR code             */
18162306a36Sopenharmony_ci#define IntrQuit	0x40	/* received QUIT code             */
18262306a36Sopenharmony_ci#define IntrEOF		0x80	/* received EOF code              */
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci#define IntrRxTrigger	0x100	/* rx data count reach trigger value */
18562306a36Sopenharmony_ci#define IntrTxTrigger	0x200	/* tx data count below trigger value */
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci#define Magic_no	(Config_base + 0)
18862306a36Sopenharmony_ci#define Card_model_no	(Config_base + 2)
18962306a36Sopenharmony_ci#define Total_ports	(Config_base + 4)
19062306a36Sopenharmony_ci#define Module_cnt	(Config_base + 8)
19162306a36Sopenharmony_ci#define Module_no	(Config_base + 10)
19262306a36Sopenharmony_ci#define Timer_10ms	(Config_base + 14)
19362306a36Sopenharmony_ci#define Disable_IRQ	(Config_base + 20)
19462306a36Sopenharmony_ci#define TMS320_PORT1	(Config_base + 22)
19562306a36Sopenharmony_ci#define TMS320_PORT2	(Config_base + 24)
19662306a36Sopenharmony_ci#define TMS320_CLOCK	(Config_base + 26)
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci *    DATA BUFFER in DRAM
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_ci#define Extern_table	0x400	/* Base address of the external table
20262306a36Sopenharmony_ci				   (24 words *    64) total 3K bytes
20362306a36Sopenharmony_ci				   (24 words * 128) total 6K bytes */
20462306a36Sopenharmony_ci#define Extern_size	0x60	/* 96 bytes                       */
20562306a36Sopenharmony_ci#define RXrptr		0x00	/* read pointer for RX buffer     */
20662306a36Sopenharmony_ci#define RXwptr		0x02	/* write pointer for RX buffer    */
20762306a36Sopenharmony_ci#define TXrptr		0x04	/* read pointer for TX buffer     */
20862306a36Sopenharmony_ci#define TXwptr		0x06	/* write pointer for TX buffer    */
20962306a36Sopenharmony_ci#define HostStat	0x08	/* IRQ flag and general flag      */
21062306a36Sopenharmony_ci#define FlagStat	0x0A
21162306a36Sopenharmony_ci#define FlowControl	0x0C	/* B7 B6 B5 B4 B3 B2 B1 B0              */
21262306a36Sopenharmony_ci				/*  x  x  x  x  |  |  |  |            */
21362306a36Sopenharmony_ci				/*              |  |  |  + CTS flow   */
21462306a36Sopenharmony_ci				/*              |  |  +--- RTS flow   */
21562306a36Sopenharmony_ci				/*              |  +------ TX Xon/Xoff */
21662306a36Sopenharmony_ci				/*              +--------- RX Xon/Xoff */
21762306a36Sopenharmony_ci#define Break_cnt	0x0E	/* received break count   */
21862306a36Sopenharmony_ci#define CD180TXirq	0x10	/* if non-0: enable TX irq        */
21962306a36Sopenharmony_ci#define RX_mask		0x12
22062306a36Sopenharmony_ci#define TX_mask		0x14
22162306a36Sopenharmony_ci#define Ofs_rxb		0x16
22262306a36Sopenharmony_ci#define Ofs_txb		0x18
22362306a36Sopenharmony_ci#define Page_rxb	0x1A
22462306a36Sopenharmony_ci#define Page_txb	0x1C
22562306a36Sopenharmony_ci#define EndPage_rxb	0x1E
22662306a36Sopenharmony_ci#define EndPage_txb	0x20
22762306a36Sopenharmony_ci#define Data_error	0x22
22862306a36Sopenharmony_ci#define RxTrigger	0x28
22962306a36Sopenharmony_ci#define TxTrigger	0x2a
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci#define rRXwptr		0x34
23262306a36Sopenharmony_ci#define Low_water	0x36
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci#define FuncCode	0x40
23562306a36Sopenharmony_ci#define FuncArg		0x42
23662306a36Sopenharmony_ci#define FuncArg1	0x44
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci#define C218rx_size	0x2000	/* 8K bytes */
23962306a36Sopenharmony_ci#define C218tx_size	0x8000	/* 32K bytes */
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci#define C218rx_mask	(C218rx_size - 1)
24262306a36Sopenharmony_ci#define C218tx_mask	(C218tx_size - 1)
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci#define C320p8rx_size	0x2000
24562306a36Sopenharmony_ci#define C320p8tx_size	0x8000
24662306a36Sopenharmony_ci#define C320p8rx_mask	(C320p8rx_size - 1)
24762306a36Sopenharmony_ci#define C320p8tx_mask	(C320p8tx_size - 1)
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci#define C320p16rx_size	0x2000
25062306a36Sopenharmony_ci#define C320p16tx_size	0x4000
25162306a36Sopenharmony_ci#define C320p16rx_mask	(C320p16rx_size - 1)
25262306a36Sopenharmony_ci#define C320p16tx_mask	(C320p16tx_size - 1)
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci#define C320p24rx_size	0x2000
25562306a36Sopenharmony_ci#define C320p24tx_size	0x2000
25662306a36Sopenharmony_ci#define C320p24rx_mask	(C320p24rx_size - 1)
25762306a36Sopenharmony_ci#define C320p24tx_mask	(C320p24tx_size - 1)
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci#define C320p32rx_size	0x1000
26062306a36Sopenharmony_ci#define C320p32tx_size	0x1000
26162306a36Sopenharmony_ci#define C320p32rx_mask	(C320p32rx_size - 1)
26262306a36Sopenharmony_ci#define C320p32tx_mask	(C320p32tx_size - 1)
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci#define Page_size	0x2000U
26562306a36Sopenharmony_ci#define Page_mask	(Page_size - 1)
26662306a36Sopenharmony_ci#define C218rx_spage	3
26762306a36Sopenharmony_ci#define C218tx_spage	4
26862306a36Sopenharmony_ci#define C218rx_pageno	1
26962306a36Sopenharmony_ci#define C218tx_pageno	4
27062306a36Sopenharmony_ci#define C218buf_pageno	5
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci#define C320p8rx_spage	3
27362306a36Sopenharmony_ci#define C320p8tx_spage	4
27462306a36Sopenharmony_ci#define C320p8rx_pgno	1
27562306a36Sopenharmony_ci#define C320p8tx_pgno	4
27662306a36Sopenharmony_ci#define C320p8buf_pgno	5
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci#define C320p16rx_spage 3
27962306a36Sopenharmony_ci#define C320p16tx_spage 4
28062306a36Sopenharmony_ci#define C320p16rx_pgno	1
28162306a36Sopenharmony_ci#define C320p16tx_pgno	2
28262306a36Sopenharmony_ci#define C320p16buf_pgno 3
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci#define C320p24rx_spage 3
28562306a36Sopenharmony_ci#define C320p24tx_spage 4
28662306a36Sopenharmony_ci#define C320p24rx_pgno	1
28762306a36Sopenharmony_ci#define C320p24tx_pgno	1
28862306a36Sopenharmony_ci#define C320p24buf_pgno 2
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci#define C320p32rx_spage 3
29162306a36Sopenharmony_ci#define C320p32tx_ofs	C320p32rx_size
29262306a36Sopenharmony_ci#define C320p32tx_spage 3
29362306a36Sopenharmony_ci#define C320p32buf_pgno 1
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/*
29662306a36Sopenharmony_ci *    Host Status
29762306a36Sopenharmony_ci */
29862306a36Sopenharmony_ci#define WakeupRx	0x01
29962306a36Sopenharmony_ci#define WakeupTx	0x02
30062306a36Sopenharmony_ci#define WakeupBreak	0x08
30162306a36Sopenharmony_ci#define WakeupLine	0x10
30262306a36Sopenharmony_ci#define WakeupIntr	0x20
30362306a36Sopenharmony_ci#define WakeupQuit	0x40
30462306a36Sopenharmony_ci#define WakeupEOF	0x80	/* used in VTIME control */
30562306a36Sopenharmony_ci#define WakeupRxTrigger	0x100
30662306a36Sopenharmony_ci#define WakeupTxTrigger	0x200
30762306a36Sopenharmony_ci/*
30862306a36Sopenharmony_ci *    Flag status
30962306a36Sopenharmony_ci */
31062306a36Sopenharmony_ci#define Rx_over		0x01
31162306a36Sopenharmony_ci#define Xoff_state	0x02
31262306a36Sopenharmony_ci#define Tx_flowOff	0x04
31362306a36Sopenharmony_ci#define Tx_enable	0x08
31462306a36Sopenharmony_ci#define CTS_state	0x10
31562306a36Sopenharmony_ci#define DSR_state	0x20
31662306a36Sopenharmony_ci#define DCD_state	0x80
31762306a36Sopenharmony_ci/*
31862306a36Sopenharmony_ci *    FlowControl
31962306a36Sopenharmony_ci */
32062306a36Sopenharmony_ci#define CTS_FlowCtl	1
32162306a36Sopenharmony_ci#define RTS_FlowCtl	2
32262306a36Sopenharmony_ci#define Tx_FlowCtl	4
32362306a36Sopenharmony_ci#define Rx_FlowCtl	8
32462306a36Sopenharmony_ci#define IXM_IXANY	0x10
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci#define LowWater	128
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci#define DTR_ON		1
32962306a36Sopenharmony_ci#define RTS_ON		2
33062306a36Sopenharmony_ci#define CTS_ON		1
33162306a36Sopenharmony_ci#define DSR_ON		2
33262306a36Sopenharmony_ci#define DCD_ON		8
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/* mode definition */
33562306a36Sopenharmony_ci#define	MX_CS8		0x03
33662306a36Sopenharmony_ci#define	MX_CS7		0x02
33762306a36Sopenharmony_ci#define	MX_CS6		0x01
33862306a36Sopenharmony_ci#define	MX_CS5		0x00
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci#define	MX_STOP1	0x00
34162306a36Sopenharmony_ci#define	MX_STOP15	0x04
34262306a36Sopenharmony_ci#define	MX_STOP2	0x08
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci#define	MX_PARNONE	0x00
34562306a36Sopenharmony_ci#define	MX_PAREVEN	0x40
34662306a36Sopenharmony_ci#define	MX_PARODD	0xC0
34762306a36Sopenharmony_ci#define	MX_PARMARK	0xA0
34862306a36Sopenharmony_ci#define	MX_PARSPACE	0x20
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci#define MOXA_VERSION		"6.0k"
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci#define MOXA_FW_HDRLEN		32
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci#define MOXAMAJOR		172
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci#define MAX_BOARDS		4	/* Don't change this value */
35762306a36Sopenharmony_ci#define MAX_PORTS_PER_BOARD	32	/* Don't change this value */
35862306a36Sopenharmony_ci#define MAX_PORTS		(MAX_BOARDS * MAX_PORTS_PER_BOARD)
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \
36162306a36Sopenharmony_ci		(brd)->boardType == MOXA_BOARD_C320_PCI)
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/*
36462306a36Sopenharmony_ci *    Define the Moxa PCI vendor and device IDs.
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_ci#define MOXA_BUS_TYPE_ISA	0
36762306a36Sopenharmony_ci#define MOXA_BUS_TYPE_PCI	1
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cienum {
37062306a36Sopenharmony_ci	MOXA_BOARD_C218_PCI = 1,
37162306a36Sopenharmony_ci	MOXA_BOARD_C218_ISA,
37262306a36Sopenharmony_ci	MOXA_BOARD_C320_PCI,
37362306a36Sopenharmony_ci	MOXA_BOARD_C320_ISA,
37462306a36Sopenharmony_ci	MOXA_BOARD_CP204J,
37562306a36Sopenharmony_ci};
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic char *moxa_brdname[] =
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	"C218 Turbo PCI series",
38062306a36Sopenharmony_ci	"C218 Turbo ISA series",
38162306a36Sopenharmony_ci	"C320 Turbo PCI series",
38262306a36Sopenharmony_ci	"C320 Turbo ISA series",
38362306a36Sopenharmony_ci	"CP-204J series",
38462306a36Sopenharmony_ci};
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci#ifdef CONFIG_PCI
38762306a36Sopenharmony_cistatic const struct pci_device_id moxa_pcibrds[] = {
38862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218),
38962306a36Sopenharmony_ci		.driver_data = MOXA_BOARD_C218_PCI },
39062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C320),
39162306a36Sopenharmony_ci		.driver_data = MOXA_BOARD_C320_PCI },
39262306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP204J),
39362306a36Sopenharmony_ci		.driver_data = MOXA_BOARD_CP204J },
39462306a36Sopenharmony_ci	{ 0 }
39562306a36Sopenharmony_ci};
39662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, moxa_pcibrds);
39762306a36Sopenharmony_ci#endif /* CONFIG_PCI */
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistruct moxa_port;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic struct moxa_board_conf {
40262306a36Sopenharmony_ci	int boardType;
40362306a36Sopenharmony_ci	int numPorts;
40462306a36Sopenharmony_ci	int busType;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	unsigned int ready;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	struct moxa_port *ports;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	void __iomem *basemem;
41162306a36Sopenharmony_ci	void __iomem *intNdx;
41262306a36Sopenharmony_ci	void __iomem *intPend;
41362306a36Sopenharmony_ci	void __iomem *intTable;
41462306a36Sopenharmony_ci} moxa_boards[MAX_BOARDS];
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistruct mxser_mstatus {
41762306a36Sopenharmony_ci	tcflag_t cflag;
41862306a36Sopenharmony_ci	int cts;
41962306a36Sopenharmony_ci	int dsr;
42062306a36Sopenharmony_ci	int ri;
42162306a36Sopenharmony_ci	int dcd;
42262306a36Sopenharmony_ci};
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistruct moxaq_str {
42562306a36Sopenharmony_ci	int inq;
42662306a36Sopenharmony_ci	int outq;
42762306a36Sopenharmony_ci};
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistruct moxa_port {
43062306a36Sopenharmony_ci	struct tty_port port;
43162306a36Sopenharmony_ci	struct moxa_board_conf *board;
43262306a36Sopenharmony_ci	void __iomem *tableAddr;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	int type;
43562306a36Sopenharmony_ci	int cflag;
43662306a36Sopenharmony_ci	unsigned long statusflags;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	u8 DCDState;		/* Protected by the port lock */
43962306a36Sopenharmony_ci	u8 lineCtrl;
44062306a36Sopenharmony_ci	u8 lowChkFlag;
44162306a36Sopenharmony_ci};
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistruct mon_str {
44462306a36Sopenharmony_ci	int tick;
44562306a36Sopenharmony_ci	int rxcnt[MAX_PORTS];
44662306a36Sopenharmony_ci	int txcnt[MAX_PORTS];
44762306a36Sopenharmony_ci};
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/* statusflags */
45062306a36Sopenharmony_ci#define TXSTOPPED	1
45162306a36Sopenharmony_ci#define LOWWAIT 	2
45262306a36Sopenharmony_ci#define EMPTYWAIT	3
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci#define WAKEUP_CHARS		256
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int ttymajor = MOXAMAJOR;
45862306a36Sopenharmony_cistatic struct mon_str moxaLog;
45962306a36Sopenharmony_cistatic unsigned int moxaFuncTout = HZ / 2;
46062306a36Sopenharmony_cistatic unsigned int moxaLowWaterChk;
46162306a36Sopenharmony_cistatic DEFINE_MUTEX(moxa_openlock);
46262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(moxa_lock);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic unsigned long baseaddr[MAX_BOARDS];
46562306a36Sopenharmony_cistatic unsigned int type[MAX_BOARDS];
46662306a36Sopenharmony_cistatic unsigned int numports[MAX_BOARDS];
46762306a36Sopenharmony_cistatic struct tty_port moxa_service_port;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ciMODULE_AUTHOR("William Chen");
47062306a36Sopenharmony_ciMODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
47162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
47262306a36Sopenharmony_ciMODULE_FIRMWARE("c218tunx.cod");
47362306a36Sopenharmony_ciMODULE_FIRMWARE("cp204unx.cod");
47462306a36Sopenharmony_ciMODULE_FIRMWARE("c320tunx.cod");
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cimodule_param_array(type, uint, NULL, 0);
47762306a36Sopenharmony_ciMODULE_PARM_DESC(type, "card type: C218=2, C320=4");
47862306a36Sopenharmony_cimodule_param_hw_array(baseaddr, ulong, ioport, NULL, 0);
47962306a36Sopenharmony_ciMODULE_PARM_DESC(baseaddr, "base address");
48062306a36Sopenharmony_cimodule_param_array(numports, uint, NULL, 0);
48162306a36Sopenharmony_ciMODULE_PARM_DESC(numports, "numports (ignored for C218)");
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cimodule_param(ttymajor, int, 0);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/*
48662306a36Sopenharmony_ci * static functions:
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_cistatic int moxa_open(struct tty_struct *, struct file *);
48962306a36Sopenharmony_cistatic void moxa_close(struct tty_struct *, struct file *);
49062306a36Sopenharmony_cistatic ssize_t moxa_write(struct tty_struct *, const u8 *, size_t);
49162306a36Sopenharmony_cistatic unsigned int moxa_write_room(struct tty_struct *);
49262306a36Sopenharmony_cistatic void moxa_flush_buffer(struct tty_struct *);
49362306a36Sopenharmony_cistatic unsigned int moxa_chars_in_buffer(struct tty_struct *);
49462306a36Sopenharmony_cistatic void moxa_set_termios(struct tty_struct *, const struct ktermios *);
49562306a36Sopenharmony_cistatic void moxa_stop(struct tty_struct *);
49662306a36Sopenharmony_cistatic void moxa_start(struct tty_struct *);
49762306a36Sopenharmony_cistatic void moxa_hangup(struct tty_struct *);
49862306a36Sopenharmony_cistatic int moxa_tiocmget(struct tty_struct *tty);
49962306a36Sopenharmony_cistatic int moxa_tiocmset(struct tty_struct *tty,
50062306a36Sopenharmony_ci			 unsigned int set, unsigned int clear);
50162306a36Sopenharmony_cistatic void moxa_poll(struct timer_list *);
50262306a36Sopenharmony_cistatic void moxa_set_tty_param(struct tty_struct *, const struct ktermios *);
50362306a36Sopenharmony_cistatic void moxa_shutdown(struct tty_port *);
50462306a36Sopenharmony_cistatic bool moxa_carrier_raised(struct tty_port *);
50562306a36Sopenharmony_cistatic void moxa_dtr_rts(struct tty_port *, bool);
50662306a36Sopenharmony_ci/*
50762306a36Sopenharmony_ci * moxa board interface functions:
50862306a36Sopenharmony_ci */
50962306a36Sopenharmony_cistatic void MoxaPortEnable(struct moxa_port *);
51062306a36Sopenharmony_cistatic void MoxaPortDisable(struct moxa_port *);
51162306a36Sopenharmony_cistatic int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t);
51262306a36Sopenharmony_cistatic int MoxaPortGetLineOut(struct moxa_port *, bool *, bool *);
51362306a36Sopenharmony_cistatic void MoxaPortLineCtrl(struct moxa_port *, bool, bool);
51462306a36Sopenharmony_cistatic void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int);
51562306a36Sopenharmony_cistatic int MoxaPortLineStatus(struct moxa_port *);
51662306a36Sopenharmony_cistatic void MoxaPortFlushData(struct moxa_port *, int);
51762306a36Sopenharmony_cistatic int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int);
51862306a36Sopenharmony_cistatic int MoxaPortReadData(struct moxa_port *);
51962306a36Sopenharmony_cistatic unsigned int MoxaPortTxQueue(struct moxa_port *);
52062306a36Sopenharmony_cistatic int MoxaPortRxQueue(struct moxa_port *);
52162306a36Sopenharmony_cistatic unsigned int MoxaPortTxFree(struct moxa_port *);
52262306a36Sopenharmony_cistatic void MoxaPortTxDisable(struct moxa_port *);
52362306a36Sopenharmony_cistatic void MoxaPortTxEnable(struct moxa_port *);
52462306a36Sopenharmony_cistatic int moxa_get_serial_info(struct tty_struct *, struct serial_struct *);
52562306a36Sopenharmony_cistatic int moxa_set_serial_info(struct tty_struct *, struct serial_struct *);
52662306a36Sopenharmony_cistatic void MoxaSetFifo(struct moxa_port *port, int enable);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/*
52962306a36Sopenharmony_ci * I/O functions
53062306a36Sopenharmony_ci */
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(moxafunc_lock);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic void moxa_wait_finish(void __iomem *ofsAddr)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	unsigned long end = jiffies + moxaFuncTout;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	while (readw(ofsAddr + FuncCode) != 0)
53962306a36Sopenharmony_ci		if (time_after(jiffies, end))
54062306a36Sopenharmony_ci			return;
54162306a36Sopenharmony_ci	if (readw(ofsAddr + FuncCode) != 0)
54262306a36Sopenharmony_ci		printk_ratelimited(KERN_WARNING "moxa function expired\n");
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci        unsigned long flags;
54862306a36Sopenharmony_ci        spin_lock_irqsave(&moxafunc_lock, flags);
54962306a36Sopenharmony_ci	writew(arg, ofsAddr + FuncArg);
55062306a36Sopenharmony_ci	writew(cmd, ofsAddr + FuncCode);
55162306a36Sopenharmony_ci	moxa_wait_finish(ofsAddr);
55262306a36Sopenharmony_ci	spin_unlock_irqrestore(&moxafunc_lock, flags);
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci        unsigned long flags;
55862306a36Sopenharmony_ci        u16 ret;
55962306a36Sopenharmony_ci        spin_lock_irqsave(&moxafunc_lock, flags);
56062306a36Sopenharmony_ci	writew(arg, ofsAddr + FuncArg);
56162306a36Sopenharmony_ci	writew(cmd, ofsAddr + FuncCode);
56262306a36Sopenharmony_ci	moxa_wait_finish(ofsAddr);
56362306a36Sopenharmony_ci	ret = readw(ofsAddr + FuncArg);
56462306a36Sopenharmony_ci	spin_unlock_irqrestore(&moxafunc_lock, flags);
56562306a36Sopenharmony_ci	return ret;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic void moxa_low_water_check(void __iomem *ofsAddr)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	u16 rptr, wptr, mask, len;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (readb(ofsAddr + FlagStat) & Xoff_state) {
57362306a36Sopenharmony_ci		rptr = readw(ofsAddr + RXrptr);
57462306a36Sopenharmony_ci		wptr = readw(ofsAddr + RXwptr);
57562306a36Sopenharmony_ci		mask = readw(ofsAddr + RX_mask);
57662306a36Sopenharmony_ci		len = (wptr - rptr) & mask;
57762306a36Sopenharmony_ci		if (len <= Low_water)
57862306a36Sopenharmony_ci			moxafunc(ofsAddr, FC_SendXon, 0);
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci/*
58362306a36Sopenharmony_ci * TTY operations
58462306a36Sopenharmony_ci */
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int moxa_ioctl(struct tty_struct *tty,
58762306a36Sopenharmony_ci		      unsigned int cmd, unsigned long arg)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
59062306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
59162306a36Sopenharmony_ci	int status, ret = 0;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (tty->index == MAX_PORTS) {
59462306a36Sopenharmony_ci		if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE &&
59562306a36Sopenharmony_ci				cmd != MOXA_GETMSTATUS)
59662306a36Sopenharmony_ci			return -EINVAL;
59762306a36Sopenharmony_ci	} else if (!ch)
59862306a36Sopenharmony_ci		return -ENODEV;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	switch (cmd) {
60162306a36Sopenharmony_ci	case MOXA_GETDATACOUNT:
60262306a36Sopenharmony_ci		moxaLog.tick = jiffies;
60362306a36Sopenharmony_ci		if (copy_to_user(argp, &moxaLog, sizeof(moxaLog)))
60462306a36Sopenharmony_ci			ret = -EFAULT;
60562306a36Sopenharmony_ci		break;
60662306a36Sopenharmony_ci	case MOXA_FLUSH_QUEUE:
60762306a36Sopenharmony_ci		MoxaPortFlushData(ch, arg);
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci	case MOXA_GET_IOQUEUE: {
61062306a36Sopenharmony_ci		struct moxaq_str __user *argm = argp;
61162306a36Sopenharmony_ci		struct moxaq_str tmp;
61262306a36Sopenharmony_ci		struct moxa_port *p;
61362306a36Sopenharmony_ci		unsigned int i, j;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		for (i = 0; i < MAX_BOARDS; i++) {
61662306a36Sopenharmony_ci			p = moxa_boards[i].ports;
61762306a36Sopenharmony_ci			for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
61862306a36Sopenharmony_ci				memset(&tmp, 0, sizeof(tmp));
61962306a36Sopenharmony_ci				spin_lock_bh(&moxa_lock);
62062306a36Sopenharmony_ci				if (moxa_boards[i].ready) {
62162306a36Sopenharmony_ci					tmp.inq = MoxaPortRxQueue(p);
62262306a36Sopenharmony_ci					tmp.outq = MoxaPortTxQueue(p);
62362306a36Sopenharmony_ci				}
62462306a36Sopenharmony_ci				spin_unlock_bh(&moxa_lock);
62562306a36Sopenharmony_ci				if (copy_to_user(argm, &tmp, sizeof(tmp)))
62662306a36Sopenharmony_ci					return -EFAULT;
62762306a36Sopenharmony_ci			}
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci		break;
63062306a36Sopenharmony_ci	} case MOXA_GET_OQUEUE:
63162306a36Sopenharmony_ci		status = MoxaPortTxQueue(ch);
63262306a36Sopenharmony_ci		ret = put_user(status, (unsigned long __user *)argp);
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	case MOXA_GET_IQUEUE:
63562306a36Sopenharmony_ci		status = MoxaPortRxQueue(ch);
63662306a36Sopenharmony_ci		ret = put_user(status, (unsigned long __user *)argp);
63762306a36Sopenharmony_ci		break;
63862306a36Sopenharmony_ci	case MOXA_GETMSTATUS: {
63962306a36Sopenharmony_ci		struct mxser_mstatus __user *argm = argp;
64062306a36Sopenharmony_ci		struct mxser_mstatus tmp;
64162306a36Sopenharmony_ci		struct moxa_port *p;
64262306a36Sopenharmony_ci		unsigned int i, j;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		for (i = 0; i < MAX_BOARDS; i++) {
64562306a36Sopenharmony_ci			p = moxa_boards[i].ports;
64662306a36Sopenharmony_ci			for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
64762306a36Sopenharmony_ci				struct tty_struct *ttyp;
64862306a36Sopenharmony_ci				memset(&tmp, 0, sizeof(tmp));
64962306a36Sopenharmony_ci				spin_lock_bh(&moxa_lock);
65062306a36Sopenharmony_ci				if (!moxa_boards[i].ready) {
65162306a36Sopenharmony_ci				        spin_unlock_bh(&moxa_lock);
65262306a36Sopenharmony_ci					goto copy;
65362306a36Sopenharmony_ci                                }
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci				status = MoxaPortLineStatus(p);
65662306a36Sopenharmony_ci				spin_unlock_bh(&moxa_lock);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci				if (status & 1)
65962306a36Sopenharmony_ci					tmp.cts = 1;
66062306a36Sopenharmony_ci				if (status & 2)
66162306a36Sopenharmony_ci					tmp.dsr = 1;
66262306a36Sopenharmony_ci				if (status & 4)
66362306a36Sopenharmony_ci					tmp.dcd = 1;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci				ttyp = tty_port_tty_get(&p->port);
66662306a36Sopenharmony_ci				if (!ttyp)
66762306a36Sopenharmony_ci					tmp.cflag = p->cflag;
66862306a36Sopenharmony_ci				else
66962306a36Sopenharmony_ci					tmp.cflag = ttyp->termios.c_cflag;
67062306a36Sopenharmony_ci				tty_kref_put(ttyp);
67162306a36Sopenharmony_cicopy:
67262306a36Sopenharmony_ci				if (copy_to_user(argm, &tmp, sizeof(tmp)))
67362306a36Sopenharmony_ci					return -EFAULT;
67462306a36Sopenharmony_ci			}
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci		break;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci	default:
67962306a36Sopenharmony_ci		ret = -ENOIOCTLCMD;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci	return ret;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic int moxa_break_ctl(struct tty_struct *tty, int state)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct moxa_port *port = tty->driver_data;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak,
68962306a36Sopenharmony_ci			Magic_code);
69062306a36Sopenharmony_ci	return 0;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic const struct tty_operations moxa_ops = {
69462306a36Sopenharmony_ci	.open = moxa_open,
69562306a36Sopenharmony_ci	.close = moxa_close,
69662306a36Sopenharmony_ci	.write = moxa_write,
69762306a36Sopenharmony_ci	.write_room = moxa_write_room,
69862306a36Sopenharmony_ci	.flush_buffer = moxa_flush_buffer,
69962306a36Sopenharmony_ci	.chars_in_buffer = moxa_chars_in_buffer,
70062306a36Sopenharmony_ci	.ioctl = moxa_ioctl,
70162306a36Sopenharmony_ci	.set_termios = moxa_set_termios,
70262306a36Sopenharmony_ci	.stop = moxa_stop,
70362306a36Sopenharmony_ci	.start = moxa_start,
70462306a36Sopenharmony_ci	.hangup = moxa_hangup,
70562306a36Sopenharmony_ci	.break_ctl = moxa_break_ctl,
70662306a36Sopenharmony_ci	.tiocmget = moxa_tiocmget,
70762306a36Sopenharmony_ci	.tiocmset = moxa_tiocmset,
70862306a36Sopenharmony_ci	.set_serial = moxa_set_serial_info,
70962306a36Sopenharmony_ci	.get_serial = moxa_get_serial_info,
71062306a36Sopenharmony_ci};
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic const struct tty_port_operations moxa_port_ops = {
71362306a36Sopenharmony_ci	.carrier_raised = moxa_carrier_raised,
71462306a36Sopenharmony_ci	.dtr_rts = moxa_dtr_rts,
71562306a36Sopenharmony_ci	.shutdown = moxa_shutdown,
71662306a36Sopenharmony_ci};
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic struct tty_driver *moxaDriver;
71962306a36Sopenharmony_cistatic DEFINE_TIMER(moxaTimer, moxa_poll);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci/*
72262306a36Sopenharmony_ci * HW init
72362306a36Sopenharmony_ci */
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	switch (brd->boardType) {
72862306a36Sopenharmony_ci	case MOXA_BOARD_C218_ISA:
72962306a36Sopenharmony_ci	case MOXA_BOARD_C218_PCI:
73062306a36Sopenharmony_ci		if (model != 1)
73162306a36Sopenharmony_ci			goto err;
73262306a36Sopenharmony_ci		break;
73362306a36Sopenharmony_ci	case MOXA_BOARD_CP204J:
73462306a36Sopenharmony_ci		if (model != 3)
73562306a36Sopenharmony_ci			goto err;
73662306a36Sopenharmony_ci		break;
73762306a36Sopenharmony_ci	default:
73862306a36Sopenharmony_ci		if (model != 2)
73962306a36Sopenharmony_ci			goto err;
74062306a36Sopenharmony_ci		break;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_cierr:
74462306a36Sopenharmony_ci	return -EINVAL;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic int moxa_check_fw(const void *ptr)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	const __le16 *lptr = ptr;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (*lptr != cpu_to_le16(0x7980))
75262306a36Sopenharmony_ci		return -EINVAL;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf,
75862306a36Sopenharmony_ci		size_t len)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	void __iomem *baseAddr = brd->basemem;
76162306a36Sopenharmony_ci	u16 tmp;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	writeb(HW_reset, baseAddr + Control_reg);	/* reset */
76462306a36Sopenharmony_ci	msleep(10);
76562306a36Sopenharmony_ci	memset_io(baseAddr, 0, 4096);
76662306a36Sopenharmony_ci	memcpy_toio(baseAddr, buf, len);	/* download BIOS */
76762306a36Sopenharmony_ci	writeb(0, baseAddr + Control_reg);	/* restart */
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	msleep(2000);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	switch (brd->boardType) {
77262306a36Sopenharmony_ci	case MOXA_BOARD_C218_ISA:
77362306a36Sopenharmony_ci	case MOXA_BOARD_C218_PCI:
77462306a36Sopenharmony_ci		tmp = readw(baseAddr + C218_key);
77562306a36Sopenharmony_ci		if (tmp != C218_KeyCode)
77662306a36Sopenharmony_ci			goto err;
77762306a36Sopenharmony_ci		break;
77862306a36Sopenharmony_ci	case MOXA_BOARD_CP204J:
77962306a36Sopenharmony_ci		tmp = readw(baseAddr + C218_key);
78062306a36Sopenharmony_ci		if (tmp != CP204J_KeyCode)
78162306a36Sopenharmony_ci			goto err;
78262306a36Sopenharmony_ci		break;
78362306a36Sopenharmony_ci	default:
78462306a36Sopenharmony_ci		tmp = readw(baseAddr + C320_key);
78562306a36Sopenharmony_ci		if (tmp != C320_KeyCode)
78662306a36Sopenharmony_ci			goto err;
78762306a36Sopenharmony_ci		tmp = readw(baseAddr + C320_status);
78862306a36Sopenharmony_ci		if (tmp != STS_init) {
78962306a36Sopenharmony_ci			printk(KERN_ERR "MOXA: bios upload failed -- CPU/Basic "
79062306a36Sopenharmony_ci					"module not found\n");
79162306a36Sopenharmony_ci			return -EIO;
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci		break;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	return 0;
79762306a36Sopenharmony_cierr:
79862306a36Sopenharmony_ci	printk(KERN_ERR "MOXA: bios upload failed -- board not found\n");
79962306a36Sopenharmony_ci	return -EIO;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int moxa_load_320b(struct moxa_board_conf *brd, const u8 *ptr,
80362306a36Sopenharmony_ci		size_t len)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	void __iomem *baseAddr = brd->basemem;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (len < 7168) {
80862306a36Sopenharmony_ci		printk(KERN_ERR "MOXA: invalid 320 bios -- too short\n");
80962306a36Sopenharmony_ci		return -EINVAL;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	writew(len - 7168 - 2, baseAddr + C320bapi_len);
81362306a36Sopenharmony_ci	writeb(1, baseAddr + Control_reg);	/* Select Page 1 */
81462306a36Sopenharmony_ci	memcpy_toio(baseAddr + DynPage_addr, ptr, 7168);
81562306a36Sopenharmony_ci	writeb(2, baseAddr + Control_reg);	/* Select Page 2 */
81662306a36Sopenharmony_ci	memcpy_toio(baseAddr + DynPage_addr, ptr + 7168, len - 7168);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	return 0;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr,
82262306a36Sopenharmony_ci		size_t len)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	void __iomem *baseAddr = brd->basemem;
82562306a36Sopenharmony_ci	const __le16 *uptr = ptr;
82662306a36Sopenharmony_ci	size_t wlen, len2, j;
82762306a36Sopenharmony_ci	unsigned long key, loadbuf, loadlen, checksum, checksum_ok;
82862306a36Sopenharmony_ci	unsigned int i, retry;
82962306a36Sopenharmony_ci	u16 usum, keycode;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	keycode = (brd->boardType == MOXA_BOARD_CP204J) ? CP204J_KeyCode :
83262306a36Sopenharmony_ci				C218_KeyCode;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	switch (brd->boardType) {
83562306a36Sopenharmony_ci	case MOXA_BOARD_CP204J:
83662306a36Sopenharmony_ci	case MOXA_BOARD_C218_ISA:
83762306a36Sopenharmony_ci	case MOXA_BOARD_C218_PCI:
83862306a36Sopenharmony_ci		key = C218_key;
83962306a36Sopenharmony_ci		loadbuf = C218_LoadBuf;
84062306a36Sopenharmony_ci		loadlen = C218DLoad_len;
84162306a36Sopenharmony_ci		checksum = C218check_sum;
84262306a36Sopenharmony_ci		checksum_ok = C218chksum_ok;
84362306a36Sopenharmony_ci		break;
84462306a36Sopenharmony_ci	default:
84562306a36Sopenharmony_ci		key = C320_key;
84662306a36Sopenharmony_ci		keycode = C320_KeyCode;
84762306a36Sopenharmony_ci		loadbuf = C320_LoadBuf;
84862306a36Sopenharmony_ci		loadlen = C320DLoad_len;
84962306a36Sopenharmony_ci		checksum = C320check_sum;
85062306a36Sopenharmony_ci		checksum_ok = C320chksum_ok;
85162306a36Sopenharmony_ci		break;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	usum = 0;
85562306a36Sopenharmony_ci	wlen = len >> 1;
85662306a36Sopenharmony_ci	for (i = 0; i < wlen; i++)
85762306a36Sopenharmony_ci		usum += le16_to_cpu(uptr[i]);
85862306a36Sopenharmony_ci	retry = 0;
85962306a36Sopenharmony_ci	do {
86062306a36Sopenharmony_ci		wlen = len >> 1;
86162306a36Sopenharmony_ci		j = 0;
86262306a36Sopenharmony_ci		while (wlen) {
86362306a36Sopenharmony_ci			len2 = (wlen > 2048) ? 2048 : wlen;
86462306a36Sopenharmony_ci			wlen -= len2;
86562306a36Sopenharmony_ci			memcpy_toio(baseAddr + loadbuf, ptr + j, len2 << 1);
86662306a36Sopenharmony_ci			j += len2 << 1;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci			writew(len2, baseAddr + loadlen);
86962306a36Sopenharmony_ci			writew(0, baseAddr + key);
87062306a36Sopenharmony_ci			for (i = 0; i < 100; i++) {
87162306a36Sopenharmony_ci				if (readw(baseAddr + key) == keycode)
87262306a36Sopenharmony_ci					break;
87362306a36Sopenharmony_ci				msleep(10);
87462306a36Sopenharmony_ci			}
87562306a36Sopenharmony_ci			if (readw(baseAddr + key) != keycode)
87662306a36Sopenharmony_ci				return -EIO;
87762306a36Sopenharmony_ci		}
87862306a36Sopenharmony_ci		writew(0, baseAddr + loadlen);
87962306a36Sopenharmony_ci		writew(usum, baseAddr + checksum);
88062306a36Sopenharmony_ci		writew(0, baseAddr + key);
88162306a36Sopenharmony_ci		for (i = 0; i < 100; i++) {
88262306a36Sopenharmony_ci			if (readw(baseAddr + key) == keycode)
88362306a36Sopenharmony_ci				break;
88462306a36Sopenharmony_ci			msleep(10);
88562306a36Sopenharmony_ci		}
88662306a36Sopenharmony_ci		retry++;
88762306a36Sopenharmony_ci	} while ((readb(baseAddr + checksum_ok) != 1) && (retry < 3));
88862306a36Sopenharmony_ci	if (readb(baseAddr + checksum_ok) != 1)
88962306a36Sopenharmony_ci		return -EIO;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	writew(0, baseAddr + key);
89262306a36Sopenharmony_ci	for (i = 0; i < 600; i++) {
89362306a36Sopenharmony_ci		if (readw(baseAddr + Magic_no) == Magic_code)
89462306a36Sopenharmony_ci			break;
89562306a36Sopenharmony_ci		msleep(10);
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci	if (readw(baseAddr + Magic_no) != Magic_code)
89862306a36Sopenharmony_ci		return -EIO;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (MOXA_IS_320(brd)) {
90162306a36Sopenharmony_ci		if (brd->busType == MOXA_BUS_TYPE_PCI) {	/* ASIC board */
90262306a36Sopenharmony_ci			writew(0x3800, baseAddr + TMS320_PORT1);
90362306a36Sopenharmony_ci			writew(0x3900, baseAddr + TMS320_PORT2);
90462306a36Sopenharmony_ci			writew(28499, baseAddr + TMS320_CLOCK);
90562306a36Sopenharmony_ci		} else {
90662306a36Sopenharmony_ci			writew(0x3200, baseAddr + TMS320_PORT1);
90762306a36Sopenharmony_ci			writew(0x3400, baseAddr + TMS320_PORT2);
90862306a36Sopenharmony_ci			writew(19999, baseAddr + TMS320_CLOCK);
90962306a36Sopenharmony_ci		}
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci	writew(1, baseAddr + Disable_IRQ);
91262306a36Sopenharmony_ci	writew(0, baseAddr + Magic_no);
91362306a36Sopenharmony_ci	for (i = 0; i < 500; i++) {
91462306a36Sopenharmony_ci		if (readw(baseAddr + Magic_no) == Magic_code)
91562306a36Sopenharmony_ci			break;
91662306a36Sopenharmony_ci		msleep(10);
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci	if (readw(baseAddr + Magic_no) != Magic_code)
91962306a36Sopenharmony_ci		return -EIO;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (MOXA_IS_320(brd)) {
92262306a36Sopenharmony_ci		j = readw(baseAddr + Module_cnt);
92362306a36Sopenharmony_ci		if (j <= 0)
92462306a36Sopenharmony_ci			return -EIO;
92562306a36Sopenharmony_ci		brd->numPorts = j * 8;
92662306a36Sopenharmony_ci		writew(j, baseAddr + Module_no);
92762306a36Sopenharmony_ci		writew(0, baseAddr + Magic_no);
92862306a36Sopenharmony_ci		for (i = 0; i < 600; i++) {
92962306a36Sopenharmony_ci			if (readw(baseAddr + Magic_no) == Magic_code)
93062306a36Sopenharmony_ci				break;
93162306a36Sopenharmony_ci			msleep(10);
93262306a36Sopenharmony_ci		}
93362306a36Sopenharmony_ci		if (readw(baseAddr + Magic_no) != Magic_code)
93462306a36Sopenharmony_ci			return -EIO;
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci	brd->intNdx = baseAddr + IRQindex;
93762306a36Sopenharmony_ci	brd->intPend = baseAddr + IRQpending;
93862306a36Sopenharmony_ci	brd->intTable = baseAddr + IRQtable;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	return 0;
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic int moxa_load_code(struct moxa_board_conf *brd, const void *ptr,
94462306a36Sopenharmony_ci		size_t len)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	void __iomem *ofsAddr, *baseAddr = brd->basemem;
94762306a36Sopenharmony_ci	struct moxa_port *port;
94862306a36Sopenharmony_ci	int retval, i;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (len % 2) {
95162306a36Sopenharmony_ci		printk(KERN_ERR "MOXA: bios length is not even\n");
95262306a36Sopenharmony_ci		return -EINVAL;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	retval = moxa_real_load_code(brd, ptr, len); /* may change numPorts */
95662306a36Sopenharmony_ci	if (retval)
95762306a36Sopenharmony_ci		return retval;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	switch (brd->boardType) {
96062306a36Sopenharmony_ci	case MOXA_BOARD_C218_ISA:
96162306a36Sopenharmony_ci	case MOXA_BOARD_C218_PCI:
96262306a36Sopenharmony_ci	case MOXA_BOARD_CP204J:
96362306a36Sopenharmony_ci		port = brd->ports;
96462306a36Sopenharmony_ci		for (i = 0; i < brd->numPorts; i++, port++) {
96562306a36Sopenharmony_ci			port->board = brd;
96662306a36Sopenharmony_ci			port->DCDState = 0;
96762306a36Sopenharmony_ci			port->tableAddr = baseAddr + Extern_table +
96862306a36Sopenharmony_ci					Extern_size * i;
96962306a36Sopenharmony_ci			ofsAddr = port->tableAddr;
97062306a36Sopenharmony_ci			writew(C218rx_mask, ofsAddr + RX_mask);
97162306a36Sopenharmony_ci			writew(C218tx_mask, ofsAddr + TX_mask);
97262306a36Sopenharmony_ci			writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb);
97362306a36Sopenharmony_ci			writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci			writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb);
97662306a36Sopenharmony_ci			writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci		break;
98062306a36Sopenharmony_ci	default:
98162306a36Sopenharmony_ci		port = brd->ports;
98262306a36Sopenharmony_ci		for (i = 0; i < brd->numPorts; i++, port++) {
98362306a36Sopenharmony_ci			port->board = brd;
98462306a36Sopenharmony_ci			port->DCDState = 0;
98562306a36Sopenharmony_ci			port->tableAddr = baseAddr + Extern_table +
98662306a36Sopenharmony_ci					Extern_size * i;
98762306a36Sopenharmony_ci			ofsAddr = port->tableAddr;
98862306a36Sopenharmony_ci			switch (brd->numPorts) {
98962306a36Sopenharmony_ci			case 8:
99062306a36Sopenharmony_ci				writew(C320p8rx_mask, ofsAddr + RX_mask);
99162306a36Sopenharmony_ci				writew(C320p8tx_mask, ofsAddr + TX_mask);
99262306a36Sopenharmony_ci				writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb);
99362306a36Sopenharmony_ci				writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb);
99462306a36Sopenharmony_ci				writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb);
99562306a36Sopenharmony_ci				writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci				break;
99862306a36Sopenharmony_ci			case 16:
99962306a36Sopenharmony_ci				writew(C320p16rx_mask, ofsAddr + RX_mask);
100062306a36Sopenharmony_ci				writew(C320p16tx_mask, ofsAddr + TX_mask);
100162306a36Sopenharmony_ci				writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb);
100262306a36Sopenharmony_ci				writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb);
100362306a36Sopenharmony_ci				writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb);
100462306a36Sopenharmony_ci				writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb);
100562306a36Sopenharmony_ci				break;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci			case 24:
100862306a36Sopenharmony_ci				writew(C320p24rx_mask, ofsAddr + RX_mask);
100962306a36Sopenharmony_ci				writew(C320p24tx_mask, ofsAddr + TX_mask);
101062306a36Sopenharmony_ci				writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb);
101162306a36Sopenharmony_ci				writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb);
101262306a36Sopenharmony_ci				writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb);
101362306a36Sopenharmony_ci				writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
101462306a36Sopenharmony_ci				break;
101562306a36Sopenharmony_ci			case 32:
101662306a36Sopenharmony_ci				writew(C320p32rx_mask, ofsAddr + RX_mask);
101762306a36Sopenharmony_ci				writew(C320p32tx_mask, ofsAddr + TX_mask);
101862306a36Sopenharmony_ci				writew(C320p32tx_ofs, ofsAddr + Ofs_txb);
101962306a36Sopenharmony_ci				writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb);
102062306a36Sopenharmony_ci				writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb);
102162306a36Sopenharmony_ci				writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb);
102262306a36Sopenharmony_ci				writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
102362306a36Sopenharmony_ci				break;
102462306a36Sopenharmony_ci			}
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci		break;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci	return 0;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic int moxa_load_fw(struct moxa_board_conf *brd, const struct firmware *fw)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	const void *ptr = fw->data;
103462306a36Sopenharmony_ci	char rsn[64];
103562306a36Sopenharmony_ci	u16 lens[5];
103662306a36Sopenharmony_ci	size_t len;
103762306a36Sopenharmony_ci	unsigned int a, lenp, lencnt;
103862306a36Sopenharmony_ci	int ret = -EINVAL;
103962306a36Sopenharmony_ci	struct {
104062306a36Sopenharmony_ci		__le32 magic;	/* 0x34303430 */
104162306a36Sopenharmony_ci		u8 reserved1[2];
104262306a36Sopenharmony_ci		u8 type;	/* UNIX = 3 */
104362306a36Sopenharmony_ci		u8 model;	/* C218T=1, C320T=2, CP204=3 */
104462306a36Sopenharmony_ci		u8 reserved2[8];
104562306a36Sopenharmony_ci		__le16 len[5];
104662306a36Sopenharmony_ci	} const *hdr = ptr;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(hdr->len) != ARRAY_SIZE(lens));
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (fw->size < MOXA_FW_HDRLEN) {
105162306a36Sopenharmony_ci		strcpy(rsn, "too short (even header won't fit)");
105262306a36Sopenharmony_ci		goto err;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci	if (hdr->magic != cpu_to_le32(0x30343034)) {
105562306a36Sopenharmony_ci		sprintf(rsn, "bad magic: %.8x", le32_to_cpu(hdr->magic));
105662306a36Sopenharmony_ci		goto err;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci	if (hdr->type != 3) {
105962306a36Sopenharmony_ci		sprintf(rsn, "not for linux, type is %u", hdr->type);
106062306a36Sopenharmony_ci		goto err;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci	if (moxa_check_fw_model(brd, hdr->model)) {
106362306a36Sopenharmony_ci		sprintf(rsn, "not for this card, model is %u", hdr->model);
106462306a36Sopenharmony_ci		goto err;
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	len = MOXA_FW_HDRLEN;
106862306a36Sopenharmony_ci	lencnt = hdr->model == 2 ? 5 : 3;
106962306a36Sopenharmony_ci	for (a = 0; a < ARRAY_SIZE(lens); a++) {
107062306a36Sopenharmony_ci		lens[a] = le16_to_cpu(hdr->len[a]);
107162306a36Sopenharmony_ci		if (lens[a] && len + lens[a] <= fw->size &&
107262306a36Sopenharmony_ci				moxa_check_fw(&fw->data[len]))
107362306a36Sopenharmony_ci			printk(KERN_WARNING "MOXA firmware: unexpected input "
107462306a36Sopenharmony_ci				"at offset %u, but going on\n", (u32)len);
107562306a36Sopenharmony_ci		if (!lens[a] && a < lencnt) {
107662306a36Sopenharmony_ci			sprintf(rsn, "too few entries in fw file");
107762306a36Sopenharmony_ci			goto err;
107862306a36Sopenharmony_ci		}
107962306a36Sopenharmony_ci		len += lens[a];
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	if (len != fw->size) {
108362306a36Sopenharmony_ci		sprintf(rsn, "bad length: %u (should be %u)", (u32)fw->size,
108462306a36Sopenharmony_ci				(u32)len);
108562306a36Sopenharmony_ci		goto err;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	ptr += MOXA_FW_HDRLEN;
108962306a36Sopenharmony_ci	lenp = 0; /* bios */
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	strcpy(rsn, "read above");
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	ret = moxa_load_bios(brd, ptr, lens[lenp]);
109462306a36Sopenharmony_ci	if (ret)
109562306a36Sopenharmony_ci		goto err;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* we skip the tty section (lens[1]), since we don't need it */
109862306a36Sopenharmony_ci	ptr += lens[lenp] + lens[lenp + 1];
109962306a36Sopenharmony_ci	lenp += 2; /* comm */
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (hdr->model == 2) {
110262306a36Sopenharmony_ci		ret = moxa_load_320b(brd, ptr, lens[lenp]);
110362306a36Sopenharmony_ci		if (ret)
110462306a36Sopenharmony_ci			goto err;
110562306a36Sopenharmony_ci		/* skip another tty */
110662306a36Sopenharmony_ci		ptr += lens[lenp] + lens[lenp + 1];
110762306a36Sopenharmony_ci		lenp += 2;
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	ret = moxa_load_code(brd, ptr, lens[lenp]);
111162306a36Sopenharmony_ci	if (ret)
111262306a36Sopenharmony_ci		goto err;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_cierr:
111662306a36Sopenharmony_ci	printk(KERN_ERR "firmware failed to load, reason: %s\n", rsn);
111762306a36Sopenharmony_ci	return ret;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic int moxa_init_board(struct moxa_board_conf *brd, struct device *dev)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	const struct firmware *fw;
112362306a36Sopenharmony_ci	const char *file;
112462306a36Sopenharmony_ci	struct moxa_port *p;
112562306a36Sopenharmony_ci	unsigned int i, first_idx;
112662306a36Sopenharmony_ci	int ret;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports),
112962306a36Sopenharmony_ci			GFP_KERNEL);
113062306a36Sopenharmony_ci	if (brd->ports == NULL) {
113162306a36Sopenharmony_ci		printk(KERN_ERR "cannot allocate memory for ports\n");
113262306a36Sopenharmony_ci		ret = -ENOMEM;
113362306a36Sopenharmony_ci		goto err;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) {
113762306a36Sopenharmony_ci		tty_port_init(&p->port);
113862306a36Sopenharmony_ci		p->port.ops = &moxa_port_ops;
113962306a36Sopenharmony_ci		p->type = PORT_16550A;
114062306a36Sopenharmony_ci		p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	switch (brd->boardType) {
114462306a36Sopenharmony_ci	case MOXA_BOARD_C218_ISA:
114562306a36Sopenharmony_ci	case MOXA_BOARD_C218_PCI:
114662306a36Sopenharmony_ci		file = "c218tunx.cod";
114762306a36Sopenharmony_ci		break;
114862306a36Sopenharmony_ci	case MOXA_BOARD_CP204J:
114962306a36Sopenharmony_ci		file = "cp204unx.cod";
115062306a36Sopenharmony_ci		break;
115162306a36Sopenharmony_ci	default:
115262306a36Sopenharmony_ci		file = "c320tunx.cod";
115362306a36Sopenharmony_ci		break;
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	ret = request_firmware(&fw, file, dev);
115762306a36Sopenharmony_ci	if (ret) {
115862306a36Sopenharmony_ci		printk(KERN_ERR "MOXA: request_firmware failed. Make sure "
115962306a36Sopenharmony_ci				"you've placed '%s' file into your firmware "
116062306a36Sopenharmony_ci				"loader directory (e.g. /lib/firmware)\n",
116162306a36Sopenharmony_ci				file);
116262306a36Sopenharmony_ci		goto err_free;
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	ret = moxa_load_fw(brd, fw);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	release_firmware(fw);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (ret)
117062306a36Sopenharmony_ci		goto err_free;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	spin_lock_bh(&moxa_lock);
117362306a36Sopenharmony_ci	brd->ready = 1;
117462306a36Sopenharmony_ci	if (!timer_pending(&moxaTimer))
117562306a36Sopenharmony_ci		mod_timer(&moxaTimer, jiffies + HZ / 50);
117662306a36Sopenharmony_ci	spin_unlock_bh(&moxa_lock);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD;
117962306a36Sopenharmony_ci	for (i = 0; i < brd->numPorts; i++)
118062306a36Sopenharmony_ci		tty_port_register_device(&brd->ports[i].port, moxaDriver,
118162306a36Sopenharmony_ci				first_idx + i, dev);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	return 0;
118462306a36Sopenharmony_cierr_free:
118562306a36Sopenharmony_ci	for (i = 0; i < MAX_PORTS_PER_BOARD; i++)
118662306a36Sopenharmony_ci		tty_port_destroy(&brd->ports[i].port);
118762306a36Sopenharmony_ci	kfree(brd->ports);
118862306a36Sopenharmony_cierr:
118962306a36Sopenharmony_ci	return ret;
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_cistatic void moxa_board_deinit(struct moxa_board_conf *brd)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	unsigned int a, opened, first_idx;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	mutex_lock(&moxa_openlock);
119762306a36Sopenharmony_ci	spin_lock_bh(&moxa_lock);
119862306a36Sopenharmony_ci	brd->ready = 0;
119962306a36Sopenharmony_ci	spin_unlock_bh(&moxa_lock);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/* pci hot-un-plug support */
120262306a36Sopenharmony_ci	for (a = 0; a < brd->numPorts; a++)
120362306a36Sopenharmony_ci		if (tty_port_initialized(&brd->ports[a].port))
120462306a36Sopenharmony_ci			tty_port_tty_hangup(&brd->ports[a].port, false);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	for (a = 0; a < MAX_PORTS_PER_BOARD; a++)
120762306a36Sopenharmony_ci		tty_port_destroy(&brd->ports[a].port);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	while (1) {
121062306a36Sopenharmony_ci		opened = 0;
121162306a36Sopenharmony_ci		for (a = 0; a < brd->numPorts; a++)
121262306a36Sopenharmony_ci			if (tty_port_initialized(&brd->ports[a].port))
121362306a36Sopenharmony_ci				opened++;
121462306a36Sopenharmony_ci		mutex_unlock(&moxa_openlock);
121562306a36Sopenharmony_ci		if (!opened)
121662306a36Sopenharmony_ci			break;
121762306a36Sopenharmony_ci		msleep(50);
121862306a36Sopenharmony_ci		mutex_lock(&moxa_openlock);
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD;
122262306a36Sopenharmony_ci	for (a = 0; a < brd->numPorts; a++)
122362306a36Sopenharmony_ci		tty_unregister_device(moxaDriver, first_idx + a);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	iounmap(brd->basemem);
122662306a36Sopenharmony_ci	brd->basemem = NULL;
122762306a36Sopenharmony_ci	kfree(brd->ports);
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci#ifdef CONFIG_PCI
123162306a36Sopenharmony_cistatic int moxa_pci_probe(struct pci_dev *pdev,
123262306a36Sopenharmony_ci		const struct pci_device_id *ent)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	struct moxa_board_conf *board;
123562306a36Sopenharmony_ci	unsigned int i;
123662306a36Sopenharmony_ci	int board_type = ent->driver_data;
123762306a36Sopenharmony_ci	int retval;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	retval = pci_enable_device(pdev);
124062306a36Sopenharmony_ci	if (retval) {
124162306a36Sopenharmony_ci		dev_err(&pdev->dev, "can't enable pci device\n");
124262306a36Sopenharmony_ci		goto err;
124362306a36Sopenharmony_ci	}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	for (i = 0; i < MAX_BOARDS; i++)
124662306a36Sopenharmony_ci		if (moxa_boards[i].basemem == NULL)
124762306a36Sopenharmony_ci			break;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	retval = -ENODEV;
125062306a36Sopenharmony_ci	if (i >= MAX_BOARDS) {
125162306a36Sopenharmony_ci		dev_warn(&pdev->dev, "more than %u MOXA Intellio family boards "
125262306a36Sopenharmony_ci				"found. Board is ignored.\n", MAX_BOARDS);
125362306a36Sopenharmony_ci		goto err;
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	board = &moxa_boards[i];
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	retval = pci_request_region(pdev, 2, "moxa-base");
125962306a36Sopenharmony_ci	if (retval) {
126062306a36Sopenharmony_ci		dev_err(&pdev->dev, "can't request pci region 2\n");
126162306a36Sopenharmony_ci		goto err;
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	board->basemem = ioremap(pci_resource_start(pdev, 2), 0x4000);
126562306a36Sopenharmony_ci	if (board->basemem == NULL) {
126662306a36Sopenharmony_ci		dev_err(&pdev->dev, "can't remap io space 2\n");
126762306a36Sopenharmony_ci		retval = -ENOMEM;
126862306a36Sopenharmony_ci		goto err_reg;
126962306a36Sopenharmony_ci	}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	board->boardType = board_type;
127262306a36Sopenharmony_ci	switch (board_type) {
127362306a36Sopenharmony_ci	case MOXA_BOARD_C218_ISA:
127462306a36Sopenharmony_ci	case MOXA_BOARD_C218_PCI:
127562306a36Sopenharmony_ci		board->numPorts = 8;
127662306a36Sopenharmony_ci		break;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	case MOXA_BOARD_CP204J:
127962306a36Sopenharmony_ci		board->numPorts = 4;
128062306a36Sopenharmony_ci		break;
128162306a36Sopenharmony_ci	default:
128262306a36Sopenharmony_ci		board->numPorts = 0;
128362306a36Sopenharmony_ci		break;
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci	board->busType = MOXA_BUS_TYPE_PCI;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	retval = moxa_init_board(board, &pdev->dev);
128862306a36Sopenharmony_ci	if (retval)
128962306a36Sopenharmony_ci		goto err_base;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	pci_set_drvdata(pdev, board);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	dev_info(&pdev->dev, "board '%s' ready (%u ports, firmware loaded)\n",
129462306a36Sopenharmony_ci			moxa_brdname[board_type - 1], board->numPorts);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	return 0;
129762306a36Sopenharmony_cierr_base:
129862306a36Sopenharmony_ci	iounmap(board->basemem);
129962306a36Sopenharmony_ci	board->basemem = NULL;
130062306a36Sopenharmony_cierr_reg:
130162306a36Sopenharmony_ci	pci_release_region(pdev, 2);
130262306a36Sopenharmony_cierr:
130362306a36Sopenharmony_ci	return retval;
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_cistatic void moxa_pci_remove(struct pci_dev *pdev)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	struct moxa_board_conf *brd = pci_get_drvdata(pdev);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	moxa_board_deinit(brd);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	pci_release_region(pdev, 2);
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_cistatic struct pci_driver moxa_pci_driver = {
131662306a36Sopenharmony_ci	.name = "moxa",
131762306a36Sopenharmony_ci	.id_table = moxa_pcibrds,
131862306a36Sopenharmony_ci	.probe = moxa_pci_probe,
131962306a36Sopenharmony_ci	.remove = moxa_pci_remove
132062306a36Sopenharmony_ci};
132162306a36Sopenharmony_ci#endif /* CONFIG_PCI */
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic int __init moxa_init(void)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	unsigned int isabrds = 0;
132662306a36Sopenharmony_ci	int retval = 0;
132762306a36Sopenharmony_ci	struct moxa_board_conf *brd = moxa_boards;
132862306a36Sopenharmony_ci	unsigned int i;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	printk(KERN_INFO "MOXA Intellio family driver version %s\n",
133162306a36Sopenharmony_ci			MOXA_VERSION);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	tty_port_init(&moxa_service_port);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	moxaDriver = tty_alloc_driver(MAX_PORTS + 1,
133662306a36Sopenharmony_ci			TTY_DRIVER_REAL_RAW |
133762306a36Sopenharmony_ci			TTY_DRIVER_DYNAMIC_DEV);
133862306a36Sopenharmony_ci	if (IS_ERR(moxaDriver))
133962306a36Sopenharmony_ci		return PTR_ERR(moxaDriver);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	moxaDriver->name = "ttyMX";
134262306a36Sopenharmony_ci	moxaDriver->major = ttymajor;
134362306a36Sopenharmony_ci	moxaDriver->minor_start = 0;
134462306a36Sopenharmony_ci	moxaDriver->type = TTY_DRIVER_TYPE_SERIAL;
134562306a36Sopenharmony_ci	moxaDriver->subtype = SERIAL_TYPE_NORMAL;
134662306a36Sopenharmony_ci	moxaDriver->init_termios = tty_std_termios;
134762306a36Sopenharmony_ci	moxaDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
134862306a36Sopenharmony_ci	moxaDriver->init_termios.c_ispeed = 9600;
134962306a36Sopenharmony_ci	moxaDriver->init_termios.c_ospeed = 9600;
135062306a36Sopenharmony_ci	tty_set_operations(moxaDriver, &moxa_ops);
135162306a36Sopenharmony_ci	/* Having one more port only for ioctls is ugly */
135262306a36Sopenharmony_ci	tty_port_link_device(&moxa_service_port, moxaDriver, MAX_PORTS);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	if (tty_register_driver(moxaDriver)) {
135562306a36Sopenharmony_ci		printk(KERN_ERR "can't register MOXA Smartio tty driver!\n");
135662306a36Sopenharmony_ci		tty_driver_kref_put(moxaDriver);
135762306a36Sopenharmony_ci		return -1;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	/* Find the boards defined from module args. */
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	for (i = 0; i < MAX_BOARDS; i++) {
136362306a36Sopenharmony_ci		if (!baseaddr[i])
136462306a36Sopenharmony_ci			break;
136562306a36Sopenharmony_ci		if (type[i] == MOXA_BOARD_C218_ISA ||
136662306a36Sopenharmony_ci				type[i] == MOXA_BOARD_C320_ISA) {
136762306a36Sopenharmony_ci			pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n",
136862306a36Sopenharmony_ci					isabrds + 1, moxa_brdname[type[i] - 1],
136962306a36Sopenharmony_ci					baseaddr[i]);
137062306a36Sopenharmony_ci			brd->boardType = type[i];
137162306a36Sopenharmony_ci			brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 :
137262306a36Sopenharmony_ci					numports[i];
137362306a36Sopenharmony_ci			brd->busType = MOXA_BUS_TYPE_ISA;
137462306a36Sopenharmony_ci			brd->basemem = ioremap(baseaddr[i], 0x4000);
137562306a36Sopenharmony_ci			if (!brd->basemem) {
137662306a36Sopenharmony_ci				printk(KERN_ERR "MOXA: can't remap %lx\n",
137762306a36Sopenharmony_ci						baseaddr[i]);
137862306a36Sopenharmony_ci				continue;
137962306a36Sopenharmony_ci			}
138062306a36Sopenharmony_ci			if (moxa_init_board(brd, NULL)) {
138162306a36Sopenharmony_ci				iounmap(brd->basemem);
138262306a36Sopenharmony_ci				brd->basemem = NULL;
138362306a36Sopenharmony_ci				continue;
138462306a36Sopenharmony_ci			}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci			printk(KERN_INFO "MOXA isa board found at 0x%.8lx and "
138762306a36Sopenharmony_ci					"ready (%u ports, firmware loaded)\n",
138862306a36Sopenharmony_ci					baseaddr[i], brd->numPorts);
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci			brd++;
139162306a36Sopenharmony_ci			isabrds++;
139262306a36Sopenharmony_ci		}
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci#ifdef CONFIG_PCI
139662306a36Sopenharmony_ci	retval = pci_register_driver(&moxa_pci_driver);
139762306a36Sopenharmony_ci	if (retval) {
139862306a36Sopenharmony_ci		printk(KERN_ERR "Can't register MOXA pci driver!\n");
139962306a36Sopenharmony_ci		if (isabrds)
140062306a36Sopenharmony_ci			retval = 0;
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci#endif
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	return retval;
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_cistatic void __exit moxa_exit(void)
140862306a36Sopenharmony_ci{
140962306a36Sopenharmony_ci	unsigned int i;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci#ifdef CONFIG_PCI
141262306a36Sopenharmony_ci	pci_unregister_driver(&moxa_pci_driver);
141362306a36Sopenharmony_ci#endif
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */
141662306a36Sopenharmony_ci		if (moxa_boards[i].ready)
141762306a36Sopenharmony_ci			moxa_board_deinit(&moxa_boards[i]);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	del_timer_sync(&moxaTimer);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	tty_unregister_driver(moxaDriver);
142262306a36Sopenharmony_ci	tty_driver_kref_put(moxaDriver);
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cimodule_init(moxa_init);
142662306a36Sopenharmony_cimodule_exit(moxa_exit);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_cistatic void moxa_shutdown(struct tty_port *port)
142962306a36Sopenharmony_ci{
143062306a36Sopenharmony_ci	struct moxa_port *ch = container_of(port, struct moxa_port, port);
143162306a36Sopenharmony_ci        MoxaPortDisable(ch);
143262306a36Sopenharmony_ci	MoxaPortFlushData(ch, 2);
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic bool moxa_carrier_raised(struct tty_port *port)
143662306a36Sopenharmony_ci{
143762306a36Sopenharmony_ci	struct moxa_port *ch = container_of(port, struct moxa_port, port);
143862306a36Sopenharmony_ci	int dcd;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	spin_lock_irq(&port->lock);
144162306a36Sopenharmony_ci	dcd = ch->DCDState;
144262306a36Sopenharmony_ci	spin_unlock_irq(&port->lock);
144362306a36Sopenharmony_ci	return dcd;
144462306a36Sopenharmony_ci}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_cistatic void moxa_dtr_rts(struct tty_port *port, bool active)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	struct moxa_port *ch = container_of(port, struct moxa_port, port);
144962306a36Sopenharmony_ci	MoxaPortLineCtrl(ch, active, active);
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic int moxa_open(struct tty_struct *tty, struct file *filp)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct moxa_board_conf *brd;
145662306a36Sopenharmony_ci	struct moxa_port *ch;
145762306a36Sopenharmony_ci	int port;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	port = tty->index;
146062306a36Sopenharmony_ci	if (port == MAX_PORTS) {
146162306a36Sopenharmony_ci		return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci	if (mutex_lock_interruptible(&moxa_openlock))
146462306a36Sopenharmony_ci		return -ERESTARTSYS;
146562306a36Sopenharmony_ci	brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
146662306a36Sopenharmony_ci	if (!brd->ready) {
146762306a36Sopenharmony_ci		mutex_unlock(&moxa_openlock);
146862306a36Sopenharmony_ci		return -ENODEV;
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) {
147262306a36Sopenharmony_ci		mutex_unlock(&moxa_openlock);
147362306a36Sopenharmony_ci		return -ENODEV;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
147762306a36Sopenharmony_ci	ch->port.count++;
147862306a36Sopenharmony_ci	tty->driver_data = ch;
147962306a36Sopenharmony_ci	tty_port_tty_set(&ch->port, tty);
148062306a36Sopenharmony_ci	mutex_lock(&ch->port.mutex);
148162306a36Sopenharmony_ci	if (!tty_port_initialized(&ch->port)) {
148262306a36Sopenharmony_ci		ch->statusflags = 0;
148362306a36Sopenharmony_ci		moxa_set_tty_param(tty, &tty->termios);
148462306a36Sopenharmony_ci		MoxaPortLineCtrl(ch, true, true);
148562306a36Sopenharmony_ci		MoxaPortEnable(ch);
148662306a36Sopenharmony_ci		MoxaSetFifo(ch, ch->type == PORT_16550A);
148762306a36Sopenharmony_ci		tty_port_set_initialized(&ch->port, true);
148862306a36Sopenharmony_ci	}
148962306a36Sopenharmony_ci	mutex_unlock(&ch->port.mutex);
149062306a36Sopenharmony_ci	mutex_unlock(&moxa_openlock);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	return tty_port_block_til_ready(&ch->port, tty, filp);
149362306a36Sopenharmony_ci}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_cistatic void moxa_close(struct tty_struct *tty, struct file *filp)
149662306a36Sopenharmony_ci{
149762306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
149862306a36Sopenharmony_ci	ch->cflag = tty->termios.c_cflag;
149962306a36Sopenharmony_ci	tty_port_close(&ch->port, tty, filp);
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_cistatic ssize_t moxa_write(struct tty_struct *tty, const u8 *buf, size_t count)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
150562306a36Sopenharmony_ci	unsigned long flags;
150662306a36Sopenharmony_ci	int len;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	if (ch == NULL)
150962306a36Sopenharmony_ci		return 0;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	spin_lock_irqsave(&moxa_lock, flags);
151262306a36Sopenharmony_ci	len = MoxaPortWriteData(tty, buf, count);
151362306a36Sopenharmony_ci	spin_unlock_irqrestore(&moxa_lock, flags);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	set_bit(LOWWAIT, &ch->statusflags);
151662306a36Sopenharmony_ci	return len;
151762306a36Sopenharmony_ci}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_cistatic unsigned int moxa_write_room(struct tty_struct *tty)
152062306a36Sopenharmony_ci{
152162306a36Sopenharmony_ci	struct moxa_port *ch;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (tty->flow.stopped)
152462306a36Sopenharmony_ci		return 0;
152562306a36Sopenharmony_ci	ch = tty->driver_data;
152662306a36Sopenharmony_ci	if (ch == NULL)
152762306a36Sopenharmony_ci		return 0;
152862306a36Sopenharmony_ci	return MoxaPortTxFree(ch);
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cistatic void moxa_flush_buffer(struct tty_struct *tty)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	if (ch == NULL)
153662306a36Sopenharmony_ci		return;
153762306a36Sopenharmony_ci	MoxaPortFlushData(ch, 1);
153862306a36Sopenharmony_ci	tty_wakeup(tty);
153962306a36Sopenharmony_ci}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_cistatic unsigned int moxa_chars_in_buffer(struct tty_struct *tty)
154262306a36Sopenharmony_ci{
154362306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
154462306a36Sopenharmony_ci	unsigned int chars;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	chars = MoxaPortTxQueue(ch);
154762306a36Sopenharmony_ci	if (chars)
154862306a36Sopenharmony_ci		/*
154962306a36Sopenharmony_ci		 * Make it possible to wakeup anything waiting for output
155062306a36Sopenharmony_ci		 * in tty_ioctl.c, etc.
155162306a36Sopenharmony_ci		 */
155262306a36Sopenharmony_ci        	set_bit(EMPTYWAIT, &ch->statusflags);
155362306a36Sopenharmony_ci	return chars;
155462306a36Sopenharmony_ci}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_cistatic int moxa_tiocmget(struct tty_struct *tty)
155762306a36Sopenharmony_ci{
155862306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
155962306a36Sopenharmony_ci	bool dtr_active, rts_active;
156062306a36Sopenharmony_ci	int flag = 0;
156162306a36Sopenharmony_ci	int status;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	MoxaPortGetLineOut(ch, &dtr_active, &rts_active);
156462306a36Sopenharmony_ci	if (dtr_active)
156562306a36Sopenharmony_ci		flag |= TIOCM_DTR;
156662306a36Sopenharmony_ci	if (rts_active)
156762306a36Sopenharmony_ci		flag |= TIOCM_RTS;
156862306a36Sopenharmony_ci	status = MoxaPortLineStatus(ch);
156962306a36Sopenharmony_ci	if (status & 1)
157062306a36Sopenharmony_ci		flag |= TIOCM_CTS;
157162306a36Sopenharmony_ci	if (status & 2)
157262306a36Sopenharmony_ci		flag |= TIOCM_DSR;
157362306a36Sopenharmony_ci	if (status & 4)
157462306a36Sopenharmony_ci		flag |= TIOCM_CD;
157562306a36Sopenharmony_ci	return flag;
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistatic int moxa_tiocmset(struct tty_struct *tty,
157962306a36Sopenharmony_ci			 unsigned int set, unsigned int clear)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	bool dtr_active, rts_active;
158262306a36Sopenharmony_ci	struct moxa_port *ch;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	mutex_lock(&moxa_openlock);
158562306a36Sopenharmony_ci	ch = tty->driver_data;
158662306a36Sopenharmony_ci	if (!ch) {
158762306a36Sopenharmony_ci		mutex_unlock(&moxa_openlock);
158862306a36Sopenharmony_ci		return -EINVAL;
158962306a36Sopenharmony_ci	}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	MoxaPortGetLineOut(ch, &dtr_active, &rts_active);
159262306a36Sopenharmony_ci	if (set & TIOCM_RTS)
159362306a36Sopenharmony_ci		rts_active = true;
159462306a36Sopenharmony_ci	if (set & TIOCM_DTR)
159562306a36Sopenharmony_ci		dtr_active = true;
159662306a36Sopenharmony_ci	if (clear & TIOCM_RTS)
159762306a36Sopenharmony_ci		rts_active = false;
159862306a36Sopenharmony_ci	if (clear & TIOCM_DTR)
159962306a36Sopenharmony_ci		dtr_active = false;
160062306a36Sopenharmony_ci	MoxaPortLineCtrl(ch, dtr_active, rts_active);
160162306a36Sopenharmony_ci	mutex_unlock(&moxa_openlock);
160262306a36Sopenharmony_ci	return 0;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic void moxa_set_termios(struct tty_struct *tty,
160662306a36Sopenharmony_ci		             const struct ktermios *old_termios)
160762306a36Sopenharmony_ci{
160862306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	if (ch == NULL)
161162306a36Sopenharmony_ci		return;
161262306a36Sopenharmony_ci	moxa_set_tty_param(tty, old_termios);
161362306a36Sopenharmony_ci	if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty))
161462306a36Sopenharmony_ci		wake_up_interruptible(&ch->port.open_wait);
161562306a36Sopenharmony_ci}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_cistatic void moxa_stop(struct tty_struct *tty)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	if (ch == NULL)
162262306a36Sopenharmony_ci		return;
162362306a36Sopenharmony_ci	MoxaPortTxDisable(ch);
162462306a36Sopenharmony_ci	set_bit(TXSTOPPED, &ch->statusflags);
162562306a36Sopenharmony_ci}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_cistatic void moxa_start(struct tty_struct *tty)
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	if (ch == NULL)
163362306a36Sopenharmony_ci		return;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	if (!test_bit(TXSTOPPED, &ch->statusflags))
163662306a36Sopenharmony_ci		return;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	MoxaPortTxEnable(ch);
163962306a36Sopenharmony_ci	clear_bit(TXSTOPPED, &ch->statusflags);
164062306a36Sopenharmony_ci}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_cistatic void moxa_hangup(struct tty_struct *tty)
164362306a36Sopenharmony_ci{
164462306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
164562306a36Sopenharmony_ci	tty_port_hangup(&ch->port);
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
164962306a36Sopenharmony_ci{
165062306a36Sopenharmony_ci	unsigned long flags;
165162306a36Sopenharmony_ci	dcd = !!dcd;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	spin_lock_irqsave(&p->port.lock, flags);
165462306a36Sopenharmony_ci	if (dcd != p->DCDState) {
165562306a36Sopenharmony_ci        	p->DCDState = dcd;
165662306a36Sopenharmony_ci        	spin_unlock_irqrestore(&p->port.lock, flags);
165762306a36Sopenharmony_ci		if (!dcd)
165862306a36Sopenharmony_ci			tty_port_tty_hangup(&p->port, true);
165962306a36Sopenharmony_ci	}
166062306a36Sopenharmony_ci	else
166162306a36Sopenharmony_ci		spin_unlock_irqrestore(&p->port.lock, flags);
166262306a36Sopenharmony_ci}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_cistatic int moxa_poll_port(struct moxa_port *p, unsigned int handle,
166562306a36Sopenharmony_ci		u16 __iomem *ip)
166662306a36Sopenharmony_ci{
166762306a36Sopenharmony_ci	struct tty_struct *tty = tty_port_tty_get(&p->port);
166862306a36Sopenharmony_ci	bool inited = tty_port_initialized(&p->port);
166962306a36Sopenharmony_ci	void __iomem *ofsAddr;
167062306a36Sopenharmony_ci	u16 intr;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	if (tty) {
167362306a36Sopenharmony_ci		if (test_bit(EMPTYWAIT, &p->statusflags) &&
167462306a36Sopenharmony_ci				MoxaPortTxQueue(p) == 0) {
167562306a36Sopenharmony_ci			clear_bit(EMPTYWAIT, &p->statusflags);
167662306a36Sopenharmony_ci			tty_wakeup(tty);
167762306a36Sopenharmony_ci		}
167862306a36Sopenharmony_ci		if (test_bit(LOWWAIT, &p->statusflags) && !tty->flow.stopped &&
167962306a36Sopenharmony_ci				MoxaPortTxQueue(p) <= WAKEUP_CHARS) {
168062306a36Sopenharmony_ci			clear_bit(LOWWAIT, &p->statusflags);
168162306a36Sopenharmony_ci			tty_wakeup(tty);
168262306a36Sopenharmony_ci		}
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci		if (inited && !tty_throttled(tty) &&
168562306a36Sopenharmony_ci				MoxaPortRxQueue(p) > 0) { /* RX */
168662306a36Sopenharmony_ci			MoxaPortReadData(p);
168762306a36Sopenharmony_ci			tty_flip_buffer_push(&p->port);
168862306a36Sopenharmony_ci		}
168962306a36Sopenharmony_ci	} else {
169062306a36Sopenharmony_ci		clear_bit(EMPTYWAIT, &p->statusflags);
169162306a36Sopenharmony_ci		MoxaPortFlushData(p, 0); /* flush RX */
169262306a36Sopenharmony_ci	}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	if (!handle) /* nothing else to do */
169562306a36Sopenharmony_ci		goto put;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	intr = readw(ip); /* port irq status */
169862306a36Sopenharmony_ci	if (intr == 0)
169962306a36Sopenharmony_ci		goto put;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	writew(0, ip); /* ACK port */
170262306a36Sopenharmony_ci	ofsAddr = p->tableAddr;
170362306a36Sopenharmony_ci	if (intr & IntrTx) /* disable tx intr */
170462306a36Sopenharmony_ci		writew(readw(ofsAddr + HostStat) & ~WakeupTx,
170562306a36Sopenharmony_ci				ofsAddr + HostStat);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	if (!inited)
170862306a36Sopenharmony_ci		goto put;
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */
171162306a36Sopenharmony_ci		tty_insert_flip_char(&p->port, 0, TTY_BREAK);
171262306a36Sopenharmony_ci		tty_flip_buffer_push(&p->port);
171362306a36Sopenharmony_ci	}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	if (intr & IntrLine)
171662306a36Sopenharmony_ci		moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state);
171762306a36Sopenharmony_ciput:
171862306a36Sopenharmony_ci	tty_kref_put(tty);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	return 0;
172162306a36Sopenharmony_ci}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_cistatic void moxa_poll(struct timer_list *unused)
172462306a36Sopenharmony_ci{
172562306a36Sopenharmony_ci	struct moxa_board_conf *brd;
172662306a36Sopenharmony_ci	u16 __iomem *ip;
172762306a36Sopenharmony_ci	unsigned int card, port, served = 0;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	spin_lock(&moxa_lock);
173062306a36Sopenharmony_ci	for (card = 0; card < MAX_BOARDS; card++) {
173162306a36Sopenharmony_ci		brd = &moxa_boards[card];
173262306a36Sopenharmony_ci		if (!brd->ready)
173362306a36Sopenharmony_ci			continue;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci		served++;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci		ip = NULL;
173862306a36Sopenharmony_ci		if (readb(brd->intPend) == 0xff)
173962306a36Sopenharmony_ci			ip = brd->intTable + readb(brd->intNdx);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci		for (port = 0; port < brd->numPorts; port++)
174262306a36Sopenharmony_ci			moxa_poll_port(&brd->ports[port], !!ip, ip + port);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci		if (ip)
174562306a36Sopenharmony_ci			writeb(0, brd->intPend); /* ACK */
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci		if (moxaLowWaterChk) {
174862306a36Sopenharmony_ci			struct moxa_port *p = brd->ports;
174962306a36Sopenharmony_ci			for (port = 0; port < brd->numPorts; port++, p++)
175062306a36Sopenharmony_ci				if (p->lowChkFlag) {
175162306a36Sopenharmony_ci					p->lowChkFlag = 0;
175262306a36Sopenharmony_ci					moxa_low_water_check(p->tableAddr);
175362306a36Sopenharmony_ci				}
175462306a36Sopenharmony_ci		}
175562306a36Sopenharmony_ci	}
175662306a36Sopenharmony_ci	moxaLowWaterChk = 0;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (served)
175962306a36Sopenharmony_ci		mod_timer(&moxaTimer, jiffies + HZ / 50);
176062306a36Sopenharmony_ci	spin_unlock(&moxa_lock);
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci/******************************************************************************/
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_cistatic void moxa_set_tty_param(struct tty_struct *tty,
176662306a36Sopenharmony_ci			       const struct ktermios *old_termios)
176762306a36Sopenharmony_ci{
176862306a36Sopenharmony_ci	register struct ktermios *ts = &tty->termios;
176962306a36Sopenharmony_ci	struct moxa_port *ch = tty->driver_data;
177062306a36Sopenharmony_ci	int rts, cts, txflow, rxflow, xany, baud;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	rts = cts = txflow = rxflow = xany = 0;
177362306a36Sopenharmony_ci	if (ts->c_cflag & CRTSCTS)
177462306a36Sopenharmony_ci		rts = cts = 1;
177562306a36Sopenharmony_ci	if (ts->c_iflag & IXON)
177662306a36Sopenharmony_ci		txflow = 1;
177762306a36Sopenharmony_ci	if (ts->c_iflag & IXOFF)
177862306a36Sopenharmony_ci		rxflow = 1;
177962306a36Sopenharmony_ci	if (ts->c_iflag & IXANY)
178062306a36Sopenharmony_ci		xany = 1;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	MoxaPortFlowCtrl(ch, rts, cts, txflow, rxflow, xany);
178362306a36Sopenharmony_ci	baud = MoxaPortSetTermio(ch, ts, tty_get_baud_rate(tty));
178462306a36Sopenharmony_ci	if (baud == -1)
178562306a36Sopenharmony_ci		baud = tty_termios_baud_rate(old_termios);
178662306a36Sopenharmony_ci	/* Not put the baud rate into the termios data */
178762306a36Sopenharmony_ci	tty_encode_baud_rate(tty, baud, baud);
178862306a36Sopenharmony_ci}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci/*****************************************************************************
179162306a36Sopenharmony_ci *	Driver level functions: 					     *
179262306a36Sopenharmony_ci *****************************************************************************/
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic void MoxaPortFlushData(struct moxa_port *port, int mode)
179562306a36Sopenharmony_ci{
179662306a36Sopenharmony_ci	void __iomem *ofsAddr;
179762306a36Sopenharmony_ci	if (mode < 0 || mode > 2)
179862306a36Sopenharmony_ci		return;
179962306a36Sopenharmony_ci	ofsAddr = port->tableAddr;
180062306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_FlushQueue, mode);
180162306a36Sopenharmony_ci	if (mode != 1) {
180262306a36Sopenharmony_ci		port->lowChkFlag = 0;
180362306a36Sopenharmony_ci		moxa_low_water_check(ofsAddr);
180462306a36Sopenharmony_ci	}
180562306a36Sopenharmony_ci}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci/*
180862306a36Sopenharmony_ci *    Moxa Port Number Description:
180962306a36Sopenharmony_ci *
181062306a36Sopenharmony_ci *      MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And,
181162306a36Sopenharmony_ci *      the port number using in MOXA driver functions will be 0 to 31 for
181262306a36Sopenharmony_ci *      first MOXA board, 32 to 63 for second, 64 to 95 for third and 96
181362306a36Sopenharmony_ci *      to 127 for fourth. For example, if you setup three MOXA boards,
181462306a36Sopenharmony_ci *      first board is C218, second board is C320-16 and third board is
181562306a36Sopenharmony_ci *      C320-32. The port number of first board (C218 - 8 ports) is from
181662306a36Sopenharmony_ci *      0 to 7. The port number of second board (C320 - 16 ports) is form
181762306a36Sopenharmony_ci *      32 to 47. The port number of third board (C320 - 32 ports) is from
181862306a36Sopenharmony_ci *      64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to
181962306a36Sopenharmony_ci *      127 will be invalid.
182062306a36Sopenharmony_ci *
182162306a36Sopenharmony_ci *
182262306a36Sopenharmony_ci *      Moxa Functions Description:
182362306a36Sopenharmony_ci *
182462306a36Sopenharmony_ci *      Function 1:     Driver initialization routine, this routine must be
182562306a36Sopenharmony_ci *                      called when initialized driver.
182662306a36Sopenharmony_ci *      Syntax:
182762306a36Sopenharmony_ci *      void MoxaDriverInit();
182862306a36Sopenharmony_ci *
182962306a36Sopenharmony_ci *
183062306a36Sopenharmony_ci *      Function 2:     Moxa driver private IOCTL command processing.
183162306a36Sopenharmony_ci *      Syntax:
183262306a36Sopenharmony_ci *      int  MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port);
183362306a36Sopenharmony_ci *
183462306a36Sopenharmony_ci *           unsigned int cmd   : IOCTL command
183562306a36Sopenharmony_ci *           unsigned long arg  : IOCTL argument
183662306a36Sopenharmony_ci *           int port           : port number (0 - 127)
183762306a36Sopenharmony_ci *
183862306a36Sopenharmony_ci *           return:    0  (OK)
183962306a36Sopenharmony_ci *                      -EINVAL
184062306a36Sopenharmony_ci *                      -ENOIOCTLCMD
184162306a36Sopenharmony_ci *
184262306a36Sopenharmony_ci *
184362306a36Sopenharmony_ci *      Function 6:     Enable this port to start Tx/Rx data.
184462306a36Sopenharmony_ci *      Syntax:
184562306a36Sopenharmony_ci *      void MoxaPortEnable(int port);
184662306a36Sopenharmony_ci *           int port           : port number (0 - 127)
184762306a36Sopenharmony_ci *
184862306a36Sopenharmony_ci *
184962306a36Sopenharmony_ci *      Function 7:     Disable this port
185062306a36Sopenharmony_ci *      Syntax:
185162306a36Sopenharmony_ci *      void MoxaPortDisable(int port);
185262306a36Sopenharmony_ci *           int port           : port number (0 - 127)
185362306a36Sopenharmony_ci *
185462306a36Sopenharmony_ci *
185562306a36Sopenharmony_ci *      Function 10:    Setting baud rate of this port.
185662306a36Sopenharmony_ci *      Syntax:
185762306a36Sopenharmony_ci *      speed_t MoxaPortSetBaud(int port, speed_t baud);
185862306a36Sopenharmony_ci *           int port           : port number (0 - 127)
185962306a36Sopenharmony_ci *           long baud          : baud rate (50 - 115200)
186062306a36Sopenharmony_ci *
186162306a36Sopenharmony_ci *           return:    0       : this port is invalid or baud < 50
186262306a36Sopenharmony_ci *                      50 - 115200 : the real baud rate set to the port, if
186362306a36Sopenharmony_ci *                                    the argument baud is large than maximun
186462306a36Sopenharmony_ci *                                    available baud rate, the real setting
186562306a36Sopenharmony_ci *                                    baud rate will be the maximun baud rate.
186662306a36Sopenharmony_ci *
186762306a36Sopenharmony_ci *
186862306a36Sopenharmony_ci *      Function 12:    Configure the port.
186962306a36Sopenharmony_ci *      Syntax:
187062306a36Sopenharmony_ci *      int  MoxaPortSetTermio(int port, struct ktermios *termio, speed_t baud);
187162306a36Sopenharmony_ci *           int port           : port number (0 - 127)
187262306a36Sopenharmony_ci *           struct ktermios * termio : termio structure pointer
187362306a36Sopenharmony_ci *	     speed_t baud	: baud rate
187462306a36Sopenharmony_ci *
187562306a36Sopenharmony_ci *           return:    -1      : this port is invalid or termio == NULL
187662306a36Sopenharmony_ci *                      0       : setting O.K.
187762306a36Sopenharmony_ci *
187862306a36Sopenharmony_ci *
187962306a36Sopenharmony_ci *      Function 13:    Get the DTR/RTS state of this port.
188062306a36Sopenharmony_ci *      Syntax:
188162306a36Sopenharmony_ci *      int  MoxaPortGetLineOut(int port, bool *dtrState, bool *rtsState);
188262306a36Sopenharmony_ci *           int port           : port number (0 - 127)
188362306a36Sopenharmony_ci *           bool * dtr_active  : pointer to bool to receive the current DTR
188462306a36Sopenharmony_ci *                                state. (if NULL, this function will not
188562306a36Sopenharmony_ci *                                write to this address)
188662306a36Sopenharmony_ci *           bool * rts_active  : pointer to bool to receive the current RTS
188762306a36Sopenharmony_ci *                                state. (if NULL, this function will not
188862306a36Sopenharmony_ci *                                write to this address)
188962306a36Sopenharmony_ci *
189062306a36Sopenharmony_ci *           return:    -1      : this port is invalid
189162306a36Sopenharmony_ci *                      0       : O.K.
189262306a36Sopenharmony_ci *
189362306a36Sopenharmony_ci *
189462306a36Sopenharmony_ci *      Function 14:    Setting the DTR/RTS output state of this port.
189562306a36Sopenharmony_ci *      Syntax:
189662306a36Sopenharmony_ci *      void MoxaPortLineCtrl(int port, bool dtrState, bool rtsState);
189762306a36Sopenharmony_ci *           int port           : port number (0 - 127)
189862306a36Sopenharmony_ci *           bool dtr_active    : DTR output state
189962306a36Sopenharmony_ci *           bool rts_active    : RTS output state
190062306a36Sopenharmony_ci *
190162306a36Sopenharmony_ci *
190262306a36Sopenharmony_ci *      Function 15:    Setting the flow control of this port.
190362306a36Sopenharmony_ci *      Syntax:
190462306a36Sopenharmony_ci *      void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow,
190562306a36Sopenharmony_ci *                            int txFlow,int xany);
190662306a36Sopenharmony_ci *           int port           : port number (0 - 127)
190762306a36Sopenharmony_ci *           int rtsFlow        : H/W RTS flow control (0: no, 1: yes)
190862306a36Sopenharmony_ci *           int ctsFlow        : H/W CTS flow control (0: no, 1: yes)
190962306a36Sopenharmony_ci *           int rxFlow         : S/W Rx XON/XOFF flow control (0: no, 1: yes)
191062306a36Sopenharmony_ci *           int txFlow         : S/W Tx XON/XOFF flow control (0: no, 1: yes)
191162306a36Sopenharmony_ci *           int xany           : S/W XANY flow control (0: no, 1: yes)
191262306a36Sopenharmony_ci *
191362306a36Sopenharmony_ci *
191462306a36Sopenharmony_ci *      Function 16:    Get ths line status of this port
191562306a36Sopenharmony_ci *      Syntax:
191662306a36Sopenharmony_ci *      int  MoxaPortLineStatus(int port);
191762306a36Sopenharmony_ci *           int port           : port number (0 - 127)
191862306a36Sopenharmony_ci *
191962306a36Sopenharmony_ci *           return:    Bit 0 - CTS state (0: off, 1: on)
192062306a36Sopenharmony_ci *                      Bit 1 - DSR state (0: off, 1: on)
192162306a36Sopenharmony_ci *                      Bit 2 - DCD state (0: off, 1: on)
192262306a36Sopenharmony_ci *
192362306a36Sopenharmony_ci *
192462306a36Sopenharmony_ci *      Function 19:    Flush the Rx/Tx buffer data of this port.
192562306a36Sopenharmony_ci *      Syntax:
192662306a36Sopenharmony_ci *      void MoxaPortFlushData(int port, int mode);
192762306a36Sopenharmony_ci *           int port           : port number (0 - 127)
192862306a36Sopenharmony_ci *           int mode
192962306a36Sopenharmony_ci *                      0       : flush the Rx buffer
193062306a36Sopenharmony_ci *                      1       : flush the Tx buffer
193162306a36Sopenharmony_ci *                      2       : flush the Rx and Tx buffer
193262306a36Sopenharmony_ci *
193362306a36Sopenharmony_ci *
193462306a36Sopenharmony_ci *      Function 20:    Write data.
193562306a36Sopenharmony_ci *      Syntax:
193662306a36Sopenharmony_ci *      int  MoxaPortWriteData(int port, unsigned char * buffer, int length);
193762306a36Sopenharmony_ci *           int port           : port number (0 - 127)
193862306a36Sopenharmony_ci *           unsigned char * buffer     : pointer to write data buffer.
193962306a36Sopenharmony_ci *           int length         : write data length
194062306a36Sopenharmony_ci *
194162306a36Sopenharmony_ci *           return:    0 - length      : real write data length
194262306a36Sopenharmony_ci *
194362306a36Sopenharmony_ci *
194462306a36Sopenharmony_ci *      Function 21:    Read data.
194562306a36Sopenharmony_ci *      Syntax:
194662306a36Sopenharmony_ci *      int  MoxaPortReadData(int port, struct tty_struct *tty);
194762306a36Sopenharmony_ci *           int port           : port number (0 - 127)
194862306a36Sopenharmony_ci *	     struct tty_struct *tty : tty for data
194962306a36Sopenharmony_ci *
195062306a36Sopenharmony_ci *           return:    0 - length      : real read data length
195162306a36Sopenharmony_ci *
195262306a36Sopenharmony_ci *
195362306a36Sopenharmony_ci *      Function 24:    Get the Tx buffer current queued data bytes
195462306a36Sopenharmony_ci *      Syntax:
195562306a36Sopenharmony_ci *      int  MoxaPortTxQueue(int port);
195662306a36Sopenharmony_ci *           int port           : port number (0 - 127)
195762306a36Sopenharmony_ci *
195862306a36Sopenharmony_ci *           return:    ..      : Tx buffer current queued data bytes
195962306a36Sopenharmony_ci *
196062306a36Sopenharmony_ci *
196162306a36Sopenharmony_ci *      Function 25:    Get the Tx buffer current free space
196262306a36Sopenharmony_ci *      Syntax:
196362306a36Sopenharmony_ci *      int  MoxaPortTxFree(int port);
196462306a36Sopenharmony_ci *           int port           : port number (0 - 127)
196562306a36Sopenharmony_ci *
196662306a36Sopenharmony_ci *           return:    ..      : Tx buffer current free space
196762306a36Sopenharmony_ci *
196862306a36Sopenharmony_ci *
196962306a36Sopenharmony_ci *      Function 26:    Get the Rx buffer current queued data bytes
197062306a36Sopenharmony_ci *      Syntax:
197162306a36Sopenharmony_ci *      int  MoxaPortRxQueue(int port);
197262306a36Sopenharmony_ci *           int port           : port number (0 - 127)
197362306a36Sopenharmony_ci *
197462306a36Sopenharmony_ci *           return:    ..      : Rx buffer current queued data bytes
197562306a36Sopenharmony_ci *
197662306a36Sopenharmony_ci *
197762306a36Sopenharmony_ci *      Function 28:    Disable port data transmission.
197862306a36Sopenharmony_ci *      Syntax:
197962306a36Sopenharmony_ci *      void MoxaPortTxDisable(int port);
198062306a36Sopenharmony_ci *           int port           : port number (0 - 127)
198162306a36Sopenharmony_ci *
198262306a36Sopenharmony_ci *
198362306a36Sopenharmony_ci *      Function 29:    Enable port data transmission.
198462306a36Sopenharmony_ci *      Syntax:
198562306a36Sopenharmony_ci *      void MoxaPortTxEnable(int port);
198662306a36Sopenharmony_ci *           int port           : port number (0 - 127)
198762306a36Sopenharmony_ci *
198862306a36Sopenharmony_ci *
198962306a36Sopenharmony_ci *      Function 31:    Get the received BREAK signal count and reset it.
199062306a36Sopenharmony_ci *      Syntax:
199162306a36Sopenharmony_ci *      int  MoxaPortResetBrkCnt(int port);
199262306a36Sopenharmony_ci *           int port           : port number (0 - 127)
199362306a36Sopenharmony_ci *
199462306a36Sopenharmony_ci *           return:    0 - ..  : BREAK signal count
199562306a36Sopenharmony_ci *
199662306a36Sopenharmony_ci *
199762306a36Sopenharmony_ci */
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_cistatic void MoxaPortEnable(struct moxa_port *port)
200062306a36Sopenharmony_ci{
200162306a36Sopenharmony_ci	void __iomem *ofsAddr;
200262306a36Sopenharmony_ci	u16 lowwater = 512;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	ofsAddr = port->tableAddr;
200562306a36Sopenharmony_ci	writew(lowwater, ofsAddr + Low_water);
200662306a36Sopenharmony_ci	if (MOXA_IS_320(port->board))
200762306a36Sopenharmony_ci		moxafunc(ofsAddr, FC_SetBreakIrq, 0);
200862306a36Sopenharmony_ci	else
200962306a36Sopenharmony_ci		writew(readw(ofsAddr + HostStat) | WakeupBreak,
201062306a36Sopenharmony_ci				ofsAddr + HostStat);
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_SetLineIrq, Magic_code);
201362306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_FlushQueue, 2);
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_EnableCH, Magic_code);
201662306a36Sopenharmony_ci	MoxaPortLineStatus(port);
201762306a36Sopenharmony_ci}
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_cistatic void MoxaPortDisable(struct moxa_port *port)
202062306a36Sopenharmony_ci{
202162306a36Sopenharmony_ci	void __iomem *ofsAddr = port->tableAddr;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_SetFlowCtl, 0);	/* disable flow control */
202462306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code);
202562306a36Sopenharmony_ci	writew(0, ofsAddr + HostStat);
202662306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_DisableCH, Magic_code);
202762306a36Sopenharmony_ci}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_cistatic speed_t MoxaPortSetBaud(struct moxa_port *port, speed_t baud)
203062306a36Sopenharmony_ci{
203162306a36Sopenharmony_ci	void __iomem *ofsAddr = port->tableAddr;
203262306a36Sopenharmony_ci	unsigned int clock, val;
203362306a36Sopenharmony_ci	speed_t max;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	max = MOXA_IS_320(port->board) ? 460800 : 921600;
203662306a36Sopenharmony_ci	if (baud < 50)
203762306a36Sopenharmony_ci		return 0;
203862306a36Sopenharmony_ci	if (baud > max)
203962306a36Sopenharmony_ci		baud = max;
204062306a36Sopenharmony_ci	clock = 921600;
204162306a36Sopenharmony_ci	val = clock / baud;
204262306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_SetBaud, val);
204362306a36Sopenharmony_ci	baud = clock / val;
204462306a36Sopenharmony_ci	return baud;
204562306a36Sopenharmony_ci}
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_cistatic int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio,
204862306a36Sopenharmony_ci		speed_t baud)
204962306a36Sopenharmony_ci{
205062306a36Sopenharmony_ci	void __iomem *ofsAddr;
205162306a36Sopenharmony_ci	tcflag_t mode = 0;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	ofsAddr = port->tableAddr;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	mode = termio->c_cflag & CSIZE;
205662306a36Sopenharmony_ci	if (mode == CS5)
205762306a36Sopenharmony_ci		mode = MX_CS5;
205862306a36Sopenharmony_ci	else if (mode == CS6)
205962306a36Sopenharmony_ci		mode = MX_CS6;
206062306a36Sopenharmony_ci	else if (mode == CS7)
206162306a36Sopenharmony_ci		mode = MX_CS7;
206262306a36Sopenharmony_ci	else if (mode == CS8)
206362306a36Sopenharmony_ci		mode = MX_CS8;
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	if (termio->c_cflag & CSTOPB) {
206662306a36Sopenharmony_ci		if (mode == MX_CS5)
206762306a36Sopenharmony_ci			mode |= MX_STOP15;
206862306a36Sopenharmony_ci		else
206962306a36Sopenharmony_ci			mode |= MX_STOP2;
207062306a36Sopenharmony_ci	} else
207162306a36Sopenharmony_ci		mode |= MX_STOP1;
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	if (termio->c_cflag & PARENB) {
207462306a36Sopenharmony_ci		if (termio->c_cflag & PARODD) {
207562306a36Sopenharmony_ci			if (termio->c_cflag & CMSPAR)
207662306a36Sopenharmony_ci				mode |= MX_PARMARK;
207762306a36Sopenharmony_ci			else
207862306a36Sopenharmony_ci				mode |= MX_PARODD;
207962306a36Sopenharmony_ci		} else {
208062306a36Sopenharmony_ci			if (termio->c_cflag & CMSPAR)
208162306a36Sopenharmony_ci				mode |= MX_PARSPACE;
208262306a36Sopenharmony_ci			else
208362306a36Sopenharmony_ci				mode |= MX_PAREVEN;
208462306a36Sopenharmony_ci		}
208562306a36Sopenharmony_ci	} else
208662306a36Sopenharmony_ci		mode |= MX_PARNONE;
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	moxafunc(ofsAddr, FC_SetDataMode, (u16)mode);
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	if (MOXA_IS_320(port->board) && baud >= 921600)
209162306a36Sopenharmony_ci		return -1;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	baud = MoxaPortSetBaud(port, baud);
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	if (termio->c_iflag & (IXON | IXOFF | IXANY)) {
209662306a36Sopenharmony_ci	        spin_lock_irq(&moxafunc_lock);
209762306a36Sopenharmony_ci		writeb(termio->c_cc[VSTART], ofsAddr + FuncArg);
209862306a36Sopenharmony_ci		writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
209962306a36Sopenharmony_ci		writeb(FC_SetXonXoff, ofsAddr + FuncCode);
210062306a36Sopenharmony_ci		moxa_wait_finish(ofsAddr);
210162306a36Sopenharmony_ci		spin_unlock_irq(&moxafunc_lock);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci	return baud;
210562306a36Sopenharmony_ci}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_cistatic int MoxaPortGetLineOut(struct moxa_port *port, bool *dtr_active,
210862306a36Sopenharmony_ci		bool *rts_active)
210962306a36Sopenharmony_ci{
211062306a36Sopenharmony_ci	if (dtr_active)
211162306a36Sopenharmony_ci		*dtr_active = port->lineCtrl & DTR_ON;
211262306a36Sopenharmony_ci	if (rts_active)
211362306a36Sopenharmony_ci		*rts_active = port->lineCtrl & RTS_ON;
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	return 0;
211662306a36Sopenharmony_ci}
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_cistatic void MoxaPortLineCtrl(struct moxa_port *port, bool dtr_active, bool rts_active)
211962306a36Sopenharmony_ci{
212062306a36Sopenharmony_ci	u8 mode = 0;
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	if (dtr_active)
212362306a36Sopenharmony_ci		mode |= DTR_ON;
212462306a36Sopenharmony_ci	if (rts_active)
212562306a36Sopenharmony_ci		mode |= RTS_ON;
212662306a36Sopenharmony_ci	port->lineCtrl = mode;
212762306a36Sopenharmony_ci	moxafunc(port->tableAddr, FC_LineControl, mode);
212862306a36Sopenharmony_ci}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_cistatic void MoxaPortFlowCtrl(struct moxa_port *port, int rts, int cts,
213162306a36Sopenharmony_ci		int txflow, int rxflow, int txany)
213262306a36Sopenharmony_ci{
213362306a36Sopenharmony_ci	int mode = 0;
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	if (rts)
213662306a36Sopenharmony_ci		mode |= RTS_FlowCtl;
213762306a36Sopenharmony_ci	if (cts)
213862306a36Sopenharmony_ci		mode |= CTS_FlowCtl;
213962306a36Sopenharmony_ci	if (txflow)
214062306a36Sopenharmony_ci		mode |= Tx_FlowCtl;
214162306a36Sopenharmony_ci	if (rxflow)
214262306a36Sopenharmony_ci		mode |= Rx_FlowCtl;
214362306a36Sopenharmony_ci	if (txany)
214462306a36Sopenharmony_ci		mode |= IXM_IXANY;
214562306a36Sopenharmony_ci	moxafunc(port->tableAddr, FC_SetFlowCtl, mode);
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic int MoxaPortLineStatus(struct moxa_port *port)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	void __iomem *ofsAddr;
215162306a36Sopenharmony_ci	int val;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	ofsAddr = port->tableAddr;
215462306a36Sopenharmony_ci	if (MOXA_IS_320(port->board))
215562306a36Sopenharmony_ci		val = moxafuncret(ofsAddr, FC_LineStatus, 0);
215662306a36Sopenharmony_ci	else
215762306a36Sopenharmony_ci		val = readw(ofsAddr + FlagStat) >> 4;
215862306a36Sopenharmony_ci	val &= 0x0B;
215962306a36Sopenharmony_ci	if (val & 8)
216062306a36Sopenharmony_ci		val |= 4;
216162306a36Sopenharmony_ci	moxa_new_dcdstate(port, val & 8);
216262306a36Sopenharmony_ci	val &= 7;
216362306a36Sopenharmony_ci	return val;
216462306a36Sopenharmony_ci}
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_cistatic int MoxaPortWriteData(struct tty_struct *tty, const u8 *buffer, int len)
216762306a36Sopenharmony_ci{
216862306a36Sopenharmony_ci	struct moxa_port *port = tty->driver_data;
216962306a36Sopenharmony_ci	void __iomem *baseAddr, *ofsAddr, *ofs;
217062306a36Sopenharmony_ci	unsigned int c, total;
217162306a36Sopenharmony_ci	u16 head, tail, tx_mask, spage, epage;
217262306a36Sopenharmony_ci	u16 pageno, pageofs, bufhead;
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	ofsAddr = port->tableAddr;
217562306a36Sopenharmony_ci	baseAddr = port->board->basemem;
217662306a36Sopenharmony_ci	tx_mask = readw(ofsAddr + TX_mask);
217762306a36Sopenharmony_ci	spage = readw(ofsAddr + Page_txb);
217862306a36Sopenharmony_ci	epage = readw(ofsAddr + EndPage_txb);
217962306a36Sopenharmony_ci	tail = readw(ofsAddr + TXwptr);
218062306a36Sopenharmony_ci	head = readw(ofsAddr + TXrptr);
218162306a36Sopenharmony_ci	c = (head > tail) ? (head - tail - 1) : (head - tail + tx_mask);
218262306a36Sopenharmony_ci	if (c > len)
218362306a36Sopenharmony_ci		c = len;
218462306a36Sopenharmony_ci	moxaLog.txcnt[port->port.tty->index] += c;
218562306a36Sopenharmony_ci	total = c;
218662306a36Sopenharmony_ci	if (spage == epage) {
218762306a36Sopenharmony_ci		bufhead = readw(ofsAddr + Ofs_txb);
218862306a36Sopenharmony_ci		writew(spage, baseAddr + Control_reg);
218962306a36Sopenharmony_ci		while (c > 0) {
219062306a36Sopenharmony_ci			if (head > tail)
219162306a36Sopenharmony_ci				len = head - tail - 1;
219262306a36Sopenharmony_ci			else
219362306a36Sopenharmony_ci				len = tx_mask + 1 - tail;
219462306a36Sopenharmony_ci			len = (c > len) ? len : c;
219562306a36Sopenharmony_ci			ofs = baseAddr + DynPage_addr + bufhead + tail;
219662306a36Sopenharmony_ci			memcpy_toio(ofs, buffer, len);
219762306a36Sopenharmony_ci			buffer += len;
219862306a36Sopenharmony_ci			tail = (tail + len) & tx_mask;
219962306a36Sopenharmony_ci			c -= len;
220062306a36Sopenharmony_ci		}
220162306a36Sopenharmony_ci	} else {
220262306a36Sopenharmony_ci		pageno = spage + (tail >> 13);
220362306a36Sopenharmony_ci		pageofs = tail & Page_mask;
220462306a36Sopenharmony_ci		while (c > 0) {
220562306a36Sopenharmony_ci			len = Page_size - pageofs;
220662306a36Sopenharmony_ci			if (len > c)
220762306a36Sopenharmony_ci				len = c;
220862306a36Sopenharmony_ci			writeb(pageno, baseAddr + Control_reg);
220962306a36Sopenharmony_ci			ofs = baseAddr + DynPage_addr + pageofs;
221062306a36Sopenharmony_ci			memcpy_toio(ofs, buffer, len);
221162306a36Sopenharmony_ci			buffer += len;
221262306a36Sopenharmony_ci			if (++pageno == epage)
221362306a36Sopenharmony_ci				pageno = spage;
221462306a36Sopenharmony_ci			pageofs = 0;
221562306a36Sopenharmony_ci			c -= len;
221662306a36Sopenharmony_ci		}
221762306a36Sopenharmony_ci		tail = (tail + total) & tx_mask;
221862306a36Sopenharmony_ci	}
221962306a36Sopenharmony_ci	writew(tail, ofsAddr + TXwptr);
222062306a36Sopenharmony_ci	writeb(1, ofsAddr + CD180TXirq);	/* start to send */
222162306a36Sopenharmony_ci	return total;
222262306a36Sopenharmony_ci}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_cistatic int MoxaPortReadData(struct moxa_port *port)
222562306a36Sopenharmony_ci{
222662306a36Sopenharmony_ci	struct tty_struct *tty = port->port.tty;
222762306a36Sopenharmony_ci	unsigned char *dst;
222862306a36Sopenharmony_ci	void __iomem *baseAddr, *ofsAddr, *ofs;
222962306a36Sopenharmony_ci	unsigned int count, len, total;
223062306a36Sopenharmony_ci	u16 tail, rx_mask, spage, epage;
223162306a36Sopenharmony_ci	u16 pageno, pageofs, bufhead, head;
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	ofsAddr = port->tableAddr;
223462306a36Sopenharmony_ci	baseAddr = port->board->basemem;
223562306a36Sopenharmony_ci	head = readw(ofsAddr + RXrptr);
223662306a36Sopenharmony_ci	tail = readw(ofsAddr + RXwptr);
223762306a36Sopenharmony_ci	rx_mask = readw(ofsAddr + RX_mask);
223862306a36Sopenharmony_ci	spage = readw(ofsAddr + Page_rxb);
223962306a36Sopenharmony_ci	epage = readw(ofsAddr + EndPage_rxb);
224062306a36Sopenharmony_ci	count = (tail >= head) ? (tail - head) : (tail - head + rx_mask + 1);
224162306a36Sopenharmony_ci	if (count == 0)
224262306a36Sopenharmony_ci		return 0;
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	total = count;
224562306a36Sopenharmony_ci	moxaLog.rxcnt[tty->index] += total;
224662306a36Sopenharmony_ci	if (spage == epage) {
224762306a36Sopenharmony_ci		bufhead = readw(ofsAddr + Ofs_rxb);
224862306a36Sopenharmony_ci		writew(spage, baseAddr + Control_reg);
224962306a36Sopenharmony_ci		while (count > 0) {
225062306a36Sopenharmony_ci			ofs = baseAddr + DynPage_addr + bufhead + head;
225162306a36Sopenharmony_ci			len = (tail >= head) ? (tail - head) :
225262306a36Sopenharmony_ci					(rx_mask + 1 - head);
225362306a36Sopenharmony_ci			len = tty_prepare_flip_string(&port->port, &dst,
225462306a36Sopenharmony_ci					min(len, count));
225562306a36Sopenharmony_ci			memcpy_fromio(dst, ofs, len);
225662306a36Sopenharmony_ci			head = (head + len) & rx_mask;
225762306a36Sopenharmony_ci			count -= len;
225862306a36Sopenharmony_ci		}
225962306a36Sopenharmony_ci	} else {
226062306a36Sopenharmony_ci		pageno = spage + (head >> 13);
226162306a36Sopenharmony_ci		pageofs = head & Page_mask;
226262306a36Sopenharmony_ci		while (count > 0) {
226362306a36Sopenharmony_ci			writew(pageno, baseAddr + Control_reg);
226462306a36Sopenharmony_ci			ofs = baseAddr + DynPage_addr + pageofs;
226562306a36Sopenharmony_ci			len = tty_prepare_flip_string(&port->port, &dst,
226662306a36Sopenharmony_ci					min(Page_size - pageofs, count));
226762306a36Sopenharmony_ci			memcpy_fromio(dst, ofs, len);
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci			count -= len;
227062306a36Sopenharmony_ci			pageofs = (pageofs + len) & Page_mask;
227162306a36Sopenharmony_ci			if (pageofs == 0 && ++pageno == epage)
227262306a36Sopenharmony_ci				pageno = spage;
227362306a36Sopenharmony_ci		}
227462306a36Sopenharmony_ci		head = (head + total) & rx_mask;
227562306a36Sopenharmony_ci	}
227662306a36Sopenharmony_ci	writew(head, ofsAddr + RXrptr);
227762306a36Sopenharmony_ci	if (readb(ofsAddr + FlagStat) & Xoff_state) {
227862306a36Sopenharmony_ci		moxaLowWaterChk = 1;
227962306a36Sopenharmony_ci		port->lowChkFlag = 1;
228062306a36Sopenharmony_ci	}
228162306a36Sopenharmony_ci	return total;
228262306a36Sopenharmony_ci}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_cistatic unsigned int MoxaPortTxQueue(struct moxa_port *port)
228662306a36Sopenharmony_ci{
228762306a36Sopenharmony_ci	void __iomem *ofsAddr = port->tableAddr;
228862306a36Sopenharmony_ci	u16 rptr, wptr, mask;
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	rptr = readw(ofsAddr + TXrptr);
229162306a36Sopenharmony_ci	wptr = readw(ofsAddr + TXwptr);
229262306a36Sopenharmony_ci	mask = readw(ofsAddr + TX_mask);
229362306a36Sopenharmony_ci	return (wptr - rptr) & mask;
229462306a36Sopenharmony_ci}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_cistatic unsigned int MoxaPortTxFree(struct moxa_port *port)
229762306a36Sopenharmony_ci{
229862306a36Sopenharmony_ci	void __iomem *ofsAddr = port->tableAddr;
229962306a36Sopenharmony_ci	u16 rptr, wptr, mask;
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	rptr = readw(ofsAddr + TXrptr);
230262306a36Sopenharmony_ci	wptr = readw(ofsAddr + TXwptr);
230362306a36Sopenharmony_ci	mask = readw(ofsAddr + TX_mask);
230462306a36Sopenharmony_ci	return mask - ((wptr - rptr) & mask);
230562306a36Sopenharmony_ci}
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_cistatic int MoxaPortRxQueue(struct moxa_port *port)
230862306a36Sopenharmony_ci{
230962306a36Sopenharmony_ci	void __iomem *ofsAddr = port->tableAddr;
231062306a36Sopenharmony_ci	u16 rptr, wptr, mask;
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	rptr = readw(ofsAddr + RXrptr);
231362306a36Sopenharmony_ci	wptr = readw(ofsAddr + RXwptr);
231462306a36Sopenharmony_ci	mask = readw(ofsAddr + RX_mask);
231562306a36Sopenharmony_ci	return (wptr - rptr) & mask;
231662306a36Sopenharmony_ci}
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_cistatic void MoxaPortTxDisable(struct moxa_port *port)
231962306a36Sopenharmony_ci{
232062306a36Sopenharmony_ci	moxafunc(port->tableAddr, FC_SetXoffState, Magic_code);
232162306a36Sopenharmony_ci}
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_cistatic void MoxaPortTxEnable(struct moxa_port *port)
232462306a36Sopenharmony_ci{
232562306a36Sopenharmony_ci	moxafunc(port->tableAddr, FC_SetXonState, Magic_code);
232662306a36Sopenharmony_ci}
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_cistatic int moxa_get_serial_info(struct tty_struct *tty,
232962306a36Sopenharmony_ci		struct serial_struct *ss)
233062306a36Sopenharmony_ci{
233162306a36Sopenharmony_ci	struct moxa_port *info = tty->driver_data;
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	if (tty->index == MAX_PORTS)
233462306a36Sopenharmony_ci		return -EINVAL;
233562306a36Sopenharmony_ci	if (!info)
233662306a36Sopenharmony_ci		return -ENODEV;
233762306a36Sopenharmony_ci	mutex_lock(&info->port.mutex);
233862306a36Sopenharmony_ci	ss->type = info->type;
233962306a36Sopenharmony_ci	ss->line = info->port.tty->index;
234062306a36Sopenharmony_ci	ss->flags = info->port.flags;
234162306a36Sopenharmony_ci	ss->baud_base = 921600;
234262306a36Sopenharmony_ci	ss->close_delay = jiffies_to_msecs(info->port.close_delay) / 10;
234362306a36Sopenharmony_ci	mutex_unlock(&info->port.mutex);
234462306a36Sopenharmony_ci	return 0;
234562306a36Sopenharmony_ci}
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_cistatic int moxa_set_serial_info(struct tty_struct *tty,
234962306a36Sopenharmony_ci		struct serial_struct *ss)
235062306a36Sopenharmony_ci{
235162306a36Sopenharmony_ci	struct moxa_port *info = tty->driver_data;
235262306a36Sopenharmony_ci	unsigned int close_delay;
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	if (tty->index == MAX_PORTS)
235562306a36Sopenharmony_ci		return -EINVAL;
235662306a36Sopenharmony_ci	if (!info)
235762306a36Sopenharmony_ci		return -ENODEV;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	close_delay = msecs_to_jiffies(ss->close_delay * 10);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	mutex_lock(&info->port.mutex);
236262306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN)) {
236362306a36Sopenharmony_ci		if (close_delay != info->port.close_delay ||
236462306a36Sopenharmony_ci		    ss->type != info->type ||
236562306a36Sopenharmony_ci		    ((ss->flags & ~ASYNC_USR_MASK) !=
236662306a36Sopenharmony_ci		     (info->port.flags & ~ASYNC_USR_MASK))) {
236762306a36Sopenharmony_ci			mutex_unlock(&info->port.mutex);
236862306a36Sopenharmony_ci			return -EPERM;
236962306a36Sopenharmony_ci		}
237062306a36Sopenharmony_ci	} else {
237162306a36Sopenharmony_ci		info->port.close_delay = close_delay;
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_ci		MoxaSetFifo(info, ss->type == PORT_16550A);
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci		info->type = ss->type;
237662306a36Sopenharmony_ci	}
237762306a36Sopenharmony_ci	mutex_unlock(&info->port.mutex);
237862306a36Sopenharmony_ci	return 0;
237962306a36Sopenharmony_ci}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci/*****************************************************************************
238462306a36Sopenharmony_ci *	Static local functions: 					     *
238562306a36Sopenharmony_ci *****************************************************************************/
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_cistatic void MoxaSetFifo(struct moxa_port *port, int enable)
238862306a36Sopenharmony_ci{
238962306a36Sopenharmony_ci	void __iomem *ofsAddr = port->tableAddr;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	if (!enable) {
239262306a36Sopenharmony_ci		moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0);
239362306a36Sopenharmony_ci		moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1);
239462306a36Sopenharmony_ci	} else {
239562306a36Sopenharmony_ci		moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3);
239662306a36Sopenharmony_ci		moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16);
239762306a36Sopenharmony_ci	}
239862306a36Sopenharmony_ci}
2399