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