1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2a6afd9f3SGreg Kroah-Hartman /* 3a6afd9f3SGreg Kroah-Hartman * mxser.c -- MOXA Smartio/Industio family multiport serial driver. 4a6afd9f3SGreg Kroah-Hartman * 5a6afd9f3SGreg Kroah-Hartman * Copyright (C) 1999-2006 Moxa Technologies (support@moxa.com). 6a6afd9f3SGreg Kroah-Hartman * Copyright (C) 2006-2008 Jiri Slaby <jirislaby@gmail.com> 7a6afd9f3SGreg Kroah-Hartman * 8a6afd9f3SGreg Kroah-Hartman * This code is loosely based on the 1.8 moxa driver which is based on 9a6afd9f3SGreg Kroah-Hartman * Linux serial driver, written by Linus Torvalds, Theodore T'so and 10a6afd9f3SGreg Kroah-Hartman * others. 11a6afd9f3SGreg Kroah-Hartman * 12a6afd9f3SGreg Kroah-Hartman * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox 13a6afd9f3SGreg Kroah-Hartman * <alan@lxorguk.ukuu.org.uk>. The original 1.8 code is available on 14a6afd9f3SGreg Kroah-Hartman * www.moxa.com. 15a6afd9f3SGreg Kroah-Hartman * - Fixed x86_64 cleanness 16a6afd9f3SGreg Kroah-Hartman */ 17a6afd9f3SGreg Kroah-Hartman 18a6afd9f3SGreg Kroah-Hartman #include <linux/module.h> 19a6afd9f3SGreg Kroah-Hartman #include <linux/errno.h> 20a6afd9f3SGreg Kroah-Hartman #include <linux/signal.h> 21a6afd9f3SGreg Kroah-Hartman #include <linux/sched.h> 22a6afd9f3SGreg Kroah-Hartman #include <linux/timer.h> 23a6afd9f3SGreg Kroah-Hartman #include <linux/interrupt.h> 24a6afd9f3SGreg Kroah-Hartman #include <linux/tty.h> 25a6afd9f3SGreg Kroah-Hartman #include <linux/tty_flip.h> 26a6afd9f3SGreg Kroah-Hartman #include <linux/serial.h> 27a6afd9f3SGreg Kroah-Hartman #include <linux/serial_reg.h> 28a6afd9f3SGreg Kroah-Hartman #include <linux/major.h> 29a6afd9f3SGreg Kroah-Hartman #include <linux/string.h> 30a6afd9f3SGreg Kroah-Hartman #include <linux/fcntl.h> 31a6afd9f3SGreg Kroah-Hartman #include <linux/ptrace.h> 32a6afd9f3SGreg Kroah-Hartman #include <linux/ioport.h> 33a6afd9f3SGreg Kroah-Hartman #include <linux/mm.h> 34a6afd9f3SGreg Kroah-Hartman #include <linux/delay.h> 35a6afd9f3SGreg Kroah-Hartman #include <linux/pci.h> 36a6afd9f3SGreg Kroah-Hartman #include <linux/bitops.h> 37a6afd9f3SGreg Kroah-Hartman #include <linux/slab.h> 385a3c6b25SManuel Zerpies #include <linux/ratelimit.h> 39a6afd9f3SGreg Kroah-Hartman 40a6afd9f3SGreg Kroah-Hartman #include <asm/io.h> 41a6afd9f3SGreg Kroah-Hartman #include <asm/irq.h> 427c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 43a6afd9f3SGreg Kroah-Hartman 444463cc5bSJiri Slaby /* 454463cc5bSJiri Slaby * Semi-public control interfaces 464463cc5bSJiri Slaby */ 474463cc5bSJiri Slaby 484463cc5bSJiri Slaby /* 494463cc5bSJiri Slaby * MOXA ioctls 504463cc5bSJiri Slaby */ 514463cc5bSJiri Slaby 524463cc5bSJiri Slaby #define MOXA 0x400 534463cc5bSJiri Slaby #define MOXA_SET_OP_MODE (MOXA + 66) 544463cc5bSJiri Slaby #define MOXA_GET_OP_MODE (MOXA + 67) 554463cc5bSJiri Slaby 564463cc5bSJiri Slaby #define RS232_MODE 0 574463cc5bSJiri Slaby #define RS485_2WIRE_MODE 1 584463cc5bSJiri Slaby #define RS422_MODE 2 594463cc5bSJiri Slaby #define RS485_4WIRE_MODE 3 604463cc5bSJiri Slaby #define OP_MODE_MASK 3 614463cc5bSJiri Slaby 624463cc5bSJiri Slaby /* --------------------------------------------------- */ 634463cc5bSJiri Slaby 644463cc5bSJiri Slaby /* 654463cc5bSJiri Slaby * Follow just what Moxa Must chip defines. 664463cc5bSJiri Slaby * 67464fbf6cSJiri Slaby * When LCR register (offset 0x03) is written the following value, the Must chip 68464fbf6cSJiri Slaby * will enter enhanced mode. And a write to EFR (offset 0x02) bit 6,7 will 694463cc5bSJiri Slaby * change bank. 704463cc5bSJiri Slaby */ 71464fbf6cSJiri Slaby #define MOXA_MUST_ENTER_ENHANCED 0xBF 724463cc5bSJiri Slaby 73464fbf6cSJiri Slaby /* when enhanced mode is enabled, access to general bank register */ 744463cc5bSJiri Slaby #define MOXA_MUST_GDL_REGISTER 0x07 754463cc5bSJiri Slaby #define MOXA_MUST_GDL_MASK 0x7F 764463cc5bSJiri Slaby #define MOXA_MUST_GDL_HAS_BAD_DATA 0x80 774463cc5bSJiri Slaby 784463cc5bSJiri Slaby #define MOXA_MUST_LSR_RERR 0x80 /* error in receive FIFO */ 79464fbf6cSJiri Slaby /* enhanced register bank select and enhanced mode setting register */ 80464fbf6cSJiri Slaby /* This works only when LCR register equals to 0xBF */ 814463cc5bSJiri Slaby #define MOXA_MUST_EFR_REGISTER 0x02 82464fbf6cSJiri Slaby #define MOXA_MUST_EFR_EFRB_ENABLE 0x10 /* enhanced mode enable */ 83464fbf6cSJiri Slaby /* enhanced register bank set 0, 1, 2 */ 844463cc5bSJiri Slaby #define MOXA_MUST_EFR_BANK0 0x00 854463cc5bSJiri Slaby #define MOXA_MUST_EFR_BANK1 0x40 864463cc5bSJiri Slaby #define MOXA_MUST_EFR_BANK2 0x80 874463cc5bSJiri Slaby #define MOXA_MUST_EFR_BANK3 0xC0 884463cc5bSJiri Slaby #define MOXA_MUST_EFR_BANK_MASK 0xC0 894463cc5bSJiri Slaby 904463cc5bSJiri Slaby /* set XON1 value register, when LCR=0xBF and change to bank0 */ 914463cc5bSJiri Slaby #define MOXA_MUST_XON1_REGISTER 0x04 924463cc5bSJiri Slaby 934463cc5bSJiri Slaby /* set XON2 value register, when LCR=0xBF and change to bank0 */ 944463cc5bSJiri Slaby #define MOXA_MUST_XON2_REGISTER 0x05 954463cc5bSJiri Slaby 964463cc5bSJiri Slaby /* set XOFF1 value register, when LCR=0xBF and change to bank0 */ 974463cc5bSJiri Slaby #define MOXA_MUST_XOFF1_REGISTER 0x06 984463cc5bSJiri Slaby 994463cc5bSJiri Slaby /* set XOFF2 value register, when LCR=0xBF and change to bank0 */ 1004463cc5bSJiri Slaby #define MOXA_MUST_XOFF2_REGISTER 0x07 1014463cc5bSJiri Slaby 1024463cc5bSJiri Slaby #define MOXA_MUST_RBRTL_REGISTER 0x04 1034463cc5bSJiri Slaby #define MOXA_MUST_RBRTH_REGISTER 0x05 1044463cc5bSJiri Slaby #define MOXA_MUST_RBRTI_REGISTER 0x06 1054463cc5bSJiri Slaby #define MOXA_MUST_THRTL_REGISTER 0x07 1064463cc5bSJiri Slaby #define MOXA_MUST_ENUM_REGISTER 0x04 1074463cc5bSJiri Slaby #define MOXA_MUST_HWID_REGISTER 0x05 1084463cc5bSJiri Slaby #define MOXA_MUST_ECR_REGISTER 0x06 1094463cc5bSJiri Slaby #define MOXA_MUST_CSR_REGISTER 0x07 1104463cc5bSJiri Slaby 1114463cc5bSJiri Slaby #define MOXA_MUST_FCR_GDA_MODE_ENABLE 0x20 /* good data mode enable */ 1124463cc5bSJiri Slaby #define MOXA_MUST_FCR_GDA_ONLY_ENABLE 0x10 /* only good data put into RxFIFO */ 1134463cc5bSJiri Slaby 1144463cc5bSJiri Slaby #define MOXA_MUST_IER_ECTSI 0x80 /* enable CTS interrupt */ 1154463cc5bSJiri Slaby #define MOXA_MUST_IER_ERTSI 0x40 /* enable RTS interrupt */ 1164463cc5bSJiri Slaby #define MOXA_MUST_IER_XINT 0x20 /* enable Xon/Xoff interrupt */ 1174463cc5bSJiri Slaby #define MOXA_MUST_IER_EGDAI 0x10 /* enable GDA interrupt */ 1184463cc5bSJiri Slaby 1194463cc5bSJiri Slaby #define MOXA_MUST_RECV_ISR (UART_IER_RDI | MOXA_MUST_IER_EGDAI) 1204463cc5bSJiri Slaby 1214463cc5bSJiri Slaby /* GDA interrupt pending */ 1224463cc5bSJiri Slaby #define MOXA_MUST_IIR_GDA 0x1C 1234463cc5bSJiri Slaby #define MOXA_MUST_IIR_RDA 0x04 1244463cc5bSJiri Slaby #define MOXA_MUST_IIR_RTO 0x0C 1254463cc5bSJiri Slaby #define MOXA_MUST_IIR_LSR 0x06 1264463cc5bSJiri Slaby 1274463cc5bSJiri Slaby /* received Xon/Xoff or specical interrupt pending */ 1284463cc5bSJiri Slaby #define MOXA_MUST_IIR_XSC 0x10 1294463cc5bSJiri Slaby 1304463cc5bSJiri Slaby /* RTS/CTS change state interrupt pending */ 1314463cc5bSJiri Slaby #define MOXA_MUST_IIR_RTSCTS 0x20 1324463cc5bSJiri Slaby #define MOXA_MUST_IIR_MASK 0x3E 1334463cc5bSJiri Slaby 1344463cc5bSJiri Slaby #define MOXA_MUST_MCR_XON_FLAG 0x40 1354463cc5bSJiri Slaby #define MOXA_MUST_MCR_XON_ANY 0x80 1364463cc5bSJiri Slaby #define MOXA_MUST_MCR_TX_XON 0x08 1374463cc5bSJiri Slaby 1384463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_MASK 0x0F /* software flow control on chip mask value */ 1394463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_TX1 0x08 /* send Xon1/Xoff1 */ 1404463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_TX2 0x04 /* send Xon2/Xoff2 */ 1414463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_TX12 0x0C /* send Xon1,Xon2/Xoff1,Xoff2 */ 1424463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_TX_NO 0x00 /* don't send Xon/Xoff */ 1434463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_TX_MASK 0x0C /* Tx software flow control mask */ 1444463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_RX_NO 0x00 /* don't receive Xon/Xoff */ 1454463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_RX1 0x02 /* receive Xon1/Xoff1 */ 1464463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_RX2 0x01 /* receive Xon2/Xoff2 */ 1474463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_RX12 0x03 /* receive Xon1,Xon2/Xoff1,Xoff2 */ 1484463cc5bSJiri Slaby #define MOXA_MUST_EFR_SF_RX_MASK 0x03 /* Rx software flow control mask */ 149a6afd9f3SGreg Kroah-Hartman 150a6afd9f3SGreg Kroah-Hartman #define MXSERMAJOR 174 151a6afd9f3SGreg Kroah-Hartman 152a6afd9f3SGreg Kroah-Hartman #define MXSER_BOARDS 4 /* Max. boards */ 153a6afd9f3SGreg Kroah-Hartman #define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ 154a6afd9f3SGreg Kroah-Hartman #define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD) 155a6afd9f3SGreg Kroah-Hartman #define MXSER_ISR_PASS_LIMIT 100 156a6afd9f3SGreg Kroah-Hartman 157a6afd9f3SGreg Kroah-Hartman #define WAKEUP_CHARS 256 158a6afd9f3SGreg Kroah-Hartman 159a6970c39SJiri Slaby #define MXSER_BAUD_BASE 921600 160d811b26bSJiri Slaby #define MXSER_CUSTOM_DIVISOR (MXSER_BAUD_BASE * 16) 161a6970c39SJiri Slaby 162a6afd9f3SGreg Kroah-Hartman #define PCI_DEVICE_ID_POS104UL 0x1044 163a6afd9f3SGreg Kroah-Hartman #define PCI_DEVICE_ID_CB108 0x1080 164a6afd9f3SGreg Kroah-Hartman #define PCI_DEVICE_ID_CP102UF 0x1023 165a6afd9f3SGreg Kroah-Hartman #define PCI_DEVICE_ID_CP112UL 0x1120 166a6afd9f3SGreg Kroah-Hartman #define PCI_DEVICE_ID_CB114 0x1142 167a6afd9f3SGreg Kroah-Hartman #define PCI_DEVICE_ID_CP114UL 0x1143 168a6afd9f3SGreg Kroah-Hartman #define PCI_DEVICE_ID_CB134I 0x1341 169a6afd9f3SGreg Kroah-Hartman #define PCI_DEVICE_ID_CP138U 0x1380 170a6afd9f3SGreg Kroah-Hartman 171c24c31ffSJiri Slaby #define MXSER_NPORTS(ddata) ((ddata) & 0xffU) 172c24c31ffSJiri Slaby #define MXSER_HIGHBAUD 0x0100 173a6afd9f3SGreg Kroah-Hartman 174e4558366SJiri Slaby enum mxser_must_hwid { 175e4558366SJiri Slaby MOXA_OTHER_UART = 0x00, 176e4558366SJiri Slaby MOXA_MUST_MU150_HWID = 0x01, 177e4558366SJiri Slaby MOXA_MUST_MU860_HWID = 0x02, 178e4558366SJiri Slaby }; 179e4558366SJiri Slaby 180a6afd9f3SGreg Kroah-Hartman static const struct { 181dc33f644SJiri Slaby u8 type; 182dc33f644SJiri Slaby u8 fifo_size; 183dc33f644SJiri Slaby u8 rx_high_water; 184dc33f644SJiri Slaby u8 rx_low_water; 185dc33f644SJiri Slaby speed_t max_baud; 186a6afd9f3SGreg Kroah-Hartman } Gpci_uart_info[] = { 187dc33f644SJiri Slaby { MOXA_OTHER_UART, 16, 14, 1, 921600 }, 188dc33f644SJiri Slaby { MOXA_MUST_MU150_HWID, 64, 48, 16, 230400 }, 189dc33f644SJiri Slaby { MOXA_MUST_MU860_HWID, 128, 96, 32, 921600 } 190a6afd9f3SGreg Kroah-Hartman }; 191a6afd9f3SGreg Kroah-Hartman #define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info) 192a6afd9f3SGreg Kroah-Hartman 193a6afd9f3SGreg Kroah-Hartman 194a6afd9f3SGreg Kroah-Hartman /* driver_data correspond to the lines in the structure above 195a6afd9f3SGreg Kroah-Hartman see also ISA probe function before you change something */ 1963385ecf8SArvind Yadav static const struct pci_device_id mxser_pcibrds[] = { 197c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), .driver_data = 8 }, 198c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), .driver_data = 4 }, 19915254902SJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), .driver_data = 2 }, 200c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114), .driver_data = 4 }, 20115254902SJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114), .driver_data = 4 }, 202c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102), .driver_data = 2 | MXSER_HIGHBAUD }, 203c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 4 }, 204c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 8 }, 205c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 2 }, 206c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 4 }, 207c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 4 }, 208c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 8 }, /* RC7000 */ 209c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 8 }, 210c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 2 }, 211c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 2 }, 212c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 8 }, 213c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 8 }, 214c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 4 }, 215c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108), .driver_data = 8 }, 216c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114), .driver_data = 4 }, 217c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I), .driver_data = 4 }, 218c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U), .driver_data = 8 }, 219c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 4 }, 220c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL), .driver_data = 4 }, 221c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF), .driver_data = 2 }, 222c24c31ffSJiri Slaby { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL), .driver_data = 2 }, 223a6afd9f3SGreg Kroah-Hartman { } 224a6afd9f3SGreg Kroah-Hartman }; 225a6afd9f3SGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, mxser_pcibrds); 226a6afd9f3SGreg Kroah-Hartman 227a6afd9f3SGreg Kroah-Hartman static int ttymajor = MXSERMAJOR; 228a6afd9f3SGreg Kroah-Hartman 229a6afd9f3SGreg Kroah-Hartman /* Variables for insmod */ 230a6afd9f3SGreg Kroah-Hartman 231a6afd9f3SGreg Kroah-Hartman MODULE_AUTHOR("Casper Yang"); 232a6afd9f3SGreg Kroah-Hartman MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); 233a6afd9f3SGreg Kroah-Hartman module_param(ttymajor, int, 0); 234a6afd9f3SGreg Kroah-Hartman MODULE_LICENSE("GPL"); 235a6afd9f3SGreg Kroah-Hartman 236a6afd9f3SGreg Kroah-Hartman struct mxser_board; 237a6afd9f3SGreg Kroah-Hartman 238a6afd9f3SGreg Kroah-Hartman struct mxser_port { 239a6afd9f3SGreg Kroah-Hartman struct tty_port port; 240a6afd9f3SGreg Kroah-Hartman struct mxser_board *board; 241a6afd9f3SGreg Kroah-Hartman 242a6afd9f3SGreg Kroah-Hartman unsigned long ioaddr; 243a6afd9f3SGreg Kroah-Hartman unsigned long opmode_ioaddr; 244a6afd9f3SGreg Kroah-Hartman 245dc33f644SJiri Slaby u8 rx_high_water; 246dc33f644SJiri Slaby u8 rx_low_water; 247a6afd9f3SGreg Kroah-Hartman int type; /* UART type */ 248a6afd9f3SGreg Kroah-Hartman 249a93963e4SJiri Slaby unsigned char x_char; /* xon/xoff character */ 250a93963e4SJiri Slaby u8 IER; /* Interrupt Enable Register */ 251a93963e4SJiri Slaby u8 MCR; /* Modem control register */ 252d249e662SJiri Slaby u8 FCR; /* FIFO control register */ 253a6afd9f3SGreg Kroah-Hartman 25419236287SJiri Slaby bool ldisc_stop_rx; 255a6afd9f3SGreg Kroah-Hartman 256a6afd9f3SGreg Kroah-Hartman struct async_icount icount; /* kernel counters for 4 input interrupts */ 257104583b5SJiri Slaby unsigned int timeout; 258a6afd9f3SGreg Kroah-Hartman 259a93963e4SJiri Slaby u8 read_status_mask; 260a93963e4SJiri Slaby u8 ignore_status_mask; 261dc33f644SJiri Slaby u8 xmit_fifo_size; 26202e43144SJiri Slaby unsigned int xmit_head; 26302e43144SJiri Slaby unsigned int xmit_tail; 26402e43144SJiri Slaby unsigned int xmit_cnt; 265a6afd9f3SGreg Kroah-Hartman 266a6afd9f3SGreg Kroah-Hartman spinlock_t slock; 267a6afd9f3SGreg Kroah-Hartman }; 268a6afd9f3SGreg Kroah-Hartman 269a6afd9f3SGreg Kroah-Hartman struct mxser_board { 270a6afd9f3SGreg Kroah-Hartman unsigned int idx; 271c24c31ffSJiri Slaby unsigned short nports; 272a6afd9f3SGreg Kroah-Hartman int irq; 273a6afd9f3SGreg Kroah-Hartman unsigned long vector; 274a6afd9f3SGreg Kroah-Hartman 275e4558366SJiri Slaby enum mxser_must_hwid must_hwid; 276928f9464SJiri Slaby speed_t max_baud; 277a6afd9f3SGreg Kroah-Hartman 278ad1c92ffSJiri Slaby struct mxser_port ports[]; 279a6afd9f3SGreg Kroah-Hartman }; 280a6afd9f3SGreg Kroah-Hartman 281f8b6b327SJiri Slaby static DECLARE_BITMAP(mxser_boards, MXSER_BOARDS); 282a6afd9f3SGreg Kroah-Hartman static struct tty_driver *mxvar_sdriver; 283a6afd9f3SGreg Kroah-Hartman 284edb7d27cSJiri Slaby static u8 __mxser_must_set_EFR(unsigned long baseio, u8 clear, u8 set, 285edb7d27cSJiri Slaby bool restore_LCR) 286a6afd9f3SGreg Kroah-Hartman { 287edb7d27cSJiri Slaby u8 oldlcr, efr; 288a6afd9f3SGreg Kroah-Hartman 289a6afd9f3SGreg Kroah-Hartman oldlcr = inb(baseio + UART_LCR); 290464fbf6cSJiri Slaby outb(MOXA_MUST_ENTER_ENHANCED, baseio + UART_LCR); 291a6afd9f3SGreg Kroah-Hartman 292a6afd9f3SGreg Kroah-Hartman efr = inb(baseio + MOXA_MUST_EFR_REGISTER); 293edb7d27cSJiri Slaby efr &= ~clear; 294edb7d27cSJiri Slaby efr |= set; 295a6afd9f3SGreg Kroah-Hartman 296a6afd9f3SGreg Kroah-Hartman outb(efr, baseio + MOXA_MUST_EFR_REGISTER); 297edb7d27cSJiri Slaby 298edb7d27cSJiri Slaby if (restore_LCR) 299a6afd9f3SGreg Kroah-Hartman outb(oldlcr, baseio + UART_LCR); 300a6afd9f3SGreg Kroah-Hartman 301edb7d27cSJiri Slaby return oldlcr; 302a6afd9f3SGreg Kroah-Hartman } 303a6afd9f3SGreg Kroah-Hartman 304b286484bSJiri Slaby static u8 mxser_must_select_bank(unsigned long baseio, u8 bank) 305b286484bSJiri Slaby { 306b286484bSJiri Slaby return __mxser_must_set_EFR(baseio, MOXA_MUST_EFR_BANK_MASK, bank, 307b286484bSJiri Slaby false); 308b286484bSJiri Slaby } 309b286484bSJiri Slaby 310a6afd9f3SGreg Kroah-Hartman static void mxser_set_must_xon1_value(unsigned long baseio, u8 value) 311a6afd9f3SGreg Kroah-Hartman { 312b286484bSJiri Slaby u8 oldlcr = mxser_must_select_bank(baseio, MOXA_MUST_EFR_BANK0); 313a6afd9f3SGreg Kroah-Hartman outb(value, baseio + MOXA_MUST_XON1_REGISTER); 314a6afd9f3SGreg Kroah-Hartman outb(oldlcr, baseio + UART_LCR); 315a6afd9f3SGreg Kroah-Hartman } 316a6afd9f3SGreg Kroah-Hartman 317a6afd9f3SGreg Kroah-Hartman static void mxser_set_must_xoff1_value(unsigned long baseio, u8 value) 318a6afd9f3SGreg Kroah-Hartman { 319b286484bSJiri Slaby u8 oldlcr = mxser_must_select_bank(baseio, MOXA_MUST_EFR_BANK0); 320a6afd9f3SGreg Kroah-Hartman outb(value, baseio + MOXA_MUST_XOFF1_REGISTER); 321a6afd9f3SGreg Kroah-Hartman outb(oldlcr, baseio + UART_LCR); 322a6afd9f3SGreg Kroah-Hartman } 323a6afd9f3SGreg Kroah-Hartman 324a6afd9f3SGreg Kroah-Hartman static void mxser_set_must_fifo_value(struct mxser_port *info) 325a6afd9f3SGreg Kroah-Hartman { 326b286484bSJiri Slaby u8 oldlcr = mxser_must_select_bank(info->ioaddr, MOXA_MUST_EFR_BANK1); 327dc33f644SJiri Slaby outb(info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTH_REGISTER); 328dc33f644SJiri Slaby outb(info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTI_REGISTER); 329dc33f644SJiri Slaby outb(info->rx_low_water, info->ioaddr + MOXA_MUST_RBRTL_REGISTER); 330a6afd9f3SGreg Kroah-Hartman outb(oldlcr, info->ioaddr + UART_LCR); 331a6afd9f3SGreg Kroah-Hartman } 332a6afd9f3SGreg Kroah-Hartman 333a6afd9f3SGreg Kroah-Hartman static void mxser_set_must_enum_value(unsigned long baseio, u8 value) 334a6afd9f3SGreg Kroah-Hartman { 335b286484bSJiri Slaby u8 oldlcr = mxser_must_select_bank(baseio, MOXA_MUST_EFR_BANK2); 336a6afd9f3SGreg Kroah-Hartman outb(value, baseio + MOXA_MUST_ENUM_REGISTER); 337a6afd9f3SGreg Kroah-Hartman outb(oldlcr, baseio + UART_LCR); 338a6afd9f3SGreg Kroah-Hartman } 339a6afd9f3SGreg Kroah-Hartman 340b286484bSJiri Slaby static u8 mxser_get_must_hardware_id(unsigned long baseio) 341a6afd9f3SGreg Kroah-Hartman { 342b286484bSJiri Slaby u8 oldlcr = mxser_must_select_bank(baseio, MOXA_MUST_EFR_BANK2); 343b286484bSJiri Slaby u8 id = inb(baseio + MOXA_MUST_HWID_REGISTER); 344a6afd9f3SGreg Kroah-Hartman outb(oldlcr, baseio + UART_LCR); 345b286484bSJiri Slaby 346b286484bSJiri Slaby return id; 347a6afd9f3SGreg Kroah-Hartman } 348a6afd9f3SGreg Kroah-Hartman 349edb7d27cSJiri Slaby static void mxser_must_set_EFR(unsigned long baseio, u8 clear, u8 set) 350edb7d27cSJiri Slaby { 351edb7d27cSJiri Slaby __mxser_must_set_EFR(baseio, clear, set, true); 352edb7d27cSJiri Slaby } 353edb7d27cSJiri Slaby 354edb7d27cSJiri Slaby static void mxser_must_set_enhance_mode(unsigned long baseio, bool enable) 355edb7d27cSJiri Slaby { 356edb7d27cSJiri Slaby mxser_must_set_EFR(baseio, 357edb7d27cSJiri Slaby enable ? 0 : MOXA_MUST_EFR_EFRB_ENABLE, 358edb7d27cSJiri Slaby enable ? MOXA_MUST_EFR_EFRB_ENABLE : 0); 359edb7d27cSJiri Slaby } 360edb7d27cSJiri Slaby 361b441eb0fSJiri Slaby static void mxser_must_no_sw_flow_control(unsigned long baseio) 362a6afd9f3SGreg Kroah-Hartman { 363b441eb0fSJiri Slaby mxser_must_set_EFR(baseio, MOXA_MUST_EFR_SF_MASK, 0); 364a6afd9f3SGreg Kroah-Hartman } 365a6afd9f3SGreg Kroah-Hartman 366b441eb0fSJiri Slaby static void mxser_must_set_tx_sw_flow_control(unsigned long baseio, bool enable) 367a6afd9f3SGreg Kroah-Hartman { 368b441eb0fSJiri Slaby mxser_must_set_EFR(baseio, MOXA_MUST_EFR_SF_TX_MASK, 369b441eb0fSJiri Slaby enable ? MOXA_MUST_EFR_SF_TX1 : 0); 370a6afd9f3SGreg Kroah-Hartman } 371a6afd9f3SGreg Kroah-Hartman 372b441eb0fSJiri Slaby static void mxser_must_set_rx_sw_flow_control(unsigned long baseio, bool enable) 373a6afd9f3SGreg Kroah-Hartman { 374b441eb0fSJiri Slaby mxser_must_set_EFR(baseio, MOXA_MUST_EFR_SF_RX_MASK, 375b441eb0fSJiri Slaby enable ? MOXA_MUST_EFR_SF_RX1 : 0); 376a6afd9f3SGreg Kroah-Hartman } 377a6afd9f3SGreg Kroah-Hartman 378e4558366SJiri Slaby static enum mxser_must_hwid mxser_must_get_hwid(unsigned long io) 379a6afd9f3SGreg Kroah-Hartman { 380a6afd9f3SGreg Kroah-Hartman u8 oldmcr, hwid; 381a6afd9f3SGreg Kroah-Hartman int i; 382a6afd9f3SGreg Kroah-Hartman 383a6afd9f3SGreg Kroah-Hartman outb(0, io + UART_LCR); 384edb7d27cSJiri Slaby mxser_must_set_enhance_mode(io, false); 385a6afd9f3SGreg Kroah-Hartman oldmcr = inb(io + UART_MCR); 386a6afd9f3SGreg Kroah-Hartman outb(0, io + UART_MCR); 387a6afd9f3SGreg Kroah-Hartman mxser_set_must_xon1_value(io, 0x11); 388a6afd9f3SGreg Kroah-Hartman if ((hwid = inb(io + UART_MCR)) != 0) { 389a6afd9f3SGreg Kroah-Hartman outb(oldmcr, io + UART_MCR); 390a6afd9f3SGreg Kroah-Hartman return MOXA_OTHER_UART; 391a6afd9f3SGreg Kroah-Hartman } 392a6afd9f3SGreg Kroah-Hartman 393b286484bSJiri Slaby hwid = mxser_get_must_hardware_id(io); 394e4558366SJiri Slaby for (i = 1; i < UART_INFO_NUM; i++) /* 0 = OTHER_UART */ 395a6afd9f3SGreg Kroah-Hartman if (hwid == Gpci_uart_info[i].type) 396e4558366SJiri Slaby return hwid; 397e4558366SJiri Slaby 398a6afd9f3SGreg Kroah-Hartman return MOXA_OTHER_UART; 399a6afd9f3SGreg Kroah-Hartman } 400a6afd9f3SGreg Kroah-Hartman 4015d1ea1adSJiri Slaby static bool mxser_16550A_or_MUST(struct mxser_port *info) 4025d1ea1adSJiri Slaby { 4035d1ea1adSJiri Slaby return info->type == PORT_16550A || info->board->must_hwid; 4045d1ea1adSJiri Slaby } 4055d1ea1adSJiri Slaby 406c3db20c3SJiri Slaby static void mxser_process_txrx_fifo(struct mxser_port *info) 407a6afd9f3SGreg Kroah-Hartman { 408c3db20c3SJiri Slaby unsigned int i; 409a6afd9f3SGreg Kroah-Hartman 410c3db20c3SJiri Slaby if (info->type == PORT_16450 || info->type == PORT_8250) { 411a6afd9f3SGreg Kroah-Hartman info->rx_high_water = 1; 412a6afd9f3SGreg Kroah-Hartman info->rx_low_water = 1; 413a6afd9f3SGreg Kroah-Hartman info->xmit_fifo_size = 1; 414c3db20c3SJiri Slaby return; 415c3db20c3SJiri Slaby } 416c3db20c3SJiri Slaby 417a6afd9f3SGreg Kroah-Hartman for (i = 0; i < UART_INFO_NUM; i++) 418292955a7SJiri Slaby if (info->board->must_hwid == Gpci_uart_info[i].type) { 419a6afd9f3SGreg Kroah-Hartman info->rx_low_water = Gpci_uart_info[i].rx_low_water; 420a6afd9f3SGreg Kroah-Hartman info->rx_high_water = Gpci_uart_info[i].rx_high_water; 421dc33f644SJiri Slaby info->xmit_fifo_size = Gpci_uart_info[i].fifo_size; 422a6afd9f3SGreg Kroah-Hartman break; 423a6afd9f3SGreg Kroah-Hartman } 424a6afd9f3SGreg Kroah-Hartman } 425a6afd9f3SGreg Kroah-Hartman 426740165f7SJiri Slaby static void __mxser_start_tx(struct mxser_port *info) 427740165f7SJiri Slaby { 428740165f7SJiri Slaby outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); 429740165f7SJiri Slaby info->IER |= UART_IER_THRI; 430740165f7SJiri Slaby outb(info->IER, info->ioaddr + UART_IER); 431740165f7SJiri Slaby } 432740165f7SJiri Slaby 433740165f7SJiri Slaby static void mxser_start_tx(struct mxser_port *info) 434740165f7SJiri Slaby { 435740165f7SJiri Slaby unsigned long flags; 436740165f7SJiri Slaby 437740165f7SJiri Slaby spin_lock_irqsave(&info->slock, flags); 438740165f7SJiri Slaby __mxser_start_tx(info); 439740165f7SJiri Slaby spin_unlock_irqrestore(&info->slock, flags); 440740165f7SJiri Slaby } 441740165f7SJiri Slaby 442740165f7SJiri Slaby static void __mxser_stop_tx(struct mxser_port *info) 443740165f7SJiri Slaby { 444740165f7SJiri Slaby info->IER &= ~UART_IER_THRI; 445740165f7SJiri Slaby outb(info->IER, info->ioaddr + UART_IER); 446740165f7SJiri Slaby } 447740165f7SJiri Slaby 448a6afd9f3SGreg Kroah-Hartman static int mxser_carrier_raised(struct tty_port *port) 449a6afd9f3SGreg Kroah-Hartman { 450a6afd9f3SGreg Kroah-Hartman struct mxser_port *mp = container_of(port, struct mxser_port, port); 451a6afd9f3SGreg Kroah-Hartman return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0; 452a6afd9f3SGreg Kroah-Hartman } 453a6afd9f3SGreg Kroah-Hartman 454a6afd9f3SGreg Kroah-Hartman static void mxser_dtr_rts(struct tty_port *port, int on) 455a6afd9f3SGreg Kroah-Hartman { 456a6afd9f3SGreg Kroah-Hartman struct mxser_port *mp = container_of(port, struct mxser_port, port); 457a6afd9f3SGreg Kroah-Hartman unsigned long flags; 458007bbdc8SJiri Slaby u8 mcr; 459a6afd9f3SGreg Kroah-Hartman 460a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&mp->slock, flags); 461007bbdc8SJiri Slaby mcr = inb(mp->ioaddr + UART_MCR); 462a6afd9f3SGreg Kroah-Hartman if (on) 463007bbdc8SJiri Slaby mcr |= UART_MCR_DTR | UART_MCR_RTS; 464a6afd9f3SGreg Kroah-Hartman else 465007bbdc8SJiri Slaby mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); 466007bbdc8SJiri Slaby outb(mcr, mp->ioaddr + UART_MCR); 467a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&mp->slock, flags); 468a6afd9f3SGreg Kroah-Hartman } 469a6afd9f3SGreg Kroah-Hartman 470dc33f644SJiri Slaby static int mxser_set_baud(struct tty_struct *tty, speed_t newspd) 471a6afd9f3SGreg Kroah-Hartman { 472a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 473104583b5SJiri Slaby unsigned int quot = 0, baud; 474a6afd9f3SGreg Kroah-Hartman unsigned char cval; 475104583b5SJiri Slaby u64 timeout; 476a6afd9f3SGreg Kroah-Hartman 477928f9464SJiri Slaby if (newspd > info->board->max_baud) 478a6afd9f3SGreg Kroah-Hartman return -1; 479a6afd9f3SGreg Kroah-Hartman 480a6afd9f3SGreg Kroah-Hartman if (newspd == 134) { 481a6970c39SJiri Slaby quot = 2 * MXSER_BAUD_BASE / 269; 482a6afd9f3SGreg Kroah-Hartman tty_encode_baud_rate(tty, 134, 134); 483a6afd9f3SGreg Kroah-Hartman } else if (newspd) { 484a6970c39SJiri Slaby quot = MXSER_BAUD_BASE / newspd; 485a6afd9f3SGreg Kroah-Hartman if (quot == 0) 486a6afd9f3SGreg Kroah-Hartman quot = 1; 487a6970c39SJiri Slaby baud = MXSER_BAUD_BASE / quot; 488a6afd9f3SGreg Kroah-Hartman tty_encode_baud_rate(tty, baud, baud); 489a6afd9f3SGreg Kroah-Hartman } else { 490a6afd9f3SGreg Kroah-Hartman quot = 0; 491a6afd9f3SGreg Kroah-Hartman } 492a6afd9f3SGreg Kroah-Hartman 493104583b5SJiri Slaby /* 494104583b5SJiri Slaby * worst case (128 * 1000 * 10 * 18432) needs 35 bits, so divide in the 495104583b5SJiri Slaby * u64 domain 496104583b5SJiri Slaby */ 497104583b5SJiri Slaby timeout = (u64)info->xmit_fifo_size * HZ * 10 * quot; 498a6970c39SJiri Slaby do_div(timeout, MXSER_BAUD_BASE); 499104583b5SJiri Slaby info->timeout = timeout + HZ / 50; /* Add .02 seconds of slop */ 500a6afd9f3SGreg Kroah-Hartman 501a6afd9f3SGreg Kroah-Hartman if (quot) { 502a6afd9f3SGreg Kroah-Hartman info->MCR |= UART_MCR_DTR; 503a6afd9f3SGreg Kroah-Hartman outb(info->MCR, info->ioaddr + UART_MCR); 504a6afd9f3SGreg Kroah-Hartman } else { 505a6afd9f3SGreg Kroah-Hartman info->MCR &= ~UART_MCR_DTR; 506a6afd9f3SGreg Kroah-Hartman outb(info->MCR, info->ioaddr + UART_MCR); 507a6afd9f3SGreg Kroah-Hartman return 0; 508a6afd9f3SGreg Kroah-Hartman } 509a6afd9f3SGreg Kroah-Hartman 510a6afd9f3SGreg Kroah-Hartman cval = inb(info->ioaddr + UART_LCR); 511a6afd9f3SGreg Kroah-Hartman 512a6afd9f3SGreg Kroah-Hartman outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR); /* set DLAB */ 513a6afd9f3SGreg Kroah-Hartman 514a6afd9f3SGreg Kroah-Hartman outb(quot & 0xff, info->ioaddr + UART_DLL); /* LS of divisor */ 515a6afd9f3SGreg Kroah-Hartman outb(quot >> 8, info->ioaddr + UART_DLM); /* MS of divisor */ 516a6afd9f3SGreg Kroah-Hartman outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */ 517a6afd9f3SGreg Kroah-Hartman 518a6afd9f3SGreg Kroah-Hartman #ifdef BOTHER 519a6afd9f3SGreg Kroah-Hartman if (C_BAUD(tty) == BOTHER) { 520a6970c39SJiri Slaby quot = MXSER_BAUD_BASE % newspd; 521a6afd9f3SGreg Kroah-Hartman quot *= 8; 522a6afd9f3SGreg Kroah-Hartman if (quot % newspd > newspd / 2) { 523a6afd9f3SGreg Kroah-Hartman quot /= newspd; 524a6afd9f3SGreg Kroah-Hartman quot++; 525a6afd9f3SGreg Kroah-Hartman } else 526a6afd9f3SGreg Kroah-Hartman quot /= newspd; 527a6afd9f3SGreg Kroah-Hartman 528a6afd9f3SGreg Kroah-Hartman mxser_set_must_enum_value(info->ioaddr, quot); 529a6afd9f3SGreg Kroah-Hartman } else 530a6afd9f3SGreg Kroah-Hartman #endif 531a6afd9f3SGreg Kroah-Hartman mxser_set_must_enum_value(info->ioaddr, 0); 532a6afd9f3SGreg Kroah-Hartman 533a6afd9f3SGreg Kroah-Hartman return 0; 534a6afd9f3SGreg Kroah-Hartman } 535a6afd9f3SGreg Kroah-Hartman 536be486667SJiri Slaby static void mxser_handle_cts(struct tty_struct *tty, struct mxser_port *info, 537be486667SJiri Slaby u8 msr) 538be486667SJiri Slaby { 539be486667SJiri Slaby bool cts = msr & UART_MSR_CTS; 540be486667SJiri Slaby 541be486667SJiri Slaby if (tty->hw_stopped) { 542be486667SJiri Slaby if (cts) { 543be486667SJiri Slaby tty->hw_stopped = 0; 544be486667SJiri Slaby 5455d1ea1adSJiri Slaby if (!mxser_16550A_or_MUST(info)) 546740165f7SJiri Slaby __mxser_start_tx(info); 547be486667SJiri Slaby tty_wakeup(tty); 548be486667SJiri Slaby } 549be486667SJiri Slaby return; 550be486667SJiri Slaby } else if (cts) 551be486667SJiri Slaby return; 552be486667SJiri Slaby 553be486667SJiri Slaby tty->hw_stopped = 1; 5545d1ea1adSJiri Slaby if (!mxser_16550A_or_MUST(info)) 555740165f7SJiri Slaby __mxser_stop_tx(info); 556be486667SJiri Slaby } 557be486667SJiri Slaby 558a6afd9f3SGreg Kroah-Hartman /* 559a6afd9f3SGreg Kroah-Hartman * This routine is called to set the UART divisor registers to match 560a6afd9f3SGreg Kroah-Hartman * the specified baud rate for a serial port. 561a6afd9f3SGreg Kroah-Hartman */ 5623fdfa165SJiri Slaby static void mxser_change_speed(struct tty_struct *tty, struct ktermios *old_termios) 563a6afd9f3SGreg Kroah-Hartman { 564a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 565d249e662SJiri Slaby unsigned cflag, cval; 566a6afd9f3SGreg Kroah-Hartman 567adc8d746SAlan Cox cflag = tty->termios.c_cflag; 568a6afd9f3SGreg Kroah-Hartman 5693fdfa165SJiri Slaby if (mxser_set_baud(tty, tty_get_baud_rate(tty))) { 5703fdfa165SJiri Slaby /* Use previous rate on a failure */ 5713fdfa165SJiri Slaby if (old_termios) { 5723fdfa165SJiri Slaby speed_t baud = tty_termios_baud_rate(old_termios); 5733fdfa165SJiri Slaby tty_encode_baud_rate(tty, baud, baud); 5743fdfa165SJiri Slaby } 5753fdfa165SJiri Slaby } 576a6afd9f3SGreg Kroah-Hartman 577a6afd9f3SGreg Kroah-Hartman /* byte size and parity */ 578a6afd9f3SGreg Kroah-Hartman switch (cflag & CSIZE) { 5792c21832bSJiri Slaby default: 580a6afd9f3SGreg Kroah-Hartman case CS5: 5812c21832bSJiri Slaby cval = UART_LCR_WLEN5; 582a6afd9f3SGreg Kroah-Hartman break; 583a6afd9f3SGreg Kroah-Hartman case CS6: 5842c21832bSJiri Slaby cval = UART_LCR_WLEN6; 585a6afd9f3SGreg Kroah-Hartman break; 586a6afd9f3SGreg Kroah-Hartman case CS7: 5872c21832bSJiri Slaby cval = UART_LCR_WLEN7; 588a6afd9f3SGreg Kroah-Hartman break; 589a6afd9f3SGreg Kroah-Hartman case CS8: 5902c21832bSJiri Slaby cval = UART_LCR_WLEN8; 591a6afd9f3SGreg Kroah-Hartman break; 592a6afd9f3SGreg Kroah-Hartman } 5932c21832bSJiri Slaby 594a6afd9f3SGreg Kroah-Hartman if (cflag & CSTOPB) 5952c21832bSJiri Slaby cval |= UART_LCR_STOP; 596a6afd9f3SGreg Kroah-Hartman if (cflag & PARENB) 597a6afd9f3SGreg Kroah-Hartman cval |= UART_LCR_PARITY; 598a6afd9f3SGreg Kroah-Hartman if (!(cflag & PARODD)) 599a6afd9f3SGreg Kroah-Hartman cval |= UART_LCR_EPAR; 600a6afd9f3SGreg Kroah-Hartman if (cflag & CMSPAR) 601a6afd9f3SGreg Kroah-Hartman cval |= UART_LCR_SPAR; 602a6afd9f3SGreg Kroah-Hartman 603d249e662SJiri Slaby info->FCR = 0; 604292955a7SJiri Slaby if (info->board->must_hwid) { 605d249e662SJiri Slaby info->FCR |= UART_FCR_ENABLE_FIFO | 606bf1434c1SJiri Slaby MOXA_MUST_FCR_GDA_MODE_ENABLE; 607a6afd9f3SGreg Kroah-Hartman mxser_set_must_fifo_value(info); 608bf1434c1SJiri Slaby } else if (info->type != PORT_8250 && info->type != PORT_16450) { 609d249e662SJiri Slaby info->FCR |= UART_FCR_ENABLE_FIFO; 610dc33f644SJiri Slaby switch (info->rx_high_water) { 611a6afd9f3SGreg Kroah-Hartman case 1: 612d249e662SJiri Slaby info->FCR |= UART_FCR_TRIGGER_1; 613a6afd9f3SGreg Kroah-Hartman break; 614a6afd9f3SGreg Kroah-Hartman case 4: 615d249e662SJiri Slaby info->FCR |= UART_FCR_TRIGGER_4; 616a6afd9f3SGreg Kroah-Hartman break; 617a6afd9f3SGreg Kroah-Hartman case 8: 618d249e662SJiri Slaby info->FCR |= UART_FCR_TRIGGER_8; 619a6afd9f3SGreg Kroah-Hartman break; 620a6afd9f3SGreg Kroah-Hartman default: 621d249e662SJiri Slaby info->FCR |= UART_FCR_TRIGGER_14; 622a6afd9f3SGreg Kroah-Hartman break; 623a6afd9f3SGreg Kroah-Hartman } 624a6afd9f3SGreg Kroah-Hartman } 625a6afd9f3SGreg Kroah-Hartman 626a6afd9f3SGreg Kroah-Hartman /* CTS flow control flag and modem status interrupts */ 627a6afd9f3SGreg Kroah-Hartman info->IER &= ~UART_IER_MSI; 628a6afd9f3SGreg Kroah-Hartman info->MCR &= ~UART_MCR_AFE; 6295604a98eSPeter Hurley tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); 630a6afd9f3SGreg Kroah-Hartman if (cflag & CRTSCTS) { 631a6afd9f3SGreg Kroah-Hartman info->IER |= UART_IER_MSI; 6325d1ea1adSJiri Slaby if (mxser_16550A_or_MUST(info)) { 633a6afd9f3SGreg Kroah-Hartman info->MCR |= UART_MCR_AFE; 634a6afd9f3SGreg Kroah-Hartman } else { 635be486667SJiri Slaby mxser_handle_cts(tty, info, 636be486667SJiri Slaby inb(info->ioaddr + UART_MSR)); 637a6afd9f3SGreg Kroah-Hartman } 638a6afd9f3SGreg Kroah-Hartman } 639a6afd9f3SGreg Kroah-Hartman outb(info->MCR, info->ioaddr + UART_MCR); 6402d68655dSPeter Hurley tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); 6412d68655dSPeter Hurley if (~cflag & CLOCAL) 642a6afd9f3SGreg Kroah-Hartman info->IER |= UART_IER_MSI; 643a6afd9f3SGreg Kroah-Hartman outb(info->IER, info->ioaddr + UART_IER); 644a6afd9f3SGreg Kroah-Hartman 645a6afd9f3SGreg Kroah-Hartman /* 646a6afd9f3SGreg Kroah-Hartman * Set up parity check flag 647a6afd9f3SGreg Kroah-Hartman */ 648a6afd9f3SGreg Kroah-Hartman info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; 649a6afd9f3SGreg Kroah-Hartman if (I_INPCK(tty)) 650a6afd9f3SGreg Kroah-Hartman info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; 651a6afd9f3SGreg Kroah-Hartman if (I_BRKINT(tty) || I_PARMRK(tty)) 652a6afd9f3SGreg Kroah-Hartman info->read_status_mask |= UART_LSR_BI; 653a6afd9f3SGreg Kroah-Hartman 654a6afd9f3SGreg Kroah-Hartman info->ignore_status_mask = 0; 655a6afd9f3SGreg Kroah-Hartman 656a6afd9f3SGreg Kroah-Hartman if (I_IGNBRK(tty)) { 657a6afd9f3SGreg Kroah-Hartman info->ignore_status_mask |= UART_LSR_BI; 658a6afd9f3SGreg Kroah-Hartman info->read_status_mask |= UART_LSR_BI; 659a6afd9f3SGreg Kroah-Hartman /* 660a6afd9f3SGreg Kroah-Hartman * If we're ignore parity and break indicators, ignore 661a6afd9f3SGreg Kroah-Hartman * overruns too. (For real raw support). 662a6afd9f3SGreg Kroah-Hartman */ 663a6afd9f3SGreg Kroah-Hartman if (I_IGNPAR(tty)) { 664a6afd9f3SGreg Kroah-Hartman info->ignore_status_mask |= 665a6afd9f3SGreg Kroah-Hartman UART_LSR_OE | 666a6afd9f3SGreg Kroah-Hartman UART_LSR_PE | 667a6afd9f3SGreg Kroah-Hartman UART_LSR_FE; 668a6afd9f3SGreg Kroah-Hartman info->read_status_mask |= 669a6afd9f3SGreg Kroah-Hartman UART_LSR_OE | 670a6afd9f3SGreg Kroah-Hartman UART_LSR_PE | 671a6afd9f3SGreg Kroah-Hartman UART_LSR_FE; 672a6afd9f3SGreg Kroah-Hartman } 673a6afd9f3SGreg Kroah-Hartman } 674292955a7SJiri Slaby if (info->board->must_hwid) { 675a6afd9f3SGreg Kroah-Hartman mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty)); 676a6afd9f3SGreg Kroah-Hartman mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty)); 677b441eb0fSJiri Slaby mxser_must_set_rx_sw_flow_control(info->ioaddr, I_IXON(tty)); 678b441eb0fSJiri Slaby mxser_must_set_tx_sw_flow_control(info->ioaddr, I_IXOFF(tty)); 679a6afd9f3SGreg Kroah-Hartman } 680a6afd9f3SGreg Kroah-Hartman 681a6afd9f3SGreg Kroah-Hartman 682d249e662SJiri Slaby outb(info->FCR, info->ioaddr + UART_FCR); 683a6afd9f3SGreg Kroah-Hartman outb(cval, info->ioaddr + UART_LCR); 684a6afd9f3SGreg Kroah-Hartman } 685a6afd9f3SGreg Kroah-Hartman 68630f6027fSJiri Slaby static u8 mxser_check_modem_status(struct tty_struct *tty, 68730f6027fSJiri Slaby struct mxser_port *port) 688a6afd9f3SGreg Kroah-Hartman { 68930f6027fSJiri Slaby u8 msr = inb(port->ioaddr + UART_MSR); 69030f6027fSJiri Slaby 69130f6027fSJiri Slaby if (!(msr & UART_MSR_ANY_DELTA)) 69230f6027fSJiri Slaby return msr; 69330f6027fSJiri Slaby 694a6afd9f3SGreg Kroah-Hartman /* update input line counters */ 69530f6027fSJiri Slaby if (msr & UART_MSR_TERI) 696a6afd9f3SGreg Kroah-Hartman port->icount.rng++; 69730f6027fSJiri Slaby if (msr & UART_MSR_DDSR) 698a6afd9f3SGreg Kroah-Hartman port->icount.dsr++; 69930f6027fSJiri Slaby if (msr & UART_MSR_DDCD) 700a6afd9f3SGreg Kroah-Hartman port->icount.dcd++; 70130f6027fSJiri Slaby if (msr & UART_MSR_DCTS) 702a6afd9f3SGreg Kroah-Hartman port->icount.cts++; 703a6afd9f3SGreg Kroah-Hartman wake_up_interruptible(&port->port.delta_msr_wait); 704a6afd9f3SGreg Kroah-Hartman 70530f6027fSJiri Slaby if (tty_port_check_carrier(&port->port) && (msr & UART_MSR_DDCD)) { 70630f6027fSJiri Slaby if (msr & UART_MSR_DCD) 707a6afd9f3SGreg Kroah-Hartman wake_up_interruptible(&port->port.open_wait); 708a6afd9f3SGreg Kroah-Hartman } 709a6afd9f3SGreg Kroah-Hartman 710be486667SJiri Slaby if (tty_port_cts_enabled(&port->port)) 71130f6027fSJiri Slaby mxser_handle_cts(tty, port, msr); 71230f6027fSJiri Slaby 71330f6027fSJiri Slaby return msr; 714a6afd9f3SGreg Kroah-Hartman } 715a6afd9f3SGreg Kroah-Hartman 716ee7e5e66SJiri Slaby static void mxser_disable_and_clear_FIFO(struct mxser_port *info) 717ee7e5e66SJiri Slaby { 718ee7e5e66SJiri Slaby u8 fcr = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; 719ee7e5e66SJiri Slaby 720ee7e5e66SJiri Slaby if (info->board->must_hwid) 721ee7e5e66SJiri Slaby fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; 722ee7e5e66SJiri Slaby 723ee7e5e66SJiri Slaby outb(fcr, info->ioaddr + UART_FCR); 724ee7e5e66SJiri Slaby } 725ee7e5e66SJiri Slaby 726a6afd9f3SGreg Kroah-Hartman static int mxser_activate(struct tty_port *port, struct tty_struct *tty) 727a6afd9f3SGreg Kroah-Hartman { 728a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = container_of(port, struct mxser_port, port); 729a6afd9f3SGreg Kroah-Hartman unsigned long page; 730a6afd9f3SGreg Kroah-Hartman unsigned long flags; 731a6afd9f3SGreg Kroah-Hartman 732a6afd9f3SGreg Kroah-Hartman page = __get_free_page(GFP_KERNEL); 733a6afd9f3SGreg Kroah-Hartman if (!page) 734a6afd9f3SGreg Kroah-Hartman return -ENOMEM; 735a6afd9f3SGreg Kroah-Hartman 736a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 737a6afd9f3SGreg Kroah-Hartman 738987a4cfeSJiri Slaby if (!info->type) { 739a6afd9f3SGreg Kroah-Hartman set_bit(TTY_IO_ERROR, &tty->flags); 740a6afd9f3SGreg Kroah-Hartman free_page(page); 741a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 742a6afd9f3SGreg Kroah-Hartman return 0; 743a6afd9f3SGreg Kroah-Hartman } 744a6afd9f3SGreg Kroah-Hartman info->port.xmit_buf = (unsigned char *) page; 745a6afd9f3SGreg Kroah-Hartman 746a6afd9f3SGreg Kroah-Hartman /* 747a6afd9f3SGreg Kroah-Hartman * Clear the FIFO buffers and disable them 748a6afd9f3SGreg Kroah-Hartman * (they will be reenabled in mxser_change_speed()) 749a6afd9f3SGreg Kroah-Hartman */ 750ee7e5e66SJiri Slaby mxser_disable_and_clear_FIFO(info); 751a6afd9f3SGreg Kroah-Hartman 752a6afd9f3SGreg Kroah-Hartman /* 753a6afd9f3SGreg Kroah-Hartman * At this point there's no way the LSR could still be 0xFF; 754a6afd9f3SGreg Kroah-Hartman * if it is, then bail out, because there's likely no UART 755a6afd9f3SGreg Kroah-Hartman * here. 756a6afd9f3SGreg Kroah-Hartman */ 757a6afd9f3SGreg Kroah-Hartman if (inb(info->ioaddr + UART_LSR) == 0xff) { 758a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 759a6afd9f3SGreg Kroah-Hartman if (capable(CAP_SYS_ADMIN)) { 760a6afd9f3SGreg Kroah-Hartman set_bit(TTY_IO_ERROR, &tty->flags); 761a6afd9f3SGreg Kroah-Hartman return 0; 762a6afd9f3SGreg Kroah-Hartman } else 763a6afd9f3SGreg Kroah-Hartman return -ENODEV; 764a6afd9f3SGreg Kroah-Hartman } 765a6afd9f3SGreg Kroah-Hartman 766a6afd9f3SGreg Kroah-Hartman /* 767a6afd9f3SGreg Kroah-Hartman * Clear the interrupt registers. 768a6afd9f3SGreg Kroah-Hartman */ 769a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_LSR); 770a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_RX); 771a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_IIR); 772a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_MSR); 773a6afd9f3SGreg Kroah-Hartman 774a6afd9f3SGreg Kroah-Hartman /* 775a6afd9f3SGreg Kroah-Hartman * Now, initialize the UART 776a6afd9f3SGreg Kroah-Hartman */ 777a6afd9f3SGreg Kroah-Hartman outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR); /* reset DLAB */ 778a6afd9f3SGreg Kroah-Hartman info->MCR = UART_MCR_DTR | UART_MCR_RTS; 779a6afd9f3SGreg Kroah-Hartman outb(info->MCR, info->ioaddr + UART_MCR); 780a6afd9f3SGreg Kroah-Hartman 781a6afd9f3SGreg Kroah-Hartman /* 782a6afd9f3SGreg Kroah-Hartman * Finally, enable interrupts 783a6afd9f3SGreg Kroah-Hartman */ 784a6afd9f3SGreg Kroah-Hartman info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; 785a6afd9f3SGreg Kroah-Hartman 786292955a7SJiri Slaby if (info->board->must_hwid) 787a6afd9f3SGreg Kroah-Hartman info->IER |= MOXA_MUST_IER_EGDAI; 788a6afd9f3SGreg Kroah-Hartman outb(info->IER, info->ioaddr + UART_IER); /* enable interrupts */ 789a6afd9f3SGreg Kroah-Hartman 790a6afd9f3SGreg Kroah-Hartman /* 791a6afd9f3SGreg Kroah-Hartman * And clear the interrupt registers again for luck. 792a6afd9f3SGreg Kroah-Hartman */ 793a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_LSR); 794a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_RX); 795a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_IIR); 796a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_MSR); 797a6afd9f3SGreg Kroah-Hartman 798a6afd9f3SGreg Kroah-Hartman clear_bit(TTY_IO_ERROR, &tty->flags); 799a6afd9f3SGreg Kroah-Hartman info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; 800a6afd9f3SGreg Kroah-Hartman 801a6afd9f3SGreg Kroah-Hartman /* 802a6afd9f3SGreg Kroah-Hartman * and set the speed of the serial port 803a6afd9f3SGreg Kroah-Hartman */ 8043fdfa165SJiri Slaby mxser_change_speed(tty, NULL); 805a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 806a6afd9f3SGreg Kroah-Hartman 807a6afd9f3SGreg Kroah-Hartman return 0; 808a6afd9f3SGreg Kroah-Hartman } 809a6afd9f3SGreg Kroah-Hartman 810a6afd9f3SGreg Kroah-Hartman /* 81147b722d4SJiri Slaby * To stop accepting input, we disable the receive line status interrupts, and 81247b722d4SJiri Slaby * tell the interrupt driver to stop checking the data ready bit in the line 81347b722d4SJiri Slaby * status register. 81447b722d4SJiri Slaby */ 81547b722d4SJiri Slaby static void mxser_stop_rx(struct mxser_port *info) 81647b722d4SJiri Slaby { 81747b722d4SJiri Slaby info->IER &= ~UART_IER_RLSI; 81847b722d4SJiri Slaby if (info->board->must_hwid) 81947b722d4SJiri Slaby info->IER &= ~MOXA_MUST_RECV_ISR; 82047b722d4SJiri Slaby 82147b722d4SJiri Slaby outb(info->IER, info->ioaddr + UART_IER); 82247b722d4SJiri Slaby } 82347b722d4SJiri Slaby 82447b722d4SJiri Slaby /* 825a6afd9f3SGreg Kroah-Hartman * This routine will shutdown a serial port 826a6afd9f3SGreg Kroah-Hartman */ 827a6afd9f3SGreg Kroah-Hartman static void mxser_shutdown_port(struct tty_port *port) 828a6afd9f3SGreg Kroah-Hartman { 829a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = container_of(port, struct mxser_port, port); 830a6afd9f3SGreg Kroah-Hartman unsigned long flags; 831a6afd9f3SGreg Kroah-Hartman 832a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 833a6afd9f3SGreg Kroah-Hartman 83447b722d4SJiri Slaby mxser_stop_rx(info); 83547b722d4SJiri Slaby 836a6afd9f3SGreg Kroah-Hartman /* 837a6afd9f3SGreg Kroah-Hartman * clear delta_msr_wait queue to avoid mem leaks: we may free the irq 838a6afd9f3SGreg Kroah-Hartman * here so the queue might never be waken up 839a6afd9f3SGreg Kroah-Hartman */ 840a6afd9f3SGreg Kroah-Hartman wake_up_interruptible(&info->port.delta_msr_wait); 841a6afd9f3SGreg Kroah-Hartman 842a6afd9f3SGreg Kroah-Hartman /* 843a6afd9f3SGreg Kroah-Hartman * Free the xmit buffer, if necessary 844a6afd9f3SGreg Kroah-Hartman */ 845a6afd9f3SGreg Kroah-Hartman if (info->port.xmit_buf) { 846a6afd9f3SGreg Kroah-Hartman free_page((unsigned long) info->port.xmit_buf); 847a6afd9f3SGreg Kroah-Hartman info->port.xmit_buf = NULL; 848a6afd9f3SGreg Kroah-Hartman } 849a6afd9f3SGreg Kroah-Hartman 850a6afd9f3SGreg Kroah-Hartman info->IER = 0; 851a6afd9f3SGreg Kroah-Hartman outb(0x00, info->ioaddr + UART_IER); 852a6afd9f3SGreg Kroah-Hartman 853a6afd9f3SGreg Kroah-Hartman /* clear Rx/Tx FIFO's */ 854ee7e5e66SJiri Slaby mxser_disable_and_clear_FIFO(info); 855a6afd9f3SGreg Kroah-Hartman 856a6afd9f3SGreg Kroah-Hartman /* read data port to reset things */ 857a6afd9f3SGreg Kroah-Hartman (void) inb(info->ioaddr + UART_RX); 858a6afd9f3SGreg Kroah-Hartman 859a6afd9f3SGreg Kroah-Hartman 860292955a7SJiri Slaby if (info->board->must_hwid) 861b441eb0fSJiri Slaby mxser_must_no_sw_flow_control(info->ioaddr); 862a6afd9f3SGreg Kroah-Hartman 863a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 864a6afd9f3SGreg Kroah-Hartman } 865a6afd9f3SGreg Kroah-Hartman 866a6afd9f3SGreg Kroah-Hartman /* 867a6afd9f3SGreg Kroah-Hartman * This routine is called whenever a serial port is opened. It 868a6afd9f3SGreg Kroah-Hartman * enables interrupts for a serial port, linking in its async structure into 869a6afd9f3SGreg Kroah-Hartman * the IRQ chain. It also performs the serial-specific 870a6afd9f3SGreg Kroah-Hartman * initialization for the tty structure. 871a6afd9f3SGreg Kroah-Hartman */ 872a6afd9f3SGreg Kroah-Hartman static int mxser_open(struct tty_struct *tty, struct file *filp) 873a6afd9f3SGreg Kroah-Hartman { 87442ad25fcSJiri Slaby struct tty_port *tport = tty->port; 87542ad25fcSJiri Slaby struct mxser_port *port = container_of(tport, struct mxser_port, port); 876a6afd9f3SGreg Kroah-Hartman 87742ad25fcSJiri Slaby tty->driver_data = port; 878a6afd9f3SGreg Kroah-Hartman 87942ad25fcSJiri Slaby return tty_port_open(tport, tty, filp); 880a6afd9f3SGreg Kroah-Hartman } 881a6afd9f3SGreg Kroah-Hartman 882a6afd9f3SGreg Kroah-Hartman static void mxser_flush_buffer(struct tty_struct *tty) 883a6afd9f3SGreg Kroah-Hartman { 884a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 885a6afd9f3SGreg Kroah-Hartman unsigned long flags; 886a6afd9f3SGreg Kroah-Hartman 887a6afd9f3SGreg Kroah-Hartman 888a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 889a6afd9f3SGreg Kroah-Hartman info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; 890a6afd9f3SGreg Kroah-Hartman 891d249e662SJiri Slaby outb(info->FCR | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, 892a6afd9f3SGreg Kroah-Hartman info->ioaddr + UART_FCR); 893a6afd9f3SGreg Kroah-Hartman 894a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 895a6afd9f3SGreg Kroah-Hartman 896a6afd9f3SGreg Kroah-Hartman tty_wakeup(tty); 897a6afd9f3SGreg Kroah-Hartman } 898a6afd9f3SGreg Kroah-Hartman 899a6afd9f3SGreg Kroah-Hartman static void mxser_close(struct tty_struct *tty, struct file *filp) 900a6afd9f3SGreg Kroah-Hartman { 901c7ec012fSJiri Slaby tty_port_close(tty->port, tty, filp); 902a6afd9f3SGreg Kroah-Hartman } 903a6afd9f3SGreg Kroah-Hartman 904a6afd9f3SGreg Kroah-Hartman static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count) 905a6afd9f3SGreg Kroah-Hartman { 906a6afd9f3SGreg Kroah-Hartman int c, total = 0; 907a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 908a6afd9f3SGreg Kroah-Hartman unsigned long flags; 909a6afd9f3SGreg Kroah-Hartman 910a6afd9f3SGreg Kroah-Hartman while (1) { 911a6afd9f3SGreg Kroah-Hartman c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, 912a6afd9f3SGreg Kroah-Hartman SERIAL_XMIT_SIZE - info->xmit_head)); 913a6afd9f3SGreg Kroah-Hartman if (c <= 0) 914a6afd9f3SGreg Kroah-Hartman break; 915a6afd9f3SGreg Kroah-Hartman 916a6afd9f3SGreg Kroah-Hartman memcpy(info->port.xmit_buf + info->xmit_head, buf, c); 917a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 918a6afd9f3SGreg Kroah-Hartman info->xmit_head = (info->xmit_head + c) & 919a6afd9f3SGreg Kroah-Hartman (SERIAL_XMIT_SIZE - 1); 920a6afd9f3SGreg Kroah-Hartman info->xmit_cnt += c; 921a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 922a6afd9f3SGreg Kroah-Hartman 923a6afd9f3SGreg Kroah-Hartman buf += c; 924a6afd9f3SGreg Kroah-Hartman count -= c; 925a6afd9f3SGreg Kroah-Hartman total += c; 926a6afd9f3SGreg Kroah-Hartman } 927a6afd9f3SGreg Kroah-Hartman 9285d1ea1adSJiri Slaby if (info->xmit_cnt && !tty->flow.stopped) 9295d1ea1adSJiri Slaby if (!tty->hw_stopped || mxser_16550A_or_MUST(info)) 930740165f7SJiri Slaby mxser_start_tx(info); 9315d1ea1adSJiri Slaby 932a6afd9f3SGreg Kroah-Hartman return total; 933a6afd9f3SGreg Kroah-Hartman } 934a6afd9f3SGreg Kroah-Hartman 935a6afd9f3SGreg Kroah-Hartman static int mxser_put_char(struct tty_struct *tty, unsigned char ch) 936a6afd9f3SGreg Kroah-Hartman { 937a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 938a6afd9f3SGreg Kroah-Hartman unsigned long flags; 939a6afd9f3SGreg Kroah-Hartman 940a6afd9f3SGreg Kroah-Hartman if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) 941a6afd9f3SGreg Kroah-Hartman return 0; 942a6afd9f3SGreg Kroah-Hartman 943a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 944a6afd9f3SGreg Kroah-Hartman info->port.xmit_buf[info->xmit_head++] = ch; 945a6afd9f3SGreg Kroah-Hartman info->xmit_head &= SERIAL_XMIT_SIZE - 1; 946a6afd9f3SGreg Kroah-Hartman info->xmit_cnt++; 947a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 9488aff64e0SJiri Slaby 949a6afd9f3SGreg Kroah-Hartman return 1; 950a6afd9f3SGreg Kroah-Hartman } 951a6afd9f3SGreg Kroah-Hartman 952a6afd9f3SGreg Kroah-Hartman 953a6afd9f3SGreg Kroah-Hartman static void mxser_flush_chars(struct tty_struct *tty) 954a6afd9f3SGreg Kroah-Hartman { 955a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 956a6afd9f3SGreg Kroah-Hartman 9575c338fbfSJiri Slaby if (!info->xmit_cnt || tty->flow.stopped || 9585d1ea1adSJiri Slaby (tty->hw_stopped && !mxser_16550A_or_MUST(info))) 959a6afd9f3SGreg Kroah-Hartman return; 960a6afd9f3SGreg Kroah-Hartman 961740165f7SJiri Slaby mxser_start_tx(info); 962a6afd9f3SGreg Kroah-Hartman } 963a6afd9f3SGreg Kroah-Hartman 96403b3b1a2SJiri Slaby static unsigned int mxser_write_room(struct tty_struct *tty) 965a6afd9f3SGreg Kroah-Hartman { 966a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 967a6afd9f3SGreg Kroah-Hartman int ret; 968a6afd9f3SGreg Kroah-Hartman 969a6afd9f3SGreg Kroah-Hartman ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; 970a6afd9f3SGreg Kroah-Hartman return ret < 0 ? 0 : ret; 971a6afd9f3SGreg Kroah-Hartman } 972a6afd9f3SGreg Kroah-Hartman 973fff4ef17SJiri Slaby static unsigned int mxser_chars_in_buffer(struct tty_struct *tty) 974a6afd9f3SGreg Kroah-Hartman { 975a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 976a6afd9f3SGreg Kroah-Hartman return info->xmit_cnt; 977a6afd9f3SGreg Kroah-Hartman } 978a6afd9f3SGreg Kroah-Hartman 979a6afd9f3SGreg Kroah-Hartman /* 980a6afd9f3SGreg Kroah-Hartman * ------------------------------------------------------------ 981a6afd9f3SGreg Kroah-Hartman * friends of mxser_ioctl() 982a6afd9f3SGreg Kroah-Hartman * ------------------------------------------------------------ 983a6afd9f3SGreg Kroah-Hartman */ 984a6afd9f3SGreg Kroah-Hartman static int mxser_get_serial_info(struct tty_struct *tty, 9856da5b587SAl Viro struct serial_struct *ss) 986a6afd9f3SGreg Kroah-Hartman { 987a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 9886da5b587SAl Viro struct tty_port *port = &info->port; 989be6cf583SJohan Hovold unsigned int closing_wait, close_delay; 9906da5b587SAl Viro 9916da5b587SAl Viro mutex_lock(&port->mutex); 992be6cf583SJohan Hovold 993be6cf583SJohan Hovold close_delay = jiffies_to_msecs(info->port.close_delay) / 10; 994be6cf583SJohan Hovold closing_wait = info->port.closing_wait; 995be6cf583SJohan Hovold if (closing_wait != ASYNC_CLOSING_WAIT_NONE) 996be6cf583SJohan Hovold closing_wait = jiffies_to_msecs(closing_wait) / 10; 997be6cf583SJohan Hovold 9982285c496SDan Carpenter ss->type = info->type; 9992285c496SDan Carpenter ss->line = tty->index; 10002285c496SDan Carpenter ss->port = info->ioaddr; 10012285c496SDan Carpenter ss->irq = info->board->irq; 10022285c496SDan Carpenter ss->flags = info->port.flags; 10032285c496SDan Carpenter ss->baud_base = MXSER_BAUD_BASE; 1004be6cf583SJohan Hovold ss->close_delay = close_delay; 1005be6cf583SJohan Hovold ss->closing_wait = closing_wait; 1006d811b26bSJiri Slaby ss->custom_divisor = MXSER_CUSTOM_DIVISOR, 10076da5b587SAl Viro mutex_unlock(&port->mutex); 1008a6afd9f3SGreg Kroah-Hartman return 0; 1009a6afd9f3SGreg Kroah-Hartman } 1010a6afd9f3SGreg Kroah-Hartman 1011a6afd9f3SGreg Kroah-Hartman static int mxser_set_serial_info(struct tty_struct *tty, 10126da5b587SAl Viro struct serial_struct *ss) 1013a6afd9f3SGreg Kroah-Hartman { 1014a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1015a6afd9f3SGreg Kroah-Hartman struct tty_port *port = &info->port; 1016a6afd9f3SGreg Kroah-Hartman speed_t baud; 1017a6afd9f3SGreg Kroah-Hartman unsigned long sl_flags; 101806cc52efSJiri Slaby unsigned int old_speed, close_delay, closing_wait; 1019a6afd9f3SGreg Kroah-Hartman int retval = 0; 1020a6afd9f3SGreg Kroah-Hartman 10216da5b587SAl Viro if (tty_io_error(tty)) 10226da5b587SAl Viro return -EIO; 1023a6afd9f3SGreg Kroah-Hartman 10246da5b587SAl Viro mutex_lock(&port->mutex); 10256da5b587SAl Viro 10266da5b587SAl Viro if (ss->irq != info->board->irq || 10276da5b587SAl Viro ss->port != info->ioaddr) { 10286da5b587SAl Viro mutex_unlock(&port->mutex); 1029a6afd9f3SGreg Kroah-Hartman return -EINVAL; 10306da5b587SAl Viro } 1031a6afd9f3SGreg Kroah-Hartman 103206cc52efSJiri Slaby old_speed = port->flags & ASYNC_SPD_MASK; 1033a6afd9f3SGreg Kroah-Hartman 1034be6cf583SJohan Hovold close_delay = msecs_to_jiffies(ss->close_delay * 10); 1035be6cf583SJohan Hovold closing_wait = ss->closing_wait; 1036be6cf583SJohan Hovold if (closing_wait != ASYNC_CLOSING_WAIT_NONE) 1037be6cf583SJohan Hovold closing_wait = msecs_to_jiffies(closing_wait * 10); 1038be6cf583SJohan Hovold 1039a6afd9f3SGreg Kroah-Hartman if (!capable(CAP_SYS_ADMIN)) { 1040a6970c39SJiri Slaby if ((ss->baud_base != MXSER_BAUD_BASE) || 10411b3086b6SJiri Slaby (close_delay != port->close_delay) || 10421b3086b6SJiri Slaby (closing_wait != port->closing_wait) || 10431b3086b6SJiri Slaby ((ss->flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { 10446da5b587SAl Viro mutex_unlock(&port->mutex); 1045a6afd9f3SGreg Kroah-Hartman return -EPERM; 10466da5b587SAl Viro } 10471b3086b6SJiri Slaby port->flags = (port->flags & ~ASYNC_USR_MASK) | 10481b3086b6SJiri Slaby (ss->flags & ASYNC_USR_MASK); 1049a6afd9f3SGreg Kroah-Hartman } else { 1050a6afd9f3SGreg Kroah-Hartman /* 1051a6afd9f3SGreg Kroah-Hartman * OK, past this point, all the error checking has been done. 1052a6afd9f3SGreg Kroah-Hartman * At this point, we start making changes..... 1053a6afd9f3SGreg Kroah-Hartman */ 1054a6afd9f3SGreg Kroah-Hartman port->flags = ((port->flags & ~ASYNC_FLAGS) | 10556da5b587SAl Viro (ss->flags & ASYNC_FLAGS)); 1056be6cf583SJohan Hovold port->close_delay = close_delay; 1057be6cf583SJohan Hovold port->closing_wait = closing_wait; 1058a6afd9f3SGreg Kroah-Hartman if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && 1059a6970c39SJiri Slaby (ss->baud_base != MXSER_BAUD_BASE || 10606da5b587SAl Viro ss->custom_divisor != 1061d811b26bSJiri Slaby MXSER_CUSTOM_DIVISOR)) { 10626da5b587SAl Viro if (ss->custom_divisor == 0) { 10636da5b587SAl Viro mutex_unlock(&port->mutex); 1064a6afd9f3SGreg Kroah-Hartman return -EINVAL; 10656da5b587SAl Viro } 10666da5b587SAl Viro baud = ss->baud_base / ss->custom_divisor; 1067a6afd9f3SGreg Kroah-Hartman tty_encode_baud_rate(tty, baud, baud); 1068a6afd9f3SGreg Kroah-Hartman } 1069a6afd9f3SGreg Kroah-Hartman 10706da5b587SAl Viro info->type = ss->type; 1071a6afd9f3SGreg Kroah-Hartman 1072c3db20c3SJiri Slaby mxser_process_txrx_fifo(info); 1073b91cfb25SJohan Hovold } 1074a6afd9f3SGreg Kroah-Hartman 1075d41861caSPeter Hurley if (tty_port_initialized(port)) { 107606cc52efSJiri Slaby if (old_speed != (port->flags & ASYNC_SPD_MASK)) { 1077a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, sl_flags); 10783fdfa165SJiri Slaby mxser_change_speed(tty, NULL); 1079a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, sl_flags); 1080a6afd9f3SGreg Kroah-Hartman } 1081a6afd9f3SGreg Kroah-Hartman } else { 1082a6afd9f3SGreg Kroah-Hartman retval = mxser_activate(port, tty); 1083a6afd9f3SGreg Kroah-Hartman if (retval == 0) 1084d41861caSPeter Hurley tty_port_set_initialized(port, 1); 1085a6afd9f3SGreg Kroah-Hartman } 10866da5b587SAl Viro mutex_unlock(&port->mutex); 1087a6afd9f3SGreg Kroah-Hartman return retval; 1088a6afd9f3SGreg Kroah-Hartman } 1089a6afd9f3SGreg Kroah-Hartman 1090a6afd9f3SGreg Kroah-Hartman /* 1091a6afd9f3SGreg Kroah-Hartman * mxser_get_lsr_info - get line status register info 1092a6afd9f3SGreg Kroah-Hartman * 1093a6afd9f3SGreg Kroah-Hartman * Purpose: Let user call ioctl() to get info when the UART physically 1094a6afd9f3SGreg Kroah-Hartman * is emptied. On bus types like RS485, the transmitter must 1095a6afd9f3SGreg Kroah-Hartman * release the bus after transmitting. This must be done when 1096a6afd9f3SGreg Kroah-Hartman * the transmit shift register is empty, not be done when the 1097a6afd9f3SGreg Kroah-Hartman * transmit holding register is empty. This functionality 1098a6afd9f3SGreg Kroah-Hartman * allows an RS485 driver to be written in user space. 1099a6afd9f3SGreg Kroah-Hartman */ 1100a6afd9f3SGreg Kroah-Hartman static int mxser_get_lsr_info(struct mxser_port *info, 1101a6afd9f3SGreg Kroah-Hartman unsigned int __user *value) 1102a6afd9f3SGreg Kroah-Hartman { 1103a6afd9f3SGreg Kroah-Hartman unsigned char status; 1104a6afd9f3SGreg Kroah-Hartman unsigned int result; 1105a6afd9f3SGreg Kroah-Hartman unsigned long flags; 1106a6afd9f3SGreg Kroah-Hartman 1107a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 1108a6afd9f3SGreg Kroah-Hartman status = inb(info->ioaddr + UART_LSR); 1109a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1110a6afd9f3SGreg Kroah-Hartman result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); 1111a6afd9f3SGreg Kroah-Hartman return put_user(result, value); 1112a6afd9f3SGreg Kroah-Hartman } 1113a6afd9f3SGreg Kroah-Hartman 1114a6afd9f3SGreg Kroah-Hartman static int mxser_tiocmget(struct tty_struct *tty) 1115a6afd9f3SGreg Kroah-Hartman { 1116a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 111730f6027fSJiri Slaby unsigned char control; 1118a6afd9f3SGreg Kroah-Hartman unsigned long flags; 111930f6027fSJiri Slaby u8 msr; 1120a6afd9f3SGreg Kroah-Hartman 112118900ca6SPeter Hurley if (tty_io_error(tty)) 1122a6afd9f3SGreg Kroah-Hartman return -EIO; 1123a6afd9f3SGreg Kroah-Hartman 1124a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 1125202acdaaSJiri Slaby control = info->MCR; 112630f6027fSJiri Slaby msr = mxser_check_modem_status(tty, info); 1127a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1128202acdaaSJiri Slaby 1129a6afd9f3SGreg Kroah-Hartman return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | 1130a6afd9f3SGreg Kroah-Hartman ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | 113130f6027fSJiri Slaby ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) | 113230f6027fSJiri Slaby ((msr & UART_MSR_RI) ? TIOCM_RNG : 0) | 113330f6027fSJiri Slaby ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0) | 113430f6027fSJiri Slaby ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0); 1135a6afd9f3SGreg Kroah-Hartman } 1136a6afd9f3SGreg Kroah-Hartman 1137a6afd9f3SGreg Kroah-Hartman static int mxser_tiocmset(struct tty_struct *tty, 1138a6afd9f3SGreg Kroah-Hartman unsigned int set, unsigned int clear) 1139a6afd9f3SGreg Kroah-Hartman { 1140a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1141a6afd9f3SGreg Kroah-Hartman unsigned long flags; 1142a6afd9f3SGreg Kroah-Hartman 114318900ca6SPeter Hurley if (tty_io_error(tty)) 1144a6afd9f3SGreg Kroah-Hartman return -EIO; 1145a6afd9f3SGreg Kroah-Hartman 1146a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 1147a6afd9f3SGreg Kroah-Hartman 1148a6afd9f3SGreg Kroah-Hartman if (set & TIOCM_RTS) 1149a6afd9f3SGreg Kroah-Hartman info->MCR |= UART_MCR_RTS; 1150a6afd9f3SGreg Kroah-Hartman if (set & TIOCM_DTR) 1151a6afd9f3SGreg Kroah-Hartman info->MCR |= UART_MCR_DTR; 1152a6afd9f3SGreg Kroah-Hartman 1153a6afd9f3SGreg Kroah-Hartman if (clear & TIOCM_RTS) 1154a6afd9f3SGreg Kroah-Hartman info->MCR &= ~UART_MCR_RTS; 1155a6afd9f3SGreg Kroah-Hartman if (clear & TIOCM_DTR) 1156a6afd9f3SGreg Kroah-Hartman info->MCR &= ~UART_MCR_DTR; 1157a6afd9f3SGreg Kroah-Hartman 1158a6afd9f3SGreg Kroah-Hartman outb(info->MCR, info->ioaddr + UART_MCR); 1159a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1160a6afd9f3SGreg Kroah-Hartman return 0; 1161a6afd9f3SGreg Kroah-Hartman } 1162a6afd9f3SGreg Kroah-Hartman 1163a6afd9f3SGreg Kroah-Hartman static int mxser_cflags_changed(struct mxser_port *info, unsigned long arg, 1164a6afd9f3SGreg Kroah-Hartman struct async_icount *cprev) 1165a6afd9f3SGreg Kroah-Hartman { 1166a6afd9f3SGreg Kroah-Hartman struct async_icount cnow; 1167a6afd9f3SGreg Kroah-Hartman unsigned long flags; 1168a6afd9f3SGreg Kroah-Hartman int ret; 1169a6afd9f3SGreg Kroah-Hartman 1170a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 1171a6afd9f3SGreg Kroah-Hartman cnow = info->icount; /* atomic copy */ 1172a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1173a6afd9f3SGreg Kroah-Hartman 1174a6afd9f3SGreg Kroah-Hartman ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || 1175a6afd9f3SGreg Kroah-Hartman ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || 1176a6afd9f3SGreg Kroah-Hartman ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || 1177a6afd9f3SGreg Kroah-Hartman ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); 1178a6afd9f3SGreg Kroah-Hartman 1179a6afd9f3SGreg Kroah-Hartman *cprev = cnow; 1180a6afd9f3SGreg Kroah-Hartman 1181a6afd9f3SGreg Kroah-Hartman return ret; 1182a6afd9f3SGreg Kroah-Hartman } 1183a6afd9f3SGreg Kroah-Hartman 11849fae5f85SJiri Slaby /* We should likely switch to TIOCGRS485/TIOCSRS485. */ 11859fae5f85SJiri Slaby static int mxser_ioctl_op_mode(struct mxser_port *port, int index, bool set, 11869fae5f85SJiri Slaby int __user *u_opmode) 11879fae5f85SJiri Slaby { 11889fae5f85SJiri Slaby int opmode, p = index % 4; 11899fae5f85SJiri Slaby int shiftbit = p * 2; 1190238d117dSJiri Slaby u8 val; 11919fae5f85SJiri Slaby 11929fae5f85SJiri Slaby if (port->board->must_hwid != MOXA_MUST_MU860_HWID) 11939fae5f85SJiri Slaby return -EFAULT; 11949fae5f85SJiri Slaby 11959fae5f85SJiri Slaby if (set) { 11969fae5f85SJiri Slaby if (get_user(opmode, u_opmode)) 11979fae5f85SJiri Slaby return -EFAULT; 11989fae5f85SJiri Slaby 1199238d117dSJiri Slaby if (opmode & ~OP_MODE_MASK) 1200238d117dSJiri Slaby return -EINVAL; 12019fae5f85SJiri Slaby 12029fae5f85SJiri Slaby spin_lock_irq(&port->slock); 12039fae5f85SJiri Slaby val = inb(port->opmode_ioaddr); 1204238d117dSJiri Slaby val &= ~(OP_MODE_MASK << shiftbit); 12059fae5f85SJiri Slaby val |= (opmode << shiftbit); 12069fae5f85SJiri Slaby outb(val, port->opmode_ioaddr); 12079fae5f85SJiri Slaby spin_unlock_irq(&port->slock); 1208238d117dSJiri Slaby 1209238d117dSJiri Slaby return 0; 1210238d117dSJiri Slaby } 1211238d117dSJiri Slaby 12129fae5f85SJiri Slaby spin_lock_irq(&port->slock); 12139fae5f85SJiri Slaby opmode = inb(port->opmode_ioaddr) >> shiftbit; 12149fae5f85SJiri Slaby spin_unlock_irq(&port->slock); 12159fae5f85SJiri Slaby 1216238d117dSJiri Slaby return put_user(opmode & OP_MODE_MASK, u_opmode); 12179fae5f85SJiri Slaby } 12189fae5f85SJiri Slaby 1219a6afd9f3SGreg Kroah-Hartman static int mxser_ioctl(struct tty_struct *tty, 1220a6afd9f3SGreg Kroah-Hartman unsigned int cmd, unsigned long arg) 1221a6afd9f3SGreg Kroah-Hartman { 1222a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1223a6afd9f3SGreg Kroah-Hartman struct async_icount cnow; 1224a6afd9f3SGreg Kroah-Hartman unsigned long flags; 1225a6afd9f3SGreg Kroah-Hartman void __user *argp = (void __user *)arg; 1226a6afd9f3SGreg Kroah-Hartman 12279fae5f85SJiri Slaby if (cmd == MOXA_SET_OP_MODE || cmd == MOXA_GET_OP_MODE) 12289fae5f85SJiri Slaby return mxser_ioctl_op_mode(info, tty->index, 12299fae5f85SJiri Slaby cmd == MOXA_SET_OP_MODE, argp); 1230a6afd9f3SGreg Kroah-Hartman 12316da5b587SAl Viro if (cmd != TIOCMIWAIT && tty_io_error(tty)) 1232a6afd9f3SGreg Kroah-Hartman return -EIO; 1233a6afd9f3SGreg Kroah-Hartman 1234a6afd9f3SGreg Kroah-Hartman switch (cmd) { 1235a6afd9f3SGreg Kroah-Hartman case TIOCSERGETLSR: /* Get line status register */ 1236a6afd9f3SGreg Kroah-Hartman return mxser_get_lsr_info(info, argp); 1237a6afd9f3SGreg Kroah-Hartman /* 1238a6afd9f3SGreg Kroah-Hartman * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change 1239a6afd9f3SGreg Kroah-Hartman * - mask passed in arg for lines of interest 1240a6afd9f3SGreg Kroah-Hartman * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) 1241a6afd9f3SGreg Kroah-Hartman * Caller should use TIOCGICOUNT to see which one it was 1242a6afd9f3SGreg Kroah-Hartman */ 1243a6afd9f3SGreg Kroah-Hartman case TIOCMIWAIT: 1244a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 1245a6afd9f3SGreg Kroah-Hartman cnow = info->icount; /* note the counters on entry */ 1246a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1247a6afd9f3SGreg Kroah-Hartman 1248a6afd9f3SGreg Kroah-Hartman return wait_event_interruptible(info->port.delta_msr_wait, 1249a6afd9f3SGreg Kroah-Hartman mxser_cflags_changed(info, arg, &cnow)); 1250a6afd9f3SGreg Kroah-Hartman default: 1251a6afd9f3SGreg Kroah-Hartman return -ENOIOCTLCMD; 1252a6afd9f3SGreg Kroah-Hartman } 1253a6afd9f3SGreg Kroah-Hartman return 0; 1254a6afd9f3SGreg Kroah-Hartman } 1255a6afd9f3SGreg Kroah-Hartman 1256a6afd9f3SGreg Kroah-Hartman /* 1257a6afd9f3SGreg Kroah-Hartman * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) 1258a6afd9f3SGreg Kroah-Hartman * Return: write counters to the user passed counter struct 1259a6afd9f3SGreg Kroah-Hartman * NB: both 1->0 and 0->1 transitions are counted except for 1260a6afd9f3SGreg Kroah-Hartman * RI where only 0->1 is counted. 1261a6afd9f3SGreg Kroah-Hartman */ 1262a6afd9f3SGreg Kroah-Hartman 1263a6afd9f3SGreg Kroah-Hartman static int mxser_get_icount(struct tty_struct *tty, 1264a6afd9f3SGreg Kroah-Hartman struct serial_icounter_struct *icount) 1265a6afd9f3SGreg Kroah-Hartman 1266a6afd9f3SGreg Kroah-Hartman { 1267a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1268a6afd9f3SGreg Kroah-Hartman struct async_icount cnow; 1269a6afd9f3SGreg Kroah-Hartman unsigned long flags; 1270a6afd9f3SGreg Kroah-Hartman 1271a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 1272a6afd9f3SGreg Kroah-Hartman cnow = info->icount; 1273a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1274a6afd9f3SGreg Kroah-Hartman 1275a6afd9f3SGreg Kroah-Hartman icount->frame = cnow.frame; 1276a6afd9f3SGreg Kroah-Hartman icount->brk = cnow.brk; 1277a6afd9f3SGreg Kroah-Hartman icount->overrun = cnow.overrun; 1278a6afd9f3SGreg Kroah-Hartman icount->buf_overrun = cnow.buf_overrun; 1279a6afd9f3SGreg Kroah-Hartman icount->parity = cnow.parity; 1280a6afd9f3SGreg Kroah-Hartman icount->rx = cnow.rx; 1281a6afd9f3SGreg Kroah-Hartman icount->tx = cnow.tx; 1282a6afd9f3SGreg Kroah-Hartman icount->cts = cnow.cts; 1283a6afd9f3SGreg Kroah-Hartman icount->dsr = cnow.dsr; 1284a6afd9f3SGreg Kroah-Hartman icount->rng = cnow.rng; 1285a6afd9f3SGreg Kroah-Hartman icount->dcd = cnow.dcd; 1286a6afd9f3SGreg Kroah-Hartman return 0; 1287a6afd9f3SGreg Kroah-Hartman } 1288a6afd9f3SGreg Kroah-Hartman 1289a6afd9f3SGreg Kroah-Hartman static void mxser_stoprx(struct tty_struct *tty) 1290a6afd9f3SGreg Kroah-Hartman { 1291a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1292a6afd9f3SGreg Kroah-Hartman 129319236287SJiri Slaby info->ldisc_stop_rx = true; 1294a6afd9f3SGreg Kroah-Hartman if (I_IXOFF(tty)) { 1295292955a7SJiri Slaby if (info->board->must_hwid) { 1296a6afd9f3SGreg Kroah-Hartman info->IER &= ~MOXA_MUST_RECV_ISR; 1297a6afd9f3SGreg Kroah-Hartman outb(info->IER, info->ioaddr + UART_IER); 1298a6afd9f3SGreg Kroah-Hartman } else { 1299a6afd9f3SGreg Kroah-Hartman info->x_char = STOP_CHAR(tty); 1300a6afd9f3SGreg Kroah-Hartman outb(0, info->ioaddr + UART_IER); 1301a6afd9f3SGreg Kroah-Hartman info->IER |= UART_IER_THRI; 1302a6afd9f3SGreg Kroah-Hartman outb(info->IER, info->ioaddr + UART_IER); 1303a6afd9f3SGreg Kroah-Hartman } 1304a6afd9f3SGreg Kroah-Hartman } 1305a6afd9f3SGreg Kroah-Hartman 13069db276f8SPeter Hurley if (C_CRTSCTS(tty)) { 1307a6afd9f3SGreg Kroah-Hartman info->MCR &= ~UART_MCR_RTS; 1308a6afd9f3SGreg Kroah-Hartman outb(info->MCR, info->ioaddr + UART_MCR); 1309a6afd9f3SGreg Kroah-Hartman } 1310a6afd9f3SGreg Kroah-Hartman } 1311a6afd9f3SGreg Kroah-Hartman 1312a6afd9f3SGreg Kroah-Hartman /* 1313a6afd9f3SGreg Kroah-Hartman * This routine is called by the upper-layer tty layer to signal that 1314a6afd9f3SGreg Kroah-Hartman * incoming characters should be throttled. 1315a6afd9f3SGreg Kroah-Hartman */ 1316a6afd9f3SGreg Kroah-Hartman static void mxser_throttle(struct tty_struct *tty) 1317a6afd9f3SGreg Kroah-Hartman { 1318a6afd9f3SGreg Kroah-Hartman mxser_stoprx(tty); 1319a6afd9f3SGreg Kroah-Hartman } 1320a6afd9f3SGreg Kroah-Hartman 1321a6afd9f3SGreg Kroah-Hartman static void mxser_unthrottle(struct tty_struct *tty) 1322a6afd9f3SGreg Kroah-Hartman { 1323a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1324a6afd9f3SGreg Kroah-Hartman 1325a6afd9f3SGreg Kroah-Hartman /* startrx */ 132619236287SJiri Slaby info->ldisc_stop_rx = false; 1327a6afd9f3SGreg Kroah-Hartman if (I_IXOFF(tty)) { 1328a6afd9f3SGreg Kroah-Hartman if (info->x_char) 1329a6afd9f3SGreg Kroah-Hartman info->x_char = 0; 1330a6afd9f3SGreg Kroah-Hartman else { 1331292955a7SJiri Slaby if (info->board->must_hwid) { 1332a6afd9f3SGreg Kroah-Hartman info->IER |= MOXA_MUST_RECV_ISR; 1333a6afd9f3SGreg Kroah-Hartman outb(info->IER, info->ioaddr + UART_IER); 1334a6afd9f3SGreg Kroah-Hartman } else { 1335a6afd9f3SGreg Kroah-Hartman info->x_char = START_CHAR(tty); 1336a6afd9f3SGreg Kroah-Hartman outb(0, info->ioaddr + UART_IER); 1337a6afd9f3SGreg Kroah-Hartman info->IER |= UART_IER_THRI; 1338a6afd9f3SGreg Kroah-Hartman outb(info->IER, info->ioaddr + UART_IER); 1339a6afd9f3SGreg Kroah-Hartman } 1340a6afd9f3SGreg Kroah-Hartman } 1341a6afd9f3SGreg Kroah-Hartman } 1342a6afd9f3SGreg Kroah-Hartman 13439db276f8SPeter Hurley if (C_CRTSCTS(tty)) { 1344a6afd9f3SGreg Kroah-Hartman info->MCR |= UART_MCR_RTS; 1345a6afd9f3SGreg Kroah-Hartman outb(info->MCR, info->ioaddr + UART_MCR); 1346a6afd9f3SGreg Kroah-Hartman } 1347a6afd9f3SGreg Kroah-Hartman } 1348a6afd9f3SGreg Kroah-Hartman 1349a6afd9f3SGreg Kroah-Hartman /* 1350a6afd9f3SGreg Kroah-Hartman * mxser_stop() and mxser_start() 1351a6afd9f3SGreg Kroah-Hartman * 13526e94dbc7SJiri Slaby * This routines are called before setting or resetting tty->flow.stopped. 1353a6afd9f3SGreg Kroah-Hartman * They enable or disable transmitter interrupts, as necessary. 1354a6afd9f3SGreg Kroah-Hartman */ 1355a6afd9f3SGreg Kroah-Hartman static void mxser_stop(struct tty_struct *tty) 1356a6afd9f3SGreg Kroah-Hartman { 1357a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1358a6afd9f3SGreg Kroah-Hartman unsigned long flags; 1359a6afd9f3SGreg Kroah-Hartman 1360a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 1361740165f7SJiri Slaby if (info->IER & UART_IER_THRI) 1362740165f7SJiri Slaby __mxser_stop_tx(info); 1363a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1364a6afd9f3SGreg Kroah-Hartman } 1365a6afd9f3SGreg Kroah-Hartman 1366a6afd9f3SGreg Kroah-Hartman static void mxser_start(struct tty_struct *tty) 1367a6afd9f3SGreg Kroah-Hartman { 1368a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1369a6afd9f3SGreg Kroah-Hartman unsigned long flags; 1370a6afd9f3SGreg Kroah-Hartman 1371a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 13725c338fbfSJiri Slaby if (info->xmit_cnt) 1373740165f7SJiri Slaby __mxser_start_tx(info); 1374a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1375a6afd9f3SGreg Kroah-Hartman } 1376a6afd9f3SGreg Kroah-Hartman 1377a6afd9f3SGreg Kroah-Hartman static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termios) 1378a6afd9f3SGreg Kroah-Hartman { 1379a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1380a6afd9f3SGreg Kroah-Hartman unsigned long flags; 1381a6afd9f3SGreg Kroah-Hartman 1382a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 13833fdfa165SJiri Slaby mxser_change_speed(tty, old_termios); 1384a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1385a6afd9f3SGreg Kroah-Hartman 13869db276f8SPeter Hurley if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { 1387a6afd9f3SGreg Kroah-Hartman tty->hw_stopped = 0; 1388a6afd9f3SGreg Kroah-Hartman mxser_start(tty); 1389a6afd9f3SGreg Kroah-Hartman } 1390a6afd9f3SGreg Kroah-Hartman 1391a6afd9f3SGreg Kroah-Hartman /* Handle sw stopped */ 13929db276f8SPeter Hurley if ((old_termios->c_iflag & IXON) && !I_IXON(tty)) { 13936e94dbc7SJiri Slaby tty->flow.stopped = 0; 1394a6afd9f3SGreg Kroah-Hartman 1395292955a7SJiri Slaby if (info->board->must_hwid) { 1396a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 1397b441eb0fSJiri Slaby mxser_must_set_rx_sw_flow_control(info->ioaddr, false); 1398a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 1399a6afd9f3SGreg Kroah-Hartman } 1400a6afd9f3SGreg Kroah-Hartman 1401a6afd9f3SGreg Kroah-Hartman mxser_start(tty); 1402a6afd9f3SGreg Kroah-Hartman } 1403a6afd9f3SGreg Kroah-Hartman } 1404a6afd9f3SGreg Kroah-Hartman 1405239ef19eSJiri Slaby static bool mxser_tx_empty(struct mxser_port *info) 1406239ef19eSJiri Slaby { 1407239ef19eSJiri Slaby unsigned long flags; 1408239ef19eSJiri Slaby u8 lsr; 1409239ef19eSJiri Slaby 1410239ef19eSJiri Slaby spin_lock_irqsave(&info->slock, flags); 1411239ef19eSJiri Slaby lsr = inb(info->ioaddr + UART_LSR); 1412239ef19eSJiri Slaby spin_unlock_irqrestore(&info->slock, flags); 1413239ef19eSJiri Slaby 1414239ef19eSJiri Slaby return !(lsr & UART_LSR_TEMT); 1415239ef19eSJiri Slaby } 1416239ef19eSJiri Slaby 1417a6afd9f3SGreg Kroah-Hartman /* 1418a6afd9f3SGreg Kroah-Hartman * mxser_wait_until_sent() --- wait until the transmitter is empty 1419a6afd9f3SGreg Kroah-Hartman */ 1420a6afd9f3SGreg Kroah-Hartman static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) 1421a6afd9f3SGreg Kroah-Hartman { 1422a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1423a6afd9f3SGreg Kroah-Hartman unsigned long orig_jiffies, char_time; 1424a6afd9f3SGreg Kroah-Hartman 1425a6afd9f3SGreg Kroah-Hartman if (info->type == PORT_UNKNOWN) 1426a6afd9f3SGreg Kroah-Hartman return; 1427a6afd9f3SGreg Kroah-Hartman 1428a6afd9f3SGreg Kroah-Hartman if (info->xmit_fifo_size == 0) 1429a6afd9f3SGreg Kroah-Hartman return; /* Just in case.... */ 1430a6afd9f3SGreg Kroah-Hartman 1431a6afd9f3SGreg Kroah-Hartman orig_jiffies = jiffies; 1432a6afd9f3SGreg Kroah-Hartman /* 1433a6afd9f3SGreg Kroah-Hartman * Set the check interval to be 1/5 of the estimated time to 1434a6afd9f3SGreg Kroah-Hartman * send a single character, and make it at least 1. The check 1435a6afd9f3SGreg Kroah-Hartman * interval should also be less than the timeout. 1436a6afd9f3SGreg Kroah-Hartman * 1437a6afd9f3SGreg Kroah-Hartman * Note: we have to use pretty tight timings here to satisfy 1438a6afd9f3SGreg Kroah-Hartman * the NIST-PCTS. 1439a6afd9f3SGreg Kroah-Hartman */ 1440a6afd9f3SGreg Kroah-Hartman char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; 1441a6afd9f3SGreg Kroah-Hartman char_time = char_time / 5; 1442a6afd9f3SGreg Kroah-Hartman if (char_time == 0) 1443a6afd9f3SGreg Kroah-Hartman char_time = 1; 1444a6afd9f3SGreg Kroah-Hartman if (timeout && timeout < char_time) 1445a6afd9f3SGreg Kroah-Hartman char_time = timeout; 1446*fe74bc61SJiri Slaby 1447*fe74bc61SJiri Slaby char_time = jiffies_to_msecs(char_time); 1448*fe74bc61SJiri Slaby 1449a6afd9f3SGreg Kroah-Hartman /* 1450a6afd9f3SGreg Kroah-Hartman * If the transmitter hasn't cleared in twice the approximate 1451a6afd9f3SGreg Kroah-Hartman * amount of time to send the entire FIFO, it probably won't 1452a6afd9f3SGreg Kroah-Hartman * ever clear. This assumes the UART isn't doing flow 1453a6afd9f3SGreg Kroah-Hartman * control, which is currently the case. Hence, if it ever 1454a6afd9f3SGreg Kroah-Hartman * takes longer than info->timeout, this is probably due to a 1455a6afd9f3SGreg Kroah-Hartman * UART bug of some kind. So, we clamp the timeout parameter at 1456a6afd9f3SGreg Kroah-Hartman * 2*info->timeout. 1457a6afd9f3SGreg Kroah-Hartman */ 1458a6afd9f3SGreg Kroah-Hartman if (!timeout || timeout > 2 * info->timeout) 1459a6afd9f3SGreg Kroah-Hartman timeout = 2 * info->timeout; 14608bab534bSJiri Slaby 1461239ef19eSJiri Slaby while (mxser_tx_empty(info)) { 1462*fe74bc61SJiri Slaby msleep_interruptible(char_time); 1463a6afd9f3SGreg Kroah-Hartman if (signal_pending(current)) 1464a6afd9f3SGreg Kroah-Hartman break; 1465a6afd9f3SGreg Kroah-Hartman if (timeout && time_after(jiffies, orig_jiffies + timeout)) 1466a6afd9f3SGreg Kroah-Hartman break; 1467a6afd9f3SGreg Kroah-Hartman } 1468a6afd9f3SGreg Kroah-Hartman } 1469a6afd9f3SGreg Kroah-Hartman 1470a6afd9f3SGreg Kroah-Hartman /* 1471a6afd9f3SGreg Kroah-Hartman * This routine is called by tty_hangup() when a hangup is signaled. 1472a6afd9f3SGreg Kroah-Hartman */ 1473a6afd9f3SGreg Kroah-Hartman static void mxser_hangup(struct tty_struct *tty) 1474a6afd9f3SGreg Kroah-Hartman { 1475a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1476a6afd9f3SGreg Kroah-Hartman 1477a6afd9f3SGreg Kroah-Hartman mxser_flush_buffer(tty); 1478a6afd9f3SGreg Kroah-Hartman tty_port_hangup(&info->port); 1479a6afd9f3SGreg Kroah-Hartman } 1480a6afd9f3SGreg Kroah-Hartman 1481a6afd9f3SGreg Kroah-Hartman /* 1482a6afd9f3SGreg Kroah-Hartman * mxser_rs_break() --- routine which turns the break handling on or off 1483a6afd9f3SGreg Kroah-Hartman */ 1484a6afd9f3SGreg Kroah-Hartman static int mxser_rs_break(struct tty_struct *tty, int break_state) 1485a6afd9f3SGreg Kroah-Hartman { 1486a6afd9f3SGreg Kroah-Hartman struct mxser_port *info = tty->driver_data; 1487a6afd9f3SGreg Kroah-Hartman unsigned long flags; 148859908433SJiri Slaby u8 lcr; 1489a6afd9f3SGreg Kroah-Hartman 1490a6afd9f3SGreg Kroah-Hartman spin_lock_irqsave(&info->slock, flags); 149159908433SJiri Slaby lcr = inb(info->ioaddr + UART_LCR); 1492a6afd9f3SGreg Kroah-Hartman if (break_state == -1) 149359908433SJiri Slaby lcr |= UART_LCR_SBC; 1494a6afd9f3SGreg Kroah-Hartman else 149559908433SJiri Slaby lcr &= ~UART_LCR_SBC; 149659908433SJiri Slaby outb(lcr, info->ioaddr + UART_LCR); 1497a6afd9f3SGreg Kroah-Hartman spin_unlock_irqrestore(&info->slock, flags); 149859908433SJiri Slaby 1499a6afd9f3SGreg Kroah-Hartman return 0; 1500a6afd9f3SGreg Kroah-Hartman } 1501a6afd9f3SGreg Kroah-Hartman 1502e5ce1bceSJiri Slaby static bool mxser_receive_chars_new(struct tty_struct *tty, 150395b3ea4cSJiri Slaby struct mxser_port *port, u8 status) 1504e5ce1bceSJiri Slaby { 1505e5ce1bceSJiri Slaby enum mxser_must_hwid hwid = port->board->must_hwid; 1506e5ce1bceSJiri Slaby u8 gdl; 1507e5ce1bceSJiri Slaby 1508e5ce1bceSJiri Slaby if (hwid == MOXA_OTHER_UART) 1509e5ce1bceSJiri Slaby return false; 15107d5006d5SJiri Slaby if (status & (UART_LSR_BRK_ERROR_BITS | MOXA_MUST_LSR_RERR)) 1511e5ce1bceSJiri Slaby return false; 1512e5ce1bceSJiri Slaby 1513e5ce1bceSJiri Slaby gdl = inb(port->ioaddr + MOXA_MUST_GDL_REGISTER); 1514e5ce1bceSJiri Slaby if (hwid == MOXA_MUST_MU150_HWID) 1515e5ce1bceSJiri Slaby gdl &= MOXA_MUST_GDL_MASK; 1516e5ce1bceSJiri Slaby 1517e5ce1bceSJiri Slaby if (gdl >= tty->receive_room && !port->ldisc_stop_rx) 1518e5ce1bceSJiri Slaby mxser_stoprx(tty); 1519e5ce1bceSJiri Slaby 1520e5ce1bceSJiri Slaby while (gdl--) { 1521e5ce1bceSJiri Slaby u8 ch = inb(port->ioaddr + UART_RX); 1522e5ce1bceSJiri Slaby tty_insert_flip_char(&port->port, ch, 0); 1523e5ce1bceSJiri Slaby } 1524e5ce1bceSJiri Slaby 1525e5ce1bceSJiri Slaby return true; 1526e5ce1bceSJiri Slaby } 1527e5ce1bceSJiri Slaby 15280c419421SJiri Slaby static u8 mxser_receive_chars_old(struct tty_struct *tty, 152995b3ea4cSJiri Slaby struct mxser_port *port, u8 status) 1530a6afd9f3SGreg Kroah-Hartman { 15310c419421SJiri Slaby enum mxser_must_hwid hwid = port->board->must_hwid; 15320c419421SJiri Slaby int recv_room = tty->receive_room; 1533a6afd9f3SGreg Kroah-Hartman int ignored = 0; 1534a6afd9f3SGreg Kroah-Hartman int max = 256; 153595b3ea4cSJiri Slaby int cnt = 0; 15360c419421SJiri Slaby u8 ch; 1537a6afd9f3SGreg Kroah-Hartman 1538a6afd9f3SGreg Kroah-Hartman do { 1539a6afd9f3SGreg Kroah-Hartman if (max-- < 0) 1540a6afd9f3SGreg Kroah-Hartman break; 1541a6afd9f3SGreg Kroah-Hartman 1542a6afd9f3SGreg Kroah-Hartman ch = inb(port->ioaddr + UART_RX); 15430c419421SJiri Slaby if (hwid && (status & UART_LSR_OE)) 1544d249e662SJiri Slaby outb(port->FCR | UART_FCR_CLEAR_RCVR, 1545aaa28e9fSJiri Slaby port->ioaddr + UART_FCR); 154615517806SJiri Slaby status &= port->read_status_mask; 154715517806SJiri Slaby if (status & port->ignore_status_mask) { 1548a6afd9f3SGreg Kroah-Hartman if (++ignored > 100) 1549a6afd9f3SGreg Kroah-Hartman break; 1550a6afd9f3SGreg Kroah-Hartman } else { 1551a6afd9f3SGreg Kroah-Hartman char flag = 0; 155270640052SJiri Slaby if (status & UART_LSR_BRK_ERROR_BITS) { 155315517806SJiri Slaby if (status & UART_LSR_BI) { 1554a6afd9f3SGreg Kroah-Hartman flag = TTY_BREAK; 1555a6afd9f3SGreg Kroah-Hartman port->icount.brk++; 1556a6afd9f3SGreg Kroah-Hartman 1557a6afd9f3SGreg Kroah-Hartman if (port->port.flags & ASYNC_SAK) 1558a6afd9f3SGreg Kroah-Hartman do_SAK(tty); 155915517806SJiri Slaby } else if (status & UART_LSR_PE) { 1560a6afd9f3SGreg Kroah-Hartman flag = TTY_PARITY; 1561a6afd9f3SGreg Kroah-Hartman port->icount.parity++; 156215517806SJiri Slaby } else if (status & UART_LSR_FE) { 1563a6afd9f3SGreg Kroah-Hartman flag = TTY_FRAME; 1564a6afd9f3SGreg Kroah-Hartman port->icount.frame++; 156515517806SJiri Slaby } else if (status & UART_LSR_OE) { 1566a6afd9f3SGreg Kroah-Hartman flag = TTY_OVERRUN; 1567a6afd9f3SGreg Kroah-Hartman port->icount.overrun++; 15686de6e5c4SJiri Slaby } 1569a6afd9f3SGreg Kroah-Hartman } 157092a19f9cSJiri Slaby tty_insert_flip_char(&port->port, ch, flag); 157195b3ea4cSJiri Slaby cnt++; 157295b3ea4cSJiri Slaby if (cnt >= recv_room) { 1573a6afd9f3SGreg Kroah-Hartman if (!port->ldisc_stop_rx) 1574a6afd9f3SGreg Kroah-Hartman mxser_stoprx(tty); 1575a6afd9f3SGreg Kroah-Hartman break; 1576a6afd9f3SGreg Kroah-Hartman } 1577a6afd9f3SGreg Kroah-Hartman 1578a6afd9f3SGreg Kroah-Hartman } 1579a6afd9f3SGreg Kroah-Hartman 15800c419421SJiri Slaby if (hwid) 1581a6afd9f3SGreg Kroah-Hartman break; 1582a6afd9f3SGreg Kroah-Hartman 158315517806SJiri Slaby status = inb(port->ioaddr + UART_LSR); 158415517806SJiri Slaby } while (status & UART_LSR_DR); 1585a6afd9f3SGreg Kroah-Hartman 15860c419421SJiri Slaby return status; 15870c419421SJiri Slaby } 15880c419421SJiri Slaby 15890c419421SJiri Slaby static u8 mxser_receive_chars(struct tty_struct *tty, 15900c419421SJiri Slaby struct mxser_port *port, u8 status) 15910c419421SJiri Slaby { 15920c419421SJiri Slaby if (tty->receive_room == 0 && !port->ldisc_stop_rx) 15930c419421SJiri Slaby mxser_stoprx(tty); 15940c419421SJiri Slaby 159595b3ea4cSJiri Slaby if (!mxser_receive_chars_new(tty, port, status)) 159695b3ea4cSJiri Slaby status = mxser_receive_chars_old(tty, port, status); 15970c419421SJiri Slaby 15982e124b4aSJiri Slaby tty_flip_buffer_push(&port->port); 159915517806SJiri Slaby 160015517806SJiri Slaby return status; 1601a6afd9f3SGreg Kroah-Hartman } 1602a6afd9f3SGreg Kroah-Hartman 1603a6afd9f3SGreg Kroah-Hartman static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port) 1604a6afd9f3SGreg Kroah-Hartman { 16053b88dbffSJiri Slaby int count; 1606a6afd9f3SGreg Kroah-Hartman 1607a6afd9f3SGreg Kroah-Hartman if (port->x_char) { 1608a6afd9f3SGreg Kroah-Hartman outb(port->x_char, port->ioaddr + UART_TX); 1609a6afd9f3SGreg Kroah-Hartman port->x_char = 0; 1610a6afd9f3SGreg Kroah-Hartman port->icount.tx++; 1611a6afd9f3SGreg Kroah-Hartman return; 1612a6afd9f3SGreg Kroah-Hartman } 1613a6afd9f3SGreg Kroah-Hartman 1614265ceff7SJiri Slaby if (!port->xmit_cnt || tty->flow.stopped || 16155d1ea1adSJiri Slaby (tty->hw_stopped && !mxser_16550A_or_MUST(port))) { 1616740165f7SJiri Slaby __mxser_stop_tx(port); 1617a6afd9f3SGreg Kroah-Hartman return; 1618a6afd9f3SGreg Kroah-Hartman } 1619a6afd9f3SGreg Kroah-Hartman 1620a6afd9f3SGreg Kroah-Hartman count = port->xmit_fifo_size; 1621a6afd9f3SGreg Kroah-Hartman do { 1622a6afd9f3SGreg Kroah-Hartman outb(port->port.xmit_buf[port->xmit_tail++], 1623a6afd9f3SGreg Kroah-Hartman port->ioaddr + UART_TX); 16243b88dbffSJiri Slaby port->xmit_tail &= SERIAL_XMIT_SIZE - 1; 16253b88dbffSJiri Slaby port->icount.tx++; 1626265ceff7SJiri Slaby if (!--port->xmit_cnt) 1627a6afd9f3SGreg Kroah-Hartman break; 1628a6afd9f3SGreg Kroah-Hartman } while (--count > 0); 1629a6afd9f3SGreg Kroah-Hartman 1630a6afd9f3SGreg Kroah-Hartman if (port->xmit_cnt < WAKEUP_CHARS) 1631a6afd9f3SGreg Kroah-Hartman tty_wakeup(tty); 1632a6afd9f3SGreg Kroah-Hartman 1633265ceff7SJiri Slaby if (!port->xmit_cnt) 1634740165f7SJiri Slaby __mxser_stop_tx(port); 1635a6afd9f3SGreg Kroah-Hartman } 1636a6afd9f3SGreg Kroah-Hartman 16379e40ea1fSJiri Slaby static bool mxser_port_isr(struct mxser_port *port) 16389e40ea1fSJiri Slaby { 16399e40ea1fSJiri Slaby struct tty_struct *tty; 164030f6027fSJiri Slaby u8 iir, status; 16419e40ea1fSJiri Slaby bool error = false; 16429e40ea1fSJiri Slaby 16439e40ea1fSJiri Slaby iir = inb(port->ioaddr + UART_IIR); 16449e40ea1fSJiri Slaby if (iir & UART_IIR_NO_INT) 16459e40ea1fSJiri Slaby return true; 16469e40ea1fSJiri Slaby 16479e40ea1fSJiri Slaby iir &= MOXA_MUST_IIR_MASK; 16489e40ea1fSJiri Slaby tty = tty_port_tty_get(&port->port); 1649274ab58dSJiri Slaby if (!tty) { 16509e40ea1fSJiri Slaby status = inb(port->ioaddr + UART_LSR); 1651d249e662SJiri Slaby outb(port->FCR | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, 1652aaa28e9fSJiri Slaby port->ioaddr + UART_FCR); 16539e40ea1fSJiri Slaby inb(port->ioaddr + UART_MSR); 16549e40ea1fSJiri Slaby 16559e40ea1fSJiri Slaby error = true; 16569e40ea1fSJiri Slaby goto put_tty; 16579e40ea1fSJiri Slaby } 16589e40ea1fSJiri Slaby 16599e40ea1fSJiri Slaby status = inb(port->ioaddr + UART_LSR); 16609e40ea1fSJiri Slaby 16619e40ea1fSJiri Slaby if (port->board->must_hwid) { 16629e40ea1fSJiri Slaby if (iir == MOXA_MUST_IIR_GDA || 16639e40ea1fSJiri Slaby iir == MOXA_MUST_IIR_RDA || 16649e40ea1fSJiri Slaby iir == MOXA_MUST_IIR_RTO || 16659e40ea1fSJiri Slaby iir == MOXA_MUST_IIR_LSR) 16669e40ea1fSJiri Slaby status = mxser_receive_chars(tty, port, status); 16679e40ea1fSJiri Slaby } else { 16689e40ea1fSJiri Slaby status &= port->read_status_mask; 16699e40ea1fSJiri Slaby if (status & UART_LSR_DR) 16709e40ea1fSJiri Slaby status = mxser_receive_chars(tty, port, status); 16719e40ea1fSJiri Slaby } 16729e40ea1fSJiri Slaby 167330f6027fSJiri Slaby mxser_check_modem_status(tty, port); 16749e40ea1fSJiri Slaby 16759e40ea1fSJiri Slaby if (port->board->must_hwid) { 16769e40ea1fSJiri Slaby if (iir == 0x02 && (status & UART_LSR_THRE)) 16779e40ea1fSJiri Slaby mxser_transmit_chars(tty, port); 16789e40ea1fSJiri Slaby } else { 16799e40ea1fSJiri Slaby if (status & UART_LSR_THRE) 16809e40ea1fSJiri Slaby mxser_transmit_chars(tty, port); 16819e40ea1fSJiri Slaby } 16829e40ea1fSJiri Slaby 16839e40ea1fSJiri Slaby put_tty: 16849e40ea1fSJiri Slaby tty_kref_put(tty); 16859e40ea1fSJiri Slaby 16869e40ea1fSJiri Slaby return error; 16879e40ea1fSJiri Slaby } 16889e40ea1fSJiri Slaby 1689a6afd9f3SGreg Kroah-Hartman /* 1690a6afd9f3SGreg Kroah-Hartman * This is the serial driver's generic interrupt routine 1691a6afd9f3SGreg Kroah-Hartman */ 1692a6afd9f3SGreg Kroah-Hartman static irqreturn_t mxser_interrupt(int irq, void *dev_id) 1693a6afd9f3SGreg Kroah-Hartman { 1694cef222cbSJiri Slaby struct mxser_board *brd = dev_id; 1695a6afd9f3SGreg Kroah-Hartman struct mxser_port *port; 1696a6afd9f3SGreg Kroah-Hartman unsigned int int_cnt, pass_counter = 0; 1697c24c31ffSJiri Slaby unsigned int i, max = brd->nports; 1698a6afd9f3SGreg Kroah-Hartman int handled = IRQ_NONE; 16999cb5c9c3SJiri Slaby u8 irqbits, bits, mask = BIT(max) - 1; 1700a6afd9f3SGreg Kroah-Hartman 1701a6afd9f3SGreg Kroah-Hartman while (pass_counter++ < MXSER_ISR_PASS_LIMIT) { 17029cb5c9c3SJiri Slaby irqbits = inb(brd->vector) & mask; 17039cb5c9c3SJiri Slaby if (irqbits == mask) 1704a6afd9f3SGreg Kroah-Hartman break; 1705a6afd9f3SGreg Kroah-Hartman 1706a6afd9f3SGreg Kroah-Hartman handled = IRQ_HANDLED; 1707a6afd9f3SGreg Kroah-Hartman for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { 17089cb5c9c3SJiri Slaby if (irqbits == mask) 1709a6afd9f3SGreg Kroah-Hartman break; 1710a6afd9f3SGreg Kroah-Hartman if (bits & irqbits) 1711a6afd9f3SGreg Kroah-Hartman continue; 1712a6afd9f3SGreg Kroah-Hartman port = &brd->ports[i]; 1713a6afd9f3SGreg Kroah-Hartman 1714a6afd9f3SGreg Kroah-Hartman int_cnt = 0; 1715a6afd9f3SGreg Kroah-Hartman spin_lock(&port->slock); 1716a6afd9f3SGreg Kroah-Hartman do { 17179e40ea1fSJiri Slaby if (mxser_port_isr(port)) 1718a6afd9f3SGreg Kroah-Hartman break; 1719a6afd9f3SGreg Kroah-Hartman } while (int_cnt++ < MXSER_ISR_PASS_LIMIT); 1720a6afd9f3SGreg Kroah-Hartman spin_unlock(&port->slock); 1721a6afd9f3SGreg Kroah-Hartman } 1722a6afd9f3SGreg Kroah-Hartman } 1723a6afd9f3SGreg Kroah-Hartman 1724a6afd9f3SGreg Kroah-Hartman return handled; 1725a6afd9f3SGreg Kroah-Hartman } 1726a6afd9f3SGreg Kroah-Hartman 1727a6afd9f3SGreg Kroah-Hartman static const struct tty_operations mxser_ops = { 1728a6afd9f3SGreg Kroah-Hartman .open = mxser_open, 1729a6afd9f3SGreg Kroah-Hartman .close = mxser_close, 1730a6afd9f3SGreg Kroah-Hartman .write = mxser_write, 1731a6afd9f3SGreg Kroah-Hartman .put_char = mxser_put_char, 1732a6afd9f3SGreg Kroah-Hartman .flush_chars = mxser_flush_chars, 1733a6afd9f3SGreg Kroah-Hartman .write_room = mxser_write_room, 1734a6afd9f3SGreg Kroah-Hartman .chars_in_buffer = mxser_chars_in_buffer, 1735a6afd9f3SGreg Kroah-Hartman .flush_buffer = mxser_flush_buffer, 1736a6afd9f3SGreg Kroah-Hartman .ioctl = mxser_ioctl, 1737a6afd9f3SGreg Kroah-Hartman .throttle = mxser_throttle, 1738a6afd9f3SGreg Kroah-Hartman .unthrottle = mxser_unthrottle, 1739a6afd9f3SGreg Kroah-Hartman .set_termios = mxser_set_termios, 1740a6afd9f3SGreg Kroah-Hartman .stop = mxser_stop, 1741a6afd9f3SGreg Kroah-Hartman .start = mxser_start, 1742a6afd9f3SGreg Kroah-Hartman .hangup = mxser_hangup, 1743a6afd9f3SGreg Kroah-Hartman .break_ctl = mxser_rs_break, 1744a6afd9f3SGreg Kroah-Hartman .wait_until_sent = mxser_wait_until_sent, 1745a6afd9f3SGreg Kroah-Hartman .tiocmget = mxser_tiocmget, 1746a6afd9f3SGreg Kroah-Hartman .tiocmset = mxser_tiocmset, 17476da5b587SAl Viro .set_serial = mxser_set_serial_info, 17486da5b587SAl Viro .get_serial = mxser_get_serial_info, 1749a6afd9f3SGreg Kroah-Hartman .get_icount = mxser_get_icount, 1750a6afd9f3SGreg Kroah-Hartman }; 1751a6afd9f3SGreg Kroah-Hartman 175204b757dfSAya Mahfouz static const struct tty_port_operations mxser_port_ops = { 1753a6afd9f3SGreg Kroah-Hartman .carrier_raised = mxser_carrier_raised, 1754a6afd9f3SGreg Kroah-Hartman .dtr_rts = mxser_dtr_rts, 1755a6afd9f3SGreg Kroah-Hartman .activate = mxser_activate, 1756a6afd9f3SGreg Kroah-Hartman .shutdown = mxser_shutdown_port, 1757a6afd9f3SGreg Kroah-Hartman }; 1758a6afd9f3SGreg Kroah-Hartman 1759a6afd9f3SGreg Kroah-Hartman /* 1760a6afd9f3SGreg Kroah-Hartman * The MOXA Smartio/Industio serial driver boot-time initialization code! 1761a6afd9f3SGreg Kroah-Hartman */ 1762a6afd9f3SGreg Kroah-Hartman 1763c24c31ffSJiri Slaby static void mxser_initbrd(struct mxser_board *brd, bool high_baud) 1764a6afd9f3SGreg Kroah-Hartman { 1765a6afd9f3SGreg Kroah-Hartman struct mxser_port *info; 1766a6afd9f3SGreg Kroah-Hartman unsigned int i; 176757faa7d6SJiri Slaby bool is_mu860; 176857faa7d6SJiri Slaby 176957faa7d6SJiri Slaby brd->must_hwid = mxser_must_get_hwid(brd->ports[0].ioaddr); 177057faa7d6SJiri Slaby is_mu860 = brd->must_hwid == MOXA_MUST_MU860_HWID; 177157faa7d6SJiri Slaby 177257faa7d6SJiri Slaby for (i = 0; i < UART_INFO_NUM; i++) { 177357faa7d6SJiri Slaby if (Gpci_uart_info[i].type == brd->must_hwid) { 177457faa7d6SJiri Slaby brd->max_baud = Gpci_uart_info[i].max_baud; 177557faa7d6SJiri Slaby 177657faa7d6SJiri Slaby /* exception....CP-102 */ 1777c24c31ffSJiri Slaby if (high_baud) 177857faa7d6SJiri Slaby brd->max_baud = 921600; 177957faa7d6SJiri Slaby break; 178057faa7d6SJiri Slaby } 178157faa7d6SJiri Slaby } 178257faa7d6SJiri Slaby 178357faa7d6SJiri Slaby if (is_mu860) { 178457faa7d6SJiri Slaby /* set to RS232 mode by default */ 178557faa7d6SJiri Slaby outb(0, brd->vector + 4); 178657faa7d6SJiri Slaby outb(0, brd->vector + 0x0c); 178757faa7d6SJiri Slaby } 1788a6afd9f3SGreg Kroah-Hartman 1789c24c31ffSJiri Slaby for (i = 0; i < brd->nports; i++) { 1790a6afd9f3SGreg Kroah-Hartman info = &brd->ports[i]; 179157faa7d6SJiri Slaby if (is_mu860) { 179257faa7d6SJiri Slaby if (i < 4) 179357faa7d6SJiri Slaby info->opmode_ioaddr = brd->vector + 4; 179457faa7d6SJiri Slaby else 179557faa7d6SJiri Slaby info->opmode_ioaddr = brd->vector + 0x0c; 179657faa7d6SJiri Slaby } 1797a6afd9f3SGreg Kroah-Hartman tty_port_init(&info->port); 1798a6afd9f3SGreg Kroah-Hartman info->port.ops = &mxser_port_ops; 1799a6afd9f3SGreg Kroah-Hartman info->board = brd; 180019236287SJiri Slaby info->ldisc_stop_rx = false; 1801a6afd9f3SGreg Kroah-Hartman 1802a6afd9f3SGreg Kroah-Hartman /* Enhance mode enabled here */ 1803292955a7SJiri Slaby if (brd->must_hwid != MOXA_OTHER_UART) 1804edb7d27cSJiri Slaby mxser_must_set_enhance_mode(info->ioaddr, true); 1805a6afd9f3SGreg Kroah-Hartman 180658a2ddb3SJiri Slaby info->type = PORT_16550A; 1807a6afd9f3SGreg Kroah-Hartman 1808c3db20c3SJiri Slaby mxser_process_txrx_fifo(info); 1809a6afd9f3SGreg Kroah-Hartman 1810a6afd9f3SGreg Kroah-Hartman info->port.close_delay = 5 * HZ / 10; 1811a6afd9f3SGreg Kroah-Hartman info->port.closing_wait = 30 * HZ; 1812a6afd9f3SGreg Kroah-Hartman spin_lock_init(&info->slock); 1813a6afd9f3SGreg Kroah-Hartman 1814a6afd9f3SGreg Kroah-Hartman /* before set INT ISR, disable all int */ 1815a6afd9f3SGreg Kroah-Hartman outb(inb(info->ioaddr + UART_IER) & 0xf0, 1816a6afd9f3SGreg Kroah-Hartman info->ioaddr + UART_IER); 1817a6afd9f3SGreg Kroah-Hartman } 1818a6afd9f3SGreg Kroah-Hartman } 1819a6afd9f3SGreg Kroah-Hartman 18209671f099SBill Pemberton static int mxser_probe(struct pci_dev *pdev, 1821a6afd9f3SGreg Kroah-Hartman const struct pci_device_id *ent) 1822a6afd9f3SGreg Kroah-Hartman { 1823a6afd9f3SGreg Kroah-Hartman struct mxser_board *brd; 182413d4aba8SJiri Slaby unsigned int i, base; 1825a6afd9f3SGreg Kroah-Hartman unsigned long ioaddress; 1826c24c31ffSJiri Slaby unsigned short nports = MXSER_NPORTS(ent->driver_data); 18279e17df37SAlexey Khoroshilov struct device *tty_dev; 1828a6afd9f3SGreg Kroah-Hartman int retval = -EINVAL; 1829a6afd9f3SGreg Kroah-Hartman 1830f8b6b327SJiri Slaby i = find_first_zero_bit(mxser_boards, MXSER_BOARDS); 1831a6afd9f3SGreg Kroah-Hartman if (i >= MXSER_BOARDS) { 1832a6afd9f3SGreg Kroah-Hartman dev_err(&pdev->dev, "too many boards found (maximum %d), board " 1833a6afd9f3SGreg Kroah-Hartman "not configured\n", MXSER_BOARDS); 1834a6afd9f3SGreg Kroah-Hartman goto err; 1835a6afd9f3SGreg Kroah-Hartman } 1836a6afd9f3SGreg Kroah-Hartman 1837ad1c92ffSJiri Slaby brd = devm_kzalloc(&pdev->dev, struct_size(brd, ports, nports), 1838ad1c92ffSJiri Slaby GFP_KERNEL); 1839f8b6b327SJiri Slaby if (!brd) 1840f8b6b327SJiri Slaby goto err; 1841f8b6b327SJiri Slaby 184213d4aba8SJiri Slaby brd->idx = i; 1843f8b6b327SJiri Slaby __set_bit(brd->idx, mxser_boards); 184413d4aba8SJiri Slaby base = i * MXSER_PORTS_PER_BOARD; 1845a6afd9f3SGreg Kroah-Hartman 1846dcb04e21SJiri Slaby retval = pcim_enable_device(pdev); 1847a6afd9f3SGreg Kroah-Hartman if (retval) { 1848a6afd9f3SGreg Kroah-Hartman dev_err(&pdev->dev, "PCI enable failed\n"); 1849f8b6b327SJiri Slaby goto err_zero; 1850a6afd9f3SGreg Kroah-Hartman } 1851a6afd9f3SGreg Kroah-Hartman 1852a6afd9f3SGreg Kroah-Hartman /* io address */ 1853a6afd9f3SGreg Kroah-Hartman ioaddress = pci_resource_start(pdev, 2); 1854a6afd9f3SGreg Kroah-Hartman retval = pci_request_region(pdev, 2, "mxser(IO)"); 1855a6afd9f3SGreg Kroah-Hartman if (retval) 1856f8b6b327SJiri Slaby goto err_zero; 1857a6afd9f3SGreg Kroah-Hartman 1858c24c31ffSJiri Slaby brd->nports = nports; 1859c24c31ffSJiri Slaby for (i = 0; i < nports; i++) 1860a6afd9f3SGreg Kroah-Hartman brd->ports[i].ioaddr = ioaddress + 8 * i; 1861a6afd9f3SGreg Kroah-Hartman 1862a6afd9f3SGreg Kroah-Hartman /* vector */ 1863a6afd9f3SGreg Kroah-Hartman ioaddress = pci_resource_start(pdev, 3); 1864a6afd9f3SGreg Kroah-Hartman retval = pci_request_region(pdev, 3, "mxser(vector)"); 1865a6afd9f3SGreg Kroah-Hartman if (retval) 1866a6afd9f3SGreg Kroah-Hartman goto err_zero; 1867a6afd9f3SGreg Kroah-Hartman brd->vector = ioaddress; 1868a6afd9f3SGreg Kroah-Hartman 1869a6afd9f3SGreg Kroah-Hartman /* irq */ 1870a6afd9f3SGreg Kroah-Hartman brd->irq = pdev->irq; 1871a6afd9f3SGreg Kroah-Hartman 1872c24c31ffSJiri Slaby mxser_initbrd(brd, ent->driver_data & MXSER_HIGHBAUD); 18737f0e79dcSJiri Slaby 18747f0e79dcSJiri Slaby retval = devm_request_irq(&pdev->dev, brd->irq, mxser_interrupt, 18757f0e79dcSJiri Slaby IRQF_SHARED, "mxser", brd); 18767f0e79dcSJiri Slaby if (retval) { 18777f0e79dcSJiri Slaby dev_err(&pdev->dev, "request irq failed"); 18787f0e79dcSJiri Slaby goto err_relbrd; 18797f0e79dcSJiri Slaby } 1880a6afd9f3SGreg Kroah-Hartman 1881c24c31ffSJiri Slaby for (i = 0; i < nports; i++) { 18829e17df37SAlexey Khoroshilov tty_dev = tty_port_register_device(&brd->ports[i].port, 188313d4aba8SJiri Slaby mxvar_sdriver, base + i, &pdev->dev); 18849e17df37SAlexey Khoroshilov if (IS_ERR(tty_dev)) { 18859e17df37SAlexey Khoroshilov retval = PTR_ERR(tty_dev); 18861b581f17SAlexey Khoroshilov for (; i > 0; i--) 18879e17df37SAlexey Khoroshilov tty_unregister_device(mxvar_sdriver, 188813d4aba8SJiri Slaby base + i - 1); 18899e17df37SAlexey Khoroshilov goto err_relbrd; 18909e17df37SAlexey Khoroshilov } 18919e17df37SAlexey Khoroshilov } 1892a6afd9f3SGreg Kroah-Hartman 1893a6afd9f3SGreg Kroah-Hartman pci_set_drvdata(pdev, brd); 1894a6afd9f3SGreg Kroah-Hartman 1895a6afd9f3SGreg Kroah-Hartman return 0; 18969e17df37SAlexey Khoroshilov err_relbrd: 1897c24c31ffSJiri Slaby for (i = 0; i < nports; i++) 18989e17df37SAlexey Khoroshilov tty_port_destroy(&brd->ports[i].port); 1899a6afd9f3SGreg Kroah-Hartman err_zero: 1900f8b6b327SJiri Slaby __clear_bit(brd->idx, mxser_boards); 1901a6afd9f3SGreg Kroah-Hartman err: 1902a6afd9f3SGreg Kroah-Hartman return retval; 1903a6afd9f3SGreg Kroah-Hartman } 1904a6afd9f3SGreg Kroah-Hartman 1905ae8d8a14SBill Pemberton static void mxser_remove(struct pci_dev *pdev) 1906a6afd9f3SGreg Kroah-Hartman { 1907a6afd9f3SGreg Kroah-Hartman struct mxser_board *brd = pci_get_drvdata(pdev); 190813d4aba8SJiri Slaby unsigned int i, base = brd->idx * MXSER_PORTS_PER_BOARD; 1909a6afd9f3SGreg Kroah-Hartman 1910c24c31ffSJiri Slaby for (i = 0; i < brd->nports; i++) { 191113d4aba8SJiri Slaby tty_unregister_device(mxvar_sdriver, base + i); 1912d450f085SJiri Slaby tty_port_destroy(&brd->ports[i].port); 1913d450f085SJiri Slaby } 1914d450f085SJiri Slaby 1915f8b6b327SJiri Slaby __clear_bit(brd->idx, mxser_boards); 1916a6afd9f3SGreg Kroah-Hartman } 1917a6afd9f3SGreg Kroah-Hartman 1918a6afd9f3SGreg Kroah-Hartman static struct pci_driver mxser_driver = { 1919a6afd9f3SGreg Kroah-Hartman .name = "mxser", 1920a6afd9f3SGreg Kroah-Hartman .id_table = mxser_pcibrds, 1921a6afd9f3SGreg Kroah-Hartman .probe = mxser_probe, 192291116cbaSBill Pemberton .remove = mxser_remove 1923a6afd9f3SGreg Kroah-Hartman }; 1924a6afd9f3SGreg Kroah-Hartman 1925a6afd9f3SGreg Kroah-Hartman static int __init mxser_module_init(void) 1926a6afd9f3SGreg Kroah-Hartman { 1927a6afd9f3SGreg Kroah-Hartman int retval; 1928a6afd9f3SGreg Kroah-Hartman 192939b7b42bSJiri Slaby mxvar_sdriver = tty_alloc_driver(MXSER_PORTS, TTY_DRIVER_REAL_RAW | 193039b7b42bSJiri Slaby TTY_DRIVER_DYNAMIC_DEV); 193139b7b42bSJiri Slaby if (IS_ERR(mxvar_sdriver)) 193239b7b42bSJiri Slaby return PTR_ERR(mxvar_sdriver); 1933a6afd9f3SGreg Kroah-Hartman 1934a6afd9f3SGreg Kroah-Hartman /* Initialize the tty_driver structure */ 1935a6afd9f3SGreg Kroah-Hartman mxvar_sdriver->name = "ttyMI"; 1936a6afd9f3SGreg Kroah-Hartman mxvar_sdriver->major = ttymajor; 1937a6afd9f3SGreg Kroah-Hartman mxvar_sdriver->minor_start = 0; 1938a6afd9f3SGreg Kroah-Hartman mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL; 1939a6afd9f3SGreg Kroah-Hartman mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL; 1940a6afd9f3SGreg Kroah-Hartman mxvar_sdriver->init_termios = tty_std_termios; 1941a6afd9f3SGreg Kroah-Hartman mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; 1942a6afd9f3SGreg Kroah-Hartman tty_set_operations(mxvar_sdriver, &mxser_ops); 1943a6afd9f3SGreg Kroah-Hartman 1944a6afd9f3SGreg Kroah-Hartman retval = tty_register_driver(mxvar_sdriver); 1945a6afd9f3SGreg Kroah-Hartman if (retval) { 1946a6afd9f3SGreg Kroah-Hartman printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family " 1947a6afd9f3SGreg Kroah-Hartman "tty driver !\n"); 1948a6afd9f3SGreg Kroah-Hartman goto err_put; 1949a6afd9f3SGreg Kroah-Hartman } 1950a6afd9f3SGreg Kroah-Hartman 1951a6afd9f3SGreg Kroah-Hartman retval = pci_register_driver(&mxser_driver); 1952a6afd9f3SGreg Kroah-Hartman if (retval) { 1953a6afd9f3SGreg Kroah-Hartman printk(KERN_ERR "mxser: can't register pci driver\n"); 1954a6afd9f3SGreg Kroah-Hartman goto err_unr; 1955a6afd9f3SGreg Kroah-Hartman } 1956a6afd9f3SGreg Kroah-Hartman 1957a6afd9f3SGreg Kroah-Hartman return 0; 1958a6afd9f3SGreg Kroah-Hartman err_unr: 1959a6afd9f3SGreg Kroah-Hartman tty_unregister_driver(mxvar_sdriver); 1960a6afd9f3SGreg Kroah-Hartman err_put: 19619f90a4ddSJiri Slaby tty_driver_kref_put(mxvar_sdriver); 1962a6afd9f3SGreg Kroah-Hartman return retval; 1963a6afd9f3SGreg Kroah-Hartman } 1964a6afd9f3SGreg Kroah-Hartman 1965a6afd9f3SGreg Kroah-Hartman static void __exit mxser_module_exit(void) 1966a6afd9f3SGreg Kroah-Hartman { 1967a6afd9f3SGreg Kroah-Hartman pci_unregister_driver(&mxser_driver); 1968a6afd9f3SGreg Kroah-Hartman tty_unregister_driver(mxvar_sdriver); 19699f90a4ddSJiri Slaby tty_driver_kref_put(mxvar_sdriver); 1970a6afd9f3SGreg Kroah-Hartman } 1971a6afd9f3SGreg Kroah-Hartman 1972a6afd9f3SGreg Kroah-Hartman module_init(mxser_module_init); 1973a6afd9f3SGreg Kroah-Hartman module_exit(mxser_module_exit); 1974