18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Original driver code supplied by Multi-Tech 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Changes 68c2ecf20Sopenharmony_ci * 1/9/98 alan@lxorguk.ukuu.org.uk 78c2ecf20Sopenharmony_ci * Merge to 2.0.x kernel tree 88c2ecf20Sopenharmony_ci * Obtain and use official major/minors 98c2ecf20Sopenharmony_ci * Loader switched to a misc device 108c2ecf20Sopenharmony_ci * (fixed range check bug as a side effect) 118c2ecf20Sopenharmony_ci * Printk clean up 128c2ecf20Sopenharmony_ci * 9/12/98 alan@lxorguk.ukuu.org.uk 138c2ecf20Sopenharmony_ci * Rough port to 2.1.x 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * 10/6/99 sameer Merged the ISA and PCI drivers to 168c2ecf20Sopenharmony_ci * a new unified driver. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * 3/9/99 sameer Added support for ISI4616 cards. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * 16/9/99 sameer We do not force RTS low anymore. 218c2ecf20Sopenharmony_ci * This is to prevent the firmware 228c2ecf20Sopenharmony_ci * from getting confused. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * 26/10/99 sameer Cosmetic changes:The driver now 258c2ecf20Sopenharmony_ci * dumps the Port Count information 268c2ecf20Sopenharmony_ci * along with I/O address and IRQ. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * 13/12/99 sameer Fixed the problem with IRQ sharing. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * 10/5/00 sameer Fixed isicom_shutdown_board() 318c2ecf20Sopenharmony_ci * to not lower DTR on all the ports 328c2ecf20Sopenharmony_ci * when the last port on the card is 338c2ecf20Sopenharmony_ci * closed. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * 10/5/00 sameer Signal mask setup command added 368c2ecf20Sopenharmony_ci * to isicom_setup_port and 378c2ecf20Sopenharmony_ci * isicom_shutdown_port. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * 24/5/00 sameer The driver is now SMP aware. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * 27/11/00 Vinayak P Risbud Fixed the Driver Crash Problem 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * 03/01/01 anil .s Added support for resetting the 468c2ecf20Sopenharmony_ci * internal modems on ISI cards. 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * 08/02/01 anil .s Upgraded the driver for kernel 498c2ecf20Sopenharmony_ci * 2.4.x 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * 11/04/01 Kevin Fixed firmware load problem with 528c2ecf20Sopenharmony_ci * ISIHP-4X card 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * 30/04/01 anil .s Fixed the remote login through 558c2ecf20Sopenharmony_ci * ISI port problem. Now the link 568c2ecf20Sopenharmony_ci * does not go down before password 578c2ecf20Sopenharmony_ci * prompt. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * 03/05/01 anil .s Fixed the problem with IRQ sharing 608c2ecf20Sopenharmony_ci * among ISI-PCI cards. 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * 03/05/01 anil .s Added support to display the version 638c2ecf20Sopenharmony_ci * info during insmod as well as module 648c2ecf20Sopenharmony_ci * listing by lsmod. 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * 10/05/01 anil .s Done the modifications to the source 678c2ecf20Sopenharmony_ci * file and Install script so that the 688c2ecf20Sopenharmony_ci * same installation can be used for 698c2ecf20Sopenharmony_ci * 2.2.x and 2.4.x kernel. 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * 06/06/01 anil .s Now we drop both dtr and rts during 728c2ecf20Sopenharmony_ci * shutdown_port as well as raise them 738c2ecf20Sopenharmony_ci * during isicom_config_port. 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * 09/06/01 acme@conectiva.com.br use capable, not suser, do 768c2ecf20Sopenharmony_ci * restore_flags on failure in 778c2ecf20Sopenharmony_ci * isicom_send_break, verify put_user 788c2ecf20Sopenharmony_ci * result 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * 11/02/03 ranjeeth Added support for 230 Kbps and 460 Kbps 818c2ecf20Sopenharmony_ci * Baud index extended to 21 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * 20/03/03 ranjeeth Made to work for Linux Advanced server. 848c2ecf20Sopenharmony_ci * Taken care of license warning. 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * 10/12/03 Ravindra Made to work for Fedora Core 1 of 878c2ecf20Sopenharmony_ci * Red Hat Distribution 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * 06/01/05 Alan Cox Merged the ISI and base kernel strands 908c2ecf20Sopenharmony_ci * into a single 2.6 driver 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * *********************************************************** 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * To use this driver you also need the support package. You 958c2ecf20Sopenharmony_ci * can find this in RPM format on 968c2ecf20Sopenharmony_ci * ftp://ftp.linux.org.uk/pub/linux/alan 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * You can find the original tools for this direct from Multitech 998c2ecf20Sopenharmony_ci * ftp://ftp.multitech.com/ISI-Cards/ 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Having installed the cards the module options (/etc/modprobe.d/) 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * options isicom io=card1,card2,card3,card4 irq=card1,card2,card3,card4 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * Omit those entries for boards you don't have installed. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * TODO 1088c2ecf20Sopenharmony_ci * Merge testing 1098c2ecf20Sopenharmony_ci * 64-bit verification 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#include <linux/module.h> 1158c2ecf20Sopenharmony_ci#include <linux/firmware.h> 1168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1178c2ecf20Sopenharmony_ci#include <linux/tty.h> 1188c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 1198c2ecf20Sopenharmony_ci#include <linux/termios.h> 1208c2ecf20Sopenharmony_ci#include <linux/fs.h> 1218c2ecf20Sopenharmony_ci#include <linux/sched.h> 1228c2ecf20Sopenharmony_ci#include <linux/serial.h> 1238c2ecf20Sopenharmony_ci#include <linux/mm.h> 1248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 1258c2ecf20Sopenharmony_ci#include <linux/timer.h> 1268c2ecf20Sopenharmony_ci#include <linux/delay.h> 1278c2ecf20Sopenharmony_ci#include <linux/ioport.h> 1288c2ecf20Sopenharmony_ci#include <linux/slab.h> 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1318c2ecf20Sopenharmony_ci#include <linux/io.h> 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#include <linux/pci.h> 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#include <linux/isicom.h> 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define InterruptTheCard(base) outw(0, (base) + 0xc) 1388c2ecf20Sopenharmony_ci#define ClearInterrupt(base) inw((base) + 0x0a) 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#ifdef DEBUG 1418c2ecf20Sopenharmony_ci#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c)) 1428c2ecf20Sopenharmony_ci#else 1438c2ecf20Sopenharmony_ci#define isicom_paranoia_check(a, b, c) 0 1448c2ecf20Sopenharmony_ci#endif 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int isicom_probe(struct pci_dev *, const struct pci_device_id *); 1478c2ecf20Sopenharmony_cistatic void isicom_remove(struct pci_dev *); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic const struct pci_device_id isicom_pci_tbl[] = { 1508c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2028) }, 1518c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2051) }, 1528c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2052) }, 1538c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2053) }, 1548c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2054) }, 1558c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2055) }, 1568c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2056) }, 1578c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2057) }, 1588c2ecf20Sopenharmony_ci { PCI_DEVICE(VENDOR_ID, 0x2058) }, 1598c2ecf20Sopenharmony_ci { 0 } 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, isicom_pci_tbl); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic struct pci_driver isicom_driver = { 1648c2ecf20Sopenharmony_ci .name = "isicom", 1658c2ecf20Sopenharmony_ci .id_table = isicom_pci_tbl, 1668c2ecf20Sopenharmony_ci .probe = isicom_probe, 1678c2ecf20Sopenharmony_ci .remove = isicom_remove 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int prev_card = 3; /* start servicing isi_card[0] */ 1718c2ecf20Sopenharmony_cistatic struct tty_driver *isicom_normal; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void isicom_tx(struct timer_list *unused); 1748c2ecf20Sopenharmony_cistatic void isicom_start(struct tty_struct *tty); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic DEFINE_TIMER(tx, isicom_tx); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* baud index mappings from linux defns to isi */ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic signed char linuxb_to_isib[] = { 1818c2ecf20Sopenharmony_ci -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistruct isi_board { 1858c2ecf20Sopenharmony_ci unsigned long base; 1868c2ecf20Sopenharmony_ci int irq; 1878c2ecf20Sopenharmony_ci unsigned char port_count; 1888c2ecf20Sopenharmony_ci unsigned short status; 1898c2ecf20Sopenharmony_ci unsigned short port_status; /* each bit for each port */ 1908c2ecf20Sopenharmony_ci unsigned short shift_count; 1918c2ecf20Sopenharmony_ci struct isi_port *ports; 1928c2ecf20Sopenharmony_ci signed char count; 1938c2ecf20Sopenharmony_ci spinlock_t card_lock; /* Card wide lock 11/5/00 -sameer */ 1948c2ecf20Sopenharmony_ci unsigned long flags; 1958c2ecf20Sopenharmony_ci unsigned int index; 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistruct isi_port { 1998c2ecf20Sopenharmony_ci unsigned short magic; 2008c2ecf20Sopenharmony_ci struct tty_port port; 2018c2ecf20Sopenharmony_ci u16 channel; 2028c2ecf20Sopenharmony_ci u16 status; 2038c2ecf20Sopenharmony_ci struct isi_board *card; 2048c2ecf20Sopenharmony_ci unsigned char *xmit_buf; 2058c2ecf20Sopenharmony_ci int xmit_head; 2068c2ecf20Sopenharmony_ci int xmit_tail; 2078c2ecf20Sopenharmony_ci int xmit_cnt; 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic struct isi_board isi_card[BOARD_COUNT]; 2118c2ecf20Sopenharmony_cistatic struct isi_port isi_ports[PORT_COUNT]; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * Locking functions for card level locking. We need to own both 2158c2ecf20Sopenharmony_ci * the kernel lock for the card and have the card in a position that 2168c2ecf20Sopenharmony_ci * it wants to talk. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int WaitTillCardIsFree(unsigned long base) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci unsigned int count = 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci while (!(inw(base + 0xe) & 0x1) && count++ < 100) 2248c2ecf20Sopenharmony_ci mdelay(1); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return !(inw(base + 0xe) & 0x1); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int lock_card(struct isi_board *card) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci unsigned long base = card->base; 2328c2ecf20Sopenharmony_ci unsigned int retries, a; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (retries = 0; retries < 10; retries++) { 2358c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, card->flags); 2368c2ecf20Sopenharmony_ci for (a = 0; a < 10; a++) { 2378c2ecf20Sopenharmony_ci if (inw(base + 0xe) & 0x1) 2388c2ecf20Sopenharmony_ci return 1; 2398c2ecf20Sopenharmony_ci udelay(10); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, card->flags); 2428c2ecf20Sopenharmony_ci msleep(10); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci pr_warn("Failed to lock Card (0x%lx)\n", card->base); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; /* Failed to acquire the card! */ 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void unlock_card(struct isi_board *card) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, card->flags); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* 2558c2ecf20Sopenharmony_ci * ISI Card specific ops ... 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* card->lock HAS to be held */ 2598c2ecf20Sopenharmony_cistatic void raise_dtr(struct isi_port *port) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 2628c2ecf20Sopenharmony_ci unsigned long base = card->base; 2638c2ecf20Sopenharmony_ci u16 channel = port->channel; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 2668c2ecf20Sopenharmony_ci return; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci outw(0x8000 | (channel << card->shift_count) | 0x02, base); 2698c2ecf20Sopenharmony_ci outw(0x0504, base); 2708c2ecf20Sopenharmony_ci InterruptTheCard(base); 2718c2ecf20Sopenharmony_ci port->status |= ISI_DTR; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* card->lock HAS to be held */ 2758c2ecf20Sopenharmony_cistatic void drop_dtr(struct isi_port *port) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 2788c2ecf20Sopenharmony_ci unsigned long base = card->base; 2798c2ecf20Sopenharmony_ci u16 channel = port->channel; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci outw(0x8000 | (channel << card->shift_count) | 0x02, base); 2858c2ecf20Sopenharmony_ci outw(0x0404, base); 2868c2ecf20Sopenharmony_ci InterruptTheCard(base); 2878c2ecf20Sopenharmony_ci port->status &= ~ISI_DTR; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* card->lock HAS to be held */ 2918c2ecf20Sopenharmony_cistatic inline void raise_rts(struct isi_port *port) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 2948c2ecf20Sopenharmony_ci unsigned long base = card->base; 2958c2ecf20Sopenharmony_ci u16 channel = port->channel; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 2988c2ecf20Sopenharmony_ci return; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci outw(0x8000 | (channel << card->shift_count) | 0x02, base); 3018c2ecf20Sopenharmony_ci outw(0x0a04, base); 3028c2ecf20Sopenharmony_ci InterruptTheCard(base); 3038c2ecf20Sopenharmony_ci port->status |= ISI_RTS; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* card->lock HAS to be held */ 3078c2ecf20Sopenharmony_cistatic inline void drop_rts(struct isi_port *port) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 3108c2ecf20Sopenharmony_ci unsigned long base = card->base; 3118c2ecf20Sopenharmony_ci u16 channel = port->channel; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 3148c2ecf20Sopenharmony_ci return; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci outw(0x8000 | (channel << card->shift_count) | 0x02, base); 3178c2ecf20Sopenharmony_ci outw(0x0804, base); 3188c2ecf20Sopenharmony_ci InterruptTheCard(base); 3198c2ecf20Sopenharmony_ci port->status &= ~ISI_RTS; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/* card->lock MUST NOT be held */ 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void isicom_dtr_rts(struct tty_port *port, int on) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct isi_port *ip = container_of(port, struct isi_port, port); 3278c2ecf20Sopenharmony_ci struct isi_board *card = ip->card; 3288c2ecf20Sopenharmony_ci unsigned long base = card->base; 3298c2ecf20Sopenharmony_ci u16 channel = ip->channel; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (!lock_card(card)) 3328c2ecf20Sopenharmony_ci return; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (on) { 3358c2ecf20Sopenharmony_ci outw(0x8000 | (channel << card->shift_count) | 0x02, base); 3368c2ecf20Sopenharmony_ci outw(0x0f04, base); 3378c2ecf20Sopenharmony_ci InterruptTheCard(base); 3388c2ecf20Sopenharmony_ci ip->status |= (ISI_DTR | ISI_RTS); 3398c2ecf20Sopenharmony_ci } else { 3408c2ecf20Sopenharmony_ci outw(0x8000 | (channel << card->shift_count) | 0x02, base); 3418c2ecf20Sopenharmony_ci outw(0x0C04, base); 3428c2ecf20Sopenharmony_ci InterruptTheCard(base); 3438c2ecf20Sopenharmony_ci ip->status &= ~(ISI_DTR | ISI_RTS); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci unlock_card(card); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* card->lock HAS to be held */ 3498c2ecf20Sopenharmony_cistatic void drop_dtr_rts(struct isi_port *port) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 3528c2ecf20Sopenharmony_ci unsigned long base = card->base; 3538c2ecf20Sopenharmony_ci u16 channel = port->channel; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 3568c2ecf20Sopenharmony_ci return; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci outw(0x8000 | (channel << card->shift_count) | 0x02, base); 3598c2ecf20Sopenharmony_ci outw(0x0c04, base); 3608c2ecf20Sopenharmony_ci InterruptTheCard(base); 3618c2ecf20Sopenharmony_ci port->status &= ~(ISI_RTS | ISI_DTR); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* 3658c2ecf20Sopenharmony_ci * ISICOM Driver specific routines ... 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic inline int __isicom_paranoia_check(struct isi_port const *port, 3708c2ecf20Sopenharmony_ci char *name, const char *routine) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci if (!port) { 3738c2ecf20Sopenharmony_ci pr_warn("Warning: bad isicom magic for dev %s in %s\n", 3748c2ecf20Sopenharmony_ci name, routine); 3758c2ecf20Sopenharmony_ci return 1; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci if (port->magic != ISICOM_MAGIC) { 3788c2ecf20Sopenharmony_ci pr_warn("Warning: NULL isicom port for dev %s in %s\n", 3798c2ecf20Sopenharmony_ci name, routine); 3808c2ecf20Sopenharmony_ci return 1; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * Transmitter. 3888c2ecf20Sopenharmony_ci * 3898c2ecf20Sopenharmony_ci * We shovel data into the card buffers on a regular basis. The card 3908c2ecf20Sopenharmony_ci * will do the rest of the work for us. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic void isicom_tx(struct timer_list *unused) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci unsigned long flags, base; 3968c2ecf20Sopenharmony_ci unsigned int retries; 3978c2ecf20Sopenharmony_ci short count = (BOARD_COUNT-1), card; 3988c2ecf20Sopenharmony_ci short txcount, wrd, residue, word_count, cnt; 3998c2ecf20Sopenharmony_ci struct isi_port *port; 4008c2ecf20Sopenharmony_ci struct tty_struct *tty; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* find next active board */ 4038c2ecf20Sopenharmony_ci card = (prev_card + 1) & 0x0003; 4048c2ecf20Sopenharmony_ci while (count-- > 0) { 4058c2ecf20Sopenharmony_ci if (isi_card[card].status & BOARD_ACTIVE) 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci card = (card + 1) & 0x0003; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci if (!(isi_card[card].status & BOARD_ACTIVE)) 4108c2ecf20Sopenharmony_ci goto sched_again; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci prev_card = card; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci count = isi_card[card].port_count; 4158c2ecf20Sopenharmony_ci port = isi_card[card].ports; 4168c2ecf20Sopenharmony_ci base = isi_card[card].base; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci spin_lock_irqsave(&isi_card[card].card_lock, flags); 4198c2ecf20Sopenharmony_ci for (retries = 0; retries < 100; retries++) { 4208c2ecf20Sopenharmony_ci if (inw(base + 0xe) & 0x1) 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci udelay(2); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci if (retries >= 100) 4258c2ecf20Sopenharmony_ci goto unlock; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci tty = tty_port_tty_get(&port->port); 4288c2ecf20Sopenharmony_ci if (tty == NULL) 4298c2ecf20Sopenharmony_ci goto put_unlock; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci for (; count > 0; count--, port++) { 4328c2ecf20Sopenharmony_ci /* port not active or tx disabled to force flow control */ 4338c2ecf20Sopenharmony_ci if (!tty_port_initialized(&port->port) || 4348c2ecf20Sopenharmony_ci !(port->status & ISI_TXOK)) 4358c2ecf20Sopenharmony_ci continue; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci txcount = min_t(short, TX_SIZE, port->xmit_cnt); 4388c2ecf20Sopenharmony_ci if (txcount <= 0 || tty->stopped || tty->hw_stopped) 4398c2ecf20Sopenharmony_ci continue; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (!(inw(base + 0x02) & (1 << port->channel))) 4428c2ecf20Sopenharmony_ci continue; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci pr_debug("txing %d bytes, port%d.\n", 4458c2ecf20Sopenharmony_ci txcount, port->channel + 1); 4468c2ecf20Sopenharmony_ci outw((port->channel << isi_card[card].shift_count) | txcount, 4478c2ecf20Sopenharmony_ci base); 4488c2ecf20Sopenharmony_ci residue = NO; 4498c2ecf20Sopenharmony_ci wrd = 0; 4508c2ecf20Sopenharmony_ci while (1) { 4518c2ecf20Sopenharmony_ci cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE 4528c2ecf20Sopenharmony_ci - port->xmit_tail)); 4538c2ecf20Sopenharmony_ci if (residue == YES) { 4548c2ecf20Sopenharmony_ci residue = NO; 4558c2ecf20Sopenharmony_ci if (cnt > 0) { 4568c2ecf20Sopenharmony_ci wrd |= (port->port.xmit_buf[port->xmit_tail] 4578c2ecf20Sopenharmony_ci << 8); 4588c2ecf20Sopenharmony_ci port->xmit_tail = (port->xmit_tail + 1) 4598c2ecf20Sopenharmony_ci & (SERIAL_XMIT_SIZE - 1); 4608c2ecf20Sopenharmony_ci port->xmit_cnt--; 4618c2ecf20Sopenharmony_ci txcount--; 4628c2ecf20Sopenharmony_ci cnt--; 4638c2ecf20Sopenharmony_ci outw(wrd, base); 4648c2ecf20Sopenharmony_ci } else { 4658c2ecf20Sopenharmony_ci outw(wrd, base); 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci if (cnt <= 0) 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci word_count = cnt >> 1; 4728c2ecf20Sopenharmony_ci outsw(base, port->port.xmit_buf+port->xmit_tail, word_count); 4738c2ecf20Sopenharmony_ci port->xmit_tail = (port->xmit_tail 4748c2ecf20Sopenharmony_ci + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1); 4758c2ecf20Sopenharmony_ci txcount -= (word_count << 1); 4768c2ecf20Sopenharmony_ci port->xmit_cnt -= (word_count << 1); 4778c2ecf20Sopenharmony_ci if (cnt & 0x0001) { 4788c2ecf20Sopenharmony_ci residue = YES; 4798c2ecf20Sopenharmony_ci wrd = port->port.xmit_buf[port->xmit_tail]; 4808c2ecf20Sopenharmony_ci port->xmit_tail = (port->xmit_tail + 1) 4818c2ecf20Sopenharmony_ci & (SERIAL_XMIT_SIZE - 1); 4828c2ecf20Sopenharmony_ci port->xmit_cnt--; 4838c2ecf20Sopenharmony_ci txcount--; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci InterruptTheCard(base); 4888c2ecf20Sopenharmony_ci if (port->xmit_cnt <= 0) 4898c2ecf20Sopenharmony_ci port->status &= ~ISI_TXOK; 4908c2ecf20Sopenharmony_ci if (port->xmit_cnt <= WAKEUP_CHARS) 4918c2ecf20Sopenharmony_ci tty_wakeup(tty); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ciput_unlock: 4958c2ecf20Sopenharmony_ci tty_kref_put(tty); 4968c2ecf20Sopenharmony_ciunlock: 4978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&isi_card[card].card_lock, flags); 4988c2ecf20Sopenharmony_ci /* schedule another tx for hopefully in about 10ms */ 4998c2ecf20Sopenharmony_cisched_again: 5008c2ecf20Sopenharmony_ci mod_timer(&tx, jiffies + msecs_to_jiffies(10)); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/* 5048c2ecf20Sopenharmony_ci * Main interrupt handler routine 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic irqreturn_t isicom_interrupt(int irq, void *dev_id) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct isi_board *card = dev_id; 5108c2ecf20Sopenharmony_ci struct isi_port *port; 5118c2ecf20Sopenharmony_ci struct tty_struct *tty; 5128c2ecf20Sopenharmony_ci unsigned long base; 5138c2ecf20Sopenharmony_ci u16 header, word_count, count, channel; 5148c2ecf20Sopenharmony_ci short byte_count; 5158c2ecf20Sopenharmony_ci unsigned char *rp; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (!card || !(card->status & FIRMWARE_LOADED)) 5188c2ecf20Sopenharmony_ci return IRQ_NONE; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci base = card->base; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* did the card interrupt us? */ 5238c2ecf20Sopenharmony_ci if (!(inw(base + 0x0e) & 0x02)) 5248c2ecf20Sopenharmony_ci return IRQ_NONE; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci spin_lock(&card->card_lock); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * disable any interrupts from the PCI card and lower the 5308c2ecf20Sopenharmony_ci * interrupt line 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci outw(0x8000, base+0x04); 5338c2ecf20Sopenharmony_ci ClearInterrupt(base); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci inw(base); /* get the dummy word out */ 5368c2ecf20Sopenharmony_ci header = inw(base); 5378c2ecf20Sopenharmony_ci channel = (header & 0x7800) >> card->shift_count; 5388c2ecf20Sopenharmony_ci byte_count = header & 0xff; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (channel + 1 > card->port_count) { 5418c2ecf20Sopenharmony_ci pr_warn("%s(0x%lx): %d(channel) > port_count\n", 5428c2ecf20Sopenharmony_ci __func__, base, channel + 1); 5438c2ecf20Sopenharmony_ci outw(0x0000, base+0x04); /* enable interrupts */ 5448c2ecf20Sopenharmony_ci spin_unlock(&card->card_lock); 5458c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci port = card->ports + channel; 5488c2ecf20Sopenharmony_ci if (!tty_port_initialized(&port->port)) { 5498c2ecf20Sopenharmony_ci outw(0x0000, base+0x04); /* enable interrupts */ 5508c2ecf20Sopenharmony_ci spin_unlock(&card->card_lock); 5518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci tty = tty_port_tty_get(&port->port); 5558c2ecf20Sopenharmony_ci if (tty == NULL) { 5568c2ecf20Sopenharmony_ci while (byte_count > 1) { 5578c2ecf20Sopenharmony_ci inw(base); 5588c2ecf20Sopenharmony_ci byte_count -= 2; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci if (byte_count & 0x01) 5618c2ecf20Sopenharmony_ci inw(base); 5628c2ecf20Sopenharmony_ci outw(0x0000, base+0x04); /* enable interrupts */ 5638c2ecf20Sopenharmony_ci spin_unlock(&card->card_lock); 5648c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (header & 0x8000) { /* Status Packet */ 5688c2ecf20Sopenharmony_ci header = inw(base); 5698c2ecf20Sopenharmony_ci switch (header & 0xff) { 5708c2ecf20Sopenharmony_ci case 0: /* Change in EIA signals */ 5718c2ecf20Sopenharmony_ci if (tty_port_check_carrier(&port->port)) { 5728c2ecf20Sopenharmony_ci if (port->status & ISI_DCD) { 5738c2ecf20Sopenharmony_ci if (!(header & ISI_DCD)) { 5748c2ecf20Sopenharmony_ci /* Carrier has been lost */ 5758c2ecf20Sopenharmony_ci pr_debug("%s: DCD->low.\n", 5768c2ecf20Sopenharmony_ci __func__); 5778c2ecf20Sopenharmony_ci port->status &= ~ISI_DCD; 5788c2ecf20Sopenharmony_ci tty_hangup(tty); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci } else if (header & ISI_DCD) { 5818c2ecf20Sopenharmony_ci /* Carrier has been detected */ 5828c2ecf20Sopenharmony_ci pr_debug("%s: DCD->high.\n", 5838c2ecf20Sopenharmony_ci __func__); 5848c2ecf20Sopenharmony_ci port->status |= ISI_DCD; 5858c2ecf20Sopenharmony_ci wake_up_interruptible(&port->port.open_wait); 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci } else { 5888c2ecf20Sopenharmony_ci if (header & ISI_DCD) 5898c2ecf20Sopenharmony_ci port->status |= ISI_DCD; 5908c2ecf20Sopenharmony_ci else 5918c2ecf20Sopenharmony_ci port->status &= ~ISI_DCD; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (tty_port_cts_enabled(&port->port)) { 5958c2ecf20Sopenharmony_ci if (tty->hw_stopped) { 5968c2ecf20Sopenharmony_ci if (header & ISI_CTS) { 5978c2ecf20Sopenharmony_ci tty->hw_stopped = 0; 5988c2ecf20Sopenharmony_ci /* start tx ing */ 5998c2ecf20Sopenharmony_ci port->status |= (ISI_TXOK 6008c2ecf20Sopenharmony_ci | ISI_CTS); 6018c2ecf20Sopenharmony_ci tty_wakeup(tty); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci } else if (!(header & ISI_CTS)) { 6048c2ecf20Sopenharmony_ci tty->hw_stopped = 1; 6058c2ecf20Sopenharmony_ci /* stop tx ing */ 6068c2ecf20Sopenharmony_ci port->status &= ~(ISI_TXOK | ISI_CTS); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci } else { 6098c2ecf20Sopenharmony_ci if (header & ISI_CTS) 6108c2ecf20Sopenharmony_ci port->status |= ISI_CTS; 6118c2ecf20Sopenharmony_ci else 6128c2ecf20Sopenharmony_ci port->status &= ~ISI_CTS; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (header & ISI_DSR) 6168c2ecf20Sopenharmony_ci port->status |= ISI_DSR; 6178c2ecf20Sopenharmony_ci else 6188c2ecf20Sopenharmony_ci port->status &= ~ISI_DSR; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (header & ISI_RI) 6218c2ecf20Sopenharmony_ci port->status |= ISI_RI; 6228c2ecf20Sopenharmony_ci else 6238c2ecf20Sopenharmony_ci port->status &= ~ISI_RI; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci case 1: /* Received Break !!! */ 6288c2ecf20Sopenharmony_ci tty_insert_flip_char(&port->port, 0, TTY_BREAK); 6298c2ecf20Sopenharmony_ci if (port->port.flags & ASYNC_SAK) 6308c2ecf20Sopenharmony_ci do_SAK(tty); 6318c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->port); 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci case 2: /* Statistics */ 6358c2ecf20Sopenharmony_ci pr_debug("%s: stats!!!\n", __func__); 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci default: 6398c2ecf20Sopenharmony_ci pr_debug("%s: Unknown code in status packet.\n", 6408c2ecf20Sopenharmony_ci __func__); 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci } else { /* Data Packet */ 6448c2ecf20Sopenharmony_ci count = tty_prepare_flip_string(&port->port, &rp, 6458c2ecf20Sopenharmony_ci byte_count & ~1); 6468c2ecf20Sopenharmony_ci pr_debug("%s: Can rx %d of %d bytes.\n", 6478c2ecf20Sopenharmony_ci __func__, count, byte_count); 6488c2ecf20Sopenharmony_ci word_count = count >> 1; 6498c2ecf20Sopenharmony_ci insw(base, rp, word_count); 6508c2ecf20Sopenharmony_ci byte_count -= (word_count << 1); 6518c2ecf20Sopenharmony_ci if (count & 0x0001) { 6528c2ecf20Sopenharmony_ci tty_insert_flip_char(&port->port, inw(base) & 0xff, 6538c2ecf20Sopenharmony_ci TTY_NORMAL); 6548c2ecf20Sopenharmony_ci byte_count -= 2; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci if (byte_count > 0) { 6578c2ecf20Sopenharmony_ci pr_debug("%s(0x%lx:%d): Flip buffer overflow! dropping bytes...\n", 6588c2ecf20Sopenharmony_ci __func__, base, channel + 1); 6598c2ecf20Sopenharmony_ci /* drain out unread xtra data */ 6608c2ecf20Sopenharmony_ci while (byte_count > 0) { 6618c2ecf20Sopenharmony_ci inw(base); 6628c2ecf20Sopenharmony_ci byte_count -= 2; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->port); 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci outw(0x0000, base+0x04); /* enable interrupts */ 6688c2ecf20Sopenharmony_ci spin_unlock(&card->card_lock); 6698c2ecf20Sopenharmony_ci tty_kref_put(tty); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic void isicom_config_port(struct tty_struct *tty) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 6778c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 6788c2ecf20Sopenharmony_ci unsigned long baud; 6798c2ecf20Sopenharmony_ci unsigned long base = card->base; 6808c2ecf20Sopenharmony_ci u16 channel_setup, channel = port->channel, 6818c2ecf20Sopenharmony_ci shift_count = card->shift_count; 6828c2ecf20Sopenharmony_ci unsigned char flow_ctrl; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* FIXME: Switch to new tty baud API */ 6858c2ecf20Sopenharmony_ci baud = C_BAUD(tty); 6868c2ecf20Sopenharmony_ci if (baud & CBAUDEX) { 6878c2ecf20Sopenharmony_ci baud &= ~CBAUDEX; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* if CBAUDEX bit is on and the baud is set to either 50 or 75 6908c2ecf20Sopenharmony_ci * then the card is programmed for 57.6Kbps or 115Kbps 6918c2ecf20Sopenharmony_ci * respectively. 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ 6958c2ecf20Sopenharmony_ci if (baud < 1 || baud > 4) 6968c2ecf20Sopenharmony_ci tty->termios.c_cflag &= ~CBAUDEX; 6978c2ecf20Sopenharmony_ci else 6988c2ecf20Sopenharmony_ci baud += 15; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci if (baud == 15) { 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set 7038c2ecf20Sopenharmony_ci * by the set_serial_info ioctl ... this is done by 7048c2ecf20Sopenharmony_ci * the 'setserial' utility. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) 7088c2ecf20Sopenharmony_ci baud++; /* 57.6 Kbps */ 7098c2ecf20Sopenharmony_ci if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) 7108c2ecf20Sopenharmony_ci baud += 2; /* 115 Kbps */ 7118c2ecf20Sopenharmony_ci if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) 7128c2ecf20Sopenharmony_ci baud += 3; /* 230 kbps*/ 7138c2ecf20Sopenharmony_ci if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) 7148c2ecf20Sopenharmony_ci baud += 4; /* 460 kbps*/ 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci if (linuxb_to_isib[baud] == -1) { 7178c2ecf20Sopenharmony_ci /* hang up */ 7188c2ecf20Sopenharmony_ci drop_dtr(port); 7198c2ecf20Sopenharmony_ci return; 7208c2ecf20Sopenharmony_ci } else 7218c2ecf20Sopenharmony_ci raise_dtr(port); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base) == 0) { 7248c2ecf20Sopenharmony_ci outw(0x8000 | (channel << shift_count) | 0x03, base); 7258c2ecf20Sopenharmony_ci outw(linuxb_to_isib[baud] << 8 | 0x03, base); 7268c2ecf20Sopenharmony_ci channel_setup = 0; 7278c2ecf20Sopenharmony_ci switch (C_CSIZE(tty)) { 7288c2ecf20Sopenharmony_ci case CS5: 7298c2ecf20Sopenharmony_ci channel_setup |= ISICOM_CS5; 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci case CS6: 7328c2ecf20Sopenharmony_ci channel_setup |= ISICOM_CS6; 7338c2ecf20Sopenharmony_ci break; 7348c2ecf20Sopenharmony_ci case CS7: 7358c2ecf20Sopenharmony_ci channel_setup |= ISICOM_CS7; 7368c2ecf20Sopenharmony_ci break; 7378c2ecf20Sopenharmony_ci case CS8: 7388c2ecf20Sopenharmony_ci channel_setup |= ISICOM_CS8; 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (C_CSTOPB(tty)) 7438c2ecf20Sopenharmony_ci channel_setup |= ISICOM_2SB; 7448c2ecf20Sopenharmony_ci if (C_PARENB(tty)) { 7458c2ecf20Sopenharmony_ci channel_setup |= ISICOM_EVPAR; 7468c2ecf20Sopenharmony_ci if (C_PARODD(tty)) 7478c2ecf20Sopenharmony_ci channel_setup |= ISICOM_ODPAR; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci outw(channel_setup, base); 7508c2ecf20Sopenharmony_ci InterruptTheCard(base); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci tty_port_set_check_carrier(&port->port, !C_CLOCAL(tty)); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* flow control settings ...*/ 7558c2ecf20Sopenharmony_ci flow_ctrl = 0; 7568c2ecf20Sopenharmony_ci tty_port_set_cts_flow(&port->port, C_CRTSCTS(tty)); 7578c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) 7588c2ecf20Sopenharmony_ci flow_ctrl |= ISICOM_CTSRTS; 7598c2ecf20Sopenharmony_ci if (I_IXON(tty)) 7608c2ecf20Sopenharmony_ci flow_ctrl |= ISICOM_RESPOND_XONXOFF; 7618c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) 7628c2ecf20Sopenharmony_ci flow_ctrl |= ISICOM_INITIATE_XONXOFF; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base) == 0) { 7658c2ecf20Sopenharmony_ci outw(0x8000 | (channel << shift_count) | 0x04, base); 7668c2ecf20Sopenharmony_ci outw(flow_ctrl << 8 | 0x05, base); 7678c2ecf20Sopenharmony_ci outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); 7688c2ecf20Sopenharmony_ci InterruptTheCard(base); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* rx enabled -> enable port for rx on the card */ 7728c2ecf20Sopenharmony_ci if (C_CREAD(tty)) { 7738c2ecf20Sopenharmony_ci card->port_status |= (1 << channel); 7748c2ecf20Sopenharmony_ci outw(card->port_status, base + 0x02); 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci/* open et all */ 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic inline void isicom_setup_board(struct isi_board *bp) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci int channel; 7838c2ecf20Sopenharmony_ci struct isi_port *port; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci bp->count++; 7868c2ecf20Sopenharmony_ci if (!(bp->status & BOARD_INIT)) { 7878c2ecf20Sopenharmony_ci port = bp->ports; 7888c2ecf20Sopenharmony_ci for (channel = 0; channel < bp->port_count; channel++, port++) 7898c2ecf20Sopenharmony_ci drop_dtr_rts(port); 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci bp->status |= BOARD_ACTIVE | BOARD_INIT; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci/* Activate and thus setup board are protected from races against shutdown 7958c2ecf20Sopenharmony_ci by the tty_port mutex */ 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic int isicom_activate(struct tty_port *tport, struct tty_struct *tty) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct isi_port *port = container_of(tport, struct isi_port, port); 8008c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 8018c2ecf20Sopenharmony_ci unsigned long flags; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci if (tty_port_alloc_xmit_buf(tport) < 0) 8048c2ecf20Sopenharmony_ci return -ENOMEM; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 8078c2ecf20Sopenharmony_ci isicom_setup_board(card); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* discard any residual data */ 8128c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(card->base) == 0) { 8138c2ecf20Sopenharmony_ci outw(0x8000 | (port->channel << card->shift_count) | 0x02, 8148c2ecf20Sopenharmony_ci card->base); 8158c2ecf20Sopenharmony_ci outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base); 8168c2ecf20Sopenharmony_ci InterruptTheCard(card->base); 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci isicom_config_port(tty); 8198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int isicom_carrier_raised(struct tty_port *port) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct isi_port *ip = container_of(port, struct isi_port, port); 8278c2ecf20Sopenharmony_ci return (ip->status & ISI_DCD)?1 : 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic struct tty_port *isicom_find_port(struct tty_struct *tty) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct isi_port *port; 8338c2ecf20Sopenharmony_ci struct isi_board *card; 8348c2ecf20Sopenharmony_ci unsigned int board; 8358c2ecf20Sopenharmony_ci int line = tty->index; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci board = BOARD(line); 8388c2ecf20Sopenharmony_ci card = &isi_card[board]; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci if (!(card->status & FIRMWARE_LOADED)) 8418c2ecf20Sopenharmony_ci return NULL; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* open on a port greater than the port count for the card !!! */ 8448c2ecf20Sopenharmony_ci if (line > ((board * 16) + card->port_count - 1)) 8458c2ecf20Sopenharmony_ci return NULL; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci port = &isi_ports[line]; 8488c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_open")) 8498c2ecf20Sopenharmony_ci return NULL; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return &port->port; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int isicom_open(struct tty_struct *tty, struct file *filp) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct isi_port *port; 8578c2ecf20Sopenharmony_ci struct tty_port *tport; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci tport = isicom_find_port(tty); 8608c2ecf20Sopenharmony_ci if (tport == NULL) 8618c2ecf20Sopenharmony_ci return -ENODEV; 8628c2ecf20Sopenharmony_ci port = container_of(tport, struct isi_port, port); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci tty->driver_data = port; 8658c2ecf20Sopenharmony_ci return tty_port_open(tport, tty, filp); 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci/* close et all */ 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci/* card->lock HAS to be held */ 8718c2ecf20Sopenharmony_cistatic void isicom_shutdown_port(struct isi_port *port) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (--card->count < 0) { 8768c2ecf20Sopenharmony_ci pr_debug("%s: bad board(0x%lx) count %d.\n", 8778c2ecf20Sopenharmony_ci __func__, card->base, card->count); 8788c2ecf20Sopenharmony_ci card->count = 0; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci /* last port was closed, shutdown that board too */ 8818c2ecf20Sopenharmony_ci if (!card->count) 8828c2ecf20Sopenharmony_ci card->status &= BOARD_ACTIVE; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic void isicom_flush_buffer(struct tty_struct *tty) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 8888c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 8898c2ecf20Sopenharmony_ci unsigned long flags; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer")) 8928c2ecf20Sopenharmony_ci return; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 8958c2ecf20Sopenharmony_ci port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; 8968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci tty_wakeup(tty); 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cistatic void isicom_shutdown(struct tty_port *port) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci struct isi_port *ip = container_of(port, struct isi_port, port); 9048c2ecf20Sopenharmony_ci struct isi_board *card = ip->card; 9058c2ecf20Sopenharmony_ci unsigned long flags; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* indicate to the card that no more data can be received 9088c2ecf20Sopenharmony_ci on this port */ 9098c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 9108c2ecf20Sopenharmony_ci card->port_status &= ~(1 << ip->channel); 9118c2ecf20Sopenharmony_ci outw(card->port_status, card->base + 0x02); 9128c2ecf20Sopenharmony_ci isicom_shutdown_port(ip); 9138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 9148c2ecf20Sopenharmony_ci tty_port_free_xmit_buf(port); 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic void isicom_close(struct tty_struct *tty, struct file *filp) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct isi_port *ip = tty->driver_data; 9208c2ecf20Sopenharmony_ci struct tty_port *port; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (ip == NULL) 9238c2ecf20Sopenharmony_ci return; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci port = &ip->port; 9268c2ecf20Sopenharmony_ci if (isicom_paranoia_check(ip, tty->name, "isicom_close")) 9278c2ecf20Sopenharmony_ci return; 9288c2ecf20Sopenharmony_ci tty_port_close(port, tty, filp); 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci/* write et all */ 9328c2ecf20Sopenharmony_cistatic int isicom_write(struct tty_struct *tty, const unsigned char *buf, 9338c2ecf20Sopenharmony_ci int count) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 9368c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 9378c2ecf20Sopenharmony_ci unsigned long flags; 9388c2ecf20Sopenharmony_ci int cnt, total = 0; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_write")) 9418c2ecf20Sopenharmony_ci return 0; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci while (1) { 9468c2ecf20Sopenharmony_ci cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt 9478c2ecf20Sopenharmony_ci - 1, SERIAL_XMIT_SIZE - port->xmit_head)); 9488c2ecf20Sopenharmony_ci if (cnt <= 0) 9498c2ecf20Sopenharmony_ci break; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt); 9528c2ecf20Sopenharmony_ci port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE 9538c2ecf20Sopenharmony_ci - 1); 9548c2ecf20Sopenharmony_ci port->xmit_cnt += cnt; 9558c2ecf20Sopenharmony_ci buf += cnt; 9568c2ecf20Sopenharmony_ci count -= cnt; 9578c2ecf20Sopenharmony_ci total += cnt; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped) 9608c2ecf20Sopenharmony_ci port->status |= ISI_TXOK; 9618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 9628c2ecf20Sopenharmony_ci return total; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci/* put_char et all */ 9668c2ecf20Sopenharmony_cistatic int isicom_put_char(struct tty_struct *tty, unsigned char ch) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 9698c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 9708c2ecf20Sopenharmony_ci unsigned long flags; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_put_char")) 9738c2ecf20Sopenharmony_ci return 0; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 9768c2ecf20Sopenharmony_ci if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { 9778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci port->port.xmit_buf[port->xmit_head++] = ch; 9828c2ecf20Sopenharmony_ci port->xmit_head &= (SERIAL_XMIT_SIZE - 1); 9838c2ecf20Sopenharmony_ci port->xmit_cnt++; 9848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 9858c2ecf20Sopenharmony_ci return 1; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci/* flush_chars et all */ 9898c2ecf20Sopenharmony_cistatic void isicom_flush_chars(struct tty_struct *tty) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars")) 9948c2ecf20Sopenharmony_ci return; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || 9978c2ecf20Sopenharmony_ci !port->port.xmit_buf) 9988c2ecf20Sopenharmony_ci return; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* this tells the transmitter to consider this port for 10018c2ecf20Sopenharmony_ci data output to the card ... that's the best we can do. */ 10028c2ecf20Sopenharmony_ci port->status |= ISI_TXOK; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci/* write_room et all */ 10068c2ecf20Sopenharmony_cistatic int isicom_write_room(struct tty_struct *tty) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 10098c2ecf20Sopenharmony_ci int free; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_write_room")) 10128c2ecf20Sopenharmony_ci return 0; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; 10158c2ecf20Sopenharmony_ci if (free < 0) 10168c2ecf20Sopenharmony_ci free = 0; 10178c2ecf20Sopenharmony_ci return free; 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci/* chars_in_buffer et all */ 10218c2ecf20Sopenharmony_cistatic int isicom_chars_in_buffer(struct tty_struct *tty) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 10248c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer")) 10258c2ecf20Sopenharmony_ci return 0; 10268c2ecf20Sopenharmony_ci return port->xmit_cnt; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci/* ioctl et all */ 10308c2ecf20Sopenharmony_cistatic int isicom_send_break(struct tty_struct *tty, int length) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 10338c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 10348c2ecf20Sopenharmony_ci unsigned long base = card->base; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (length == -1) 10378c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (!lock_card(card)) 10408c2ecf20Sopenharmony_ci return -EINVAL; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base); 10438c2ecf20Sopenharmony_ci outw((length & 0xff) << 8 | 0x00, base); 10448c2ecf20Sopenharmony_ci outw((length & 0xff00u), base); 10458c2ecf20Sopenharmony_ci InterruptTheCard(base); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci unlock_card(card); 10488c2ecf20Sopenharmony_ci return 0; 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic int isicom_tiocmget(struct tty_struct *tty) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 10548c2ecf20Sopenharmony_ci /* just send the port status */ 10558c2ecf20Sopenharmony_ci u16 status = port->status; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) 10588c2ecf20Sopenharmony_ci return -ENODEV; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return ((status & ISI_RTS) ? TIOCM_RTS : 0) | 10618c2ecf20Sopenharmony_ci ((status & ISI_DTR) ? TIOCM_DTR : 0) | 10628c2ecf20Sopenharmony_ci ((status & ISI_DCD) ? TIOCM_CAR : 0) | 10638c2ecf20Sopenharmony_ci ((status & ISI_DSR) ? TIOCM_DSR : 0) | 10648c2ecf20Sopenharmony_ci ((status & ISI_CTS) ? TIOCM_CTS : 0) | 10658c2ecf20Sopenharmony_ci ((status & ISI_RI ) ? TIOCM_RI : 0); 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic int isicom_tiocmset(struct tty_struct *tty, 10698c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 10728c2ecf20Sopenharmony_ci unsigned long flags; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) 10758c2ecf20Sopenharmony_ci return -ENODEV; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->card->card_lock, flags); 10788c2ecf20Sopenharmony_ci if (set & TIOCM_RTS) 10798c2ecf20Sopenharmony_ci raise_rts(port); 10808c2ecf20Sopenharmony_ci if (set & TIOCM_DTR) 10818c2ecf20Sopenharmony_ci raise_dtr(port); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (clear & TIOCM_RTS) 10848c2ecf20Sopenharmony_ci drop_rts(port); 10858c2ecf20Sopenharmony_ci if (clear & TIOCM_DTR) 10868c2ecf20Sopenharmony_ci drop_dtr(port); 10878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->card->card_lock, flags); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci return 0; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic int isicom_set_serial_info(struct tty_struct *tty, 10938c2ecf20Sopenharmony_ci struct serial_struct *ss) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 10968c2ecf20Sopenharmony_ci int reconfig_port; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) 10998c2ecf20Sopenharmony_ci return -ENODEV; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci mutex_lock(&port->port.mutex); 11028c2ecf20Sopenharmony_ci reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) != 11038c2ecf20Sopenharmony_ci (ss->flags & ASYNC_SPD_MASK)); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 11068c2ecf20Sopenharmony_ci if ((ss->close_delay != port->port.close_delay) || 11078c2ecf20Sopenharmony_ci (ss->closing_wait != port->port.closing_wait) || 11088c2ecf20Sopenharmony_ci ((ss->flags & ~ASYNC_USR_MASK) != 11098c2ecf20Sopenharmony_ci (port->port.flags & ~ASYNC_USR_MASK))) { 11108c2ecf20Sopenharmony_ci mutex_unlock(&port->port.mutex); 11118c2ecf20Sopenharmony_ci return -EPERM; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | 11148c2ecf20Sopenharmony_ci (ss->flags & ASYNC_USR_MASK)); 11158c2ecf20Sopenharmony_ci } else { 11168c2ecf20Sopenharmony_ci port->port.close_delay = ss->close_delay; 11178c2ecf20Sopenharmony_ci port->port.closing_wait = ss->closing_wait; 11188c2ecf20Sopenharmony_ci port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | 11198c2ecf20Sopenharmony_ci (ss->flags & ASYNC_FLAGS)); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci if (reconfig_port) { 11228c2ecf20Sopenharmony_ci unsigned long flags; 11238c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->card->card_lock, flags); 11248c2ecf20Sopenharmony_ci isicom_config_port(tty); 11258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->card->card_lock, flags); 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci mutex_unlock(&port->port.mutex); 11288c2ecf20Sopenharmony_ci return 0; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic int isicom_get_serial_info(struct tty_struct *tty, 11328c2ecf20Sopenharmony_ci struct serial_struct *ss) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) 11378c2ecf20Sopenharmony_ci return -ENODEV; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci mutex_lock(&port->port.mutex); 11408c2ecf20Sopenharmony_ci/* ss->type = ? */ 11418c2ecf20Sopenharmony_ci ss->line = port - isi_ports; 11428c2ecf20Sopenharmony_ci ss->port = port->card->base; 11438c2ecf20Sopenharmony_ci ss->irq = port->card->irq; 11448c2ecf20Sopenharmony_ci ss->flags = port->port.flags; 11458c2ecf20Sopenharmony_ci/* ss->baud_base = ? */ 11468c2ecf20Sopenharmony_ci ss->close_delay = port->port.close_delay; 11478c2ecf20Sopenharmony_ci ss->closing_wait = port->port.closing_wait; 11488c2ecf20Sopenharmony_ci mutex_unlock(&port->port.mutex); 11498c2ecf20Sopenharmony_ci return 0; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/* set_termios et all */ 11538c2ecf20Sopenharmony_cistatic void isicom_set_termios(struct tty_struct *tty, 11548c2ecf20Sopenharmony_ci struct ktermios *old_termios) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 11578c2ecf20Sopenharmony_ci unsigned long flags; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_set_termios")) 11608c2ecf20Sopenharmony_ci return; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (tty->termios.c_cflag == old_termios->c_cflag && 11638c2ecf20Sopenharmony_ci tty->termios.c_iflag == old_termios->c_iflag) 11648c2ecf20Sopenharmony_ci return; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->card->card_lock, flags); 11678c2ecf20Sopenharmony_ci isicom_config_port(tty); 11688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->card->card_lock, flags); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { 11718c2ecf20Sopenharmony_ci tty->hw_stopped = 0; 11728c2ecf20Sopenharmony_ci isicom_start(tty); 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci/* throttle et all */ 11778c2ecf20Sopenharmony_cistatic void isicom_throttle(struct tty_struct *tty) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 11808c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_throttle")) 11838c2ecf20Sopenharmony_ci return; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci /* tell the card that this port cannot handle any more data for now */ 11868c2ecf20Sopenharmony_ci card->port_status &= ~(1 << port->channel); 11878c2ecf20Sopenharmony_ci outw(card->port_status, card->base + 0x02); 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci/* unthrottle et all */ 11918c2ecf20Sopenharmony_cistatic void isicom_unthrottle(struct tty_struct *tty) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 11948c2ecf20Sopenharmony_ci struct isi_board *card = port->card; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle")) 11978c2ecf20Sopenharmony_ci return; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* tell the card that this port is ready to accept more data */ 12008c2ecf20Sopenharmony_ci card->port_status |= (1 << port->channel); 12018c2ecf20Sopenharmony_ci outw(card->port_status, card->base + 0x02); 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci/* stop et all */ 12058c2ecf20Sopenharmony_cistatic void isicom_stop(struct tty_struct *tty) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_stop")) 12108c2ecf20Sopenharmony_ci return; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* this tells the transmitter not to consider this port for 12138c2ecf20Sopenharmony_ci data output to the card. */ 12148c2ecf20Sopenharmony_ci port->status &= ~ISI_TXOK; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci/* start et all */ 12188c2ecf20Sopenharmony_cistatic void isicom_start(struct tty_struct *tty) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_start")) 12238c2ecf20Sopenharmony_ci return; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci /* this tells the transmitter to consider this port for 12268c2ecf20Sopenharmony_ci data output to the card. */ 12278c2ecf20Sopenharmony_ci port->status |= ISI_TXOK; 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic void isicom_hangup(struct tty_struct *tty) 12318c2ecf20Sopenharmony_ci{ 12328c2ecf20Sopenharmony_ci struct isi_port *port = tty->driver_data; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (isicom_paranoia_check(port, tty->name, "isicom_hangup")) 12358c2ecf20Sopenharmony_ci return; 12368c2ecf20Sopenharmony_ci tty_port_hangup(&port->port); 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci/* 12418c2ecf20Sopenharmony_ci * Driver init and deinit functions 12428c2ecf20Sopenharmony_ci */ 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic const struct tty_operations isicom_ops = { 12458c2ecf20Sopenharmony_ci .open = isicom_open, 12468c2ecf20Sopenharmony_ci .close = isicom_close, 12478c2ecf20Sopenharmony_ci .write = isicom_write, 12488c2ecf20Sopenharmony_ci .put_char = isicom_put_char, 12498c2ecf20Sopenharmony_ci .flush_chars = isicom_flush_chars, 12508c2ecf20Sopenharmony_ci .write_room = isicom_write_room, 12518c2ecf20Sopenharmony_ci .chars_in_buffer = isicom_chars_in_buffer, 12528c2ecf20Sopenharmony_ci .set_termios = isicom_set_termios, 12538c2ecf20Sopenharmony_ci .throttle = isicom_throttle, 12548c2ecf20Sopenharmony_ci .unthrottle = isicom_unthrottle, 12558c2ecf20Sopenharmony_ci .stop = isicom_stop, 12568c2ecf20Sopenharmony_ci .start = isicom_start, 12578c2ecf20Sopenharmony_ci .hangup = isicom_hangup, 12588c2ecf20Sopenharmony_ci .flush_buffer = isicom_flush_buffer, 12598c2ecf20Sopenharmony_ci .tiocmget = isicom_tiocmget, 12608c2ecf20Sopenharmony_ci .tiocmset = isicom_tiocmset, 12618c2ecf20Sopenharmony_ci .break_ctl = isicom_send_break, 12628c2ecf20Sopenharmony_ci .get_serial = isicom_get_serial_info, 12638c2ecf20Sopenharmony_ci .set_serial = isicom_set_serial_info, 12648c2ecf20Sopenharmony_ci}; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic const struct tty_port_operations isicom_port_ops = { 12678c2ecf20Sopenharmony_ci .carrier_raised = isicom_carrier_raised, 12688c2ecf20Sopenharmony_ci .dtr_rts = isicom_dtr_rts, 12698c2ecf20Sopenharmony_ci .activate = isicom_activate, 12708c2ecf20Sopenharmony_ci .shutdown = isicom_shutdown, 12718c2ecf20Sopenharmony_ci}; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic int reset_card(struct pci_dev *pdev, 12748c2ecf20Sopenharmony_ci const unsigned int card, unsigned int *signature) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci struct isi_board *board = pci_get_drvdata(pdev); 12778c2ecf20Sopenharmony_ci unsigned long base = board->base; 12788c2ecf20Sopenharmony_ci unsigned int sig, portcount = 0; 12798c2ecf20Sopenharmony_ci int retval = 0; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1, 12828c2ecf20Sopenharmony_ci base); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci inw(base + 0x8); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci msleep(10); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci outw(0, base + 0x8); /* Reset */ 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci msleep(1000); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci sig = inw(base + 0x4) & 0xff; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd && 12958c2ecf20Sopenharmony_ci sig != 0xee) { 12968c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible " 12978c2ecf20Sopenharmony_ci "bad I/O Port Address 0x%lx).\n", card + 1, base); 12988c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Sig=0x%x\n", sig); 12998c2ecf20Sopenharmony_ci retval = -EIO; 13008c2ecf20Sopenharmony_ci goto end; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci msleep(10); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci portcount = inw(base + 0x2); 13068c2ecf20Sopenharmony_ci if (!(inw(base + 0xe) & 0x1) || (portcount != 0 && portcount != 4 && 13078c2ecf20Sopenharmony_ci portcount != 8 && portcount != 16)) { 13088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.\n", 13098c2ecf20Sopenharmony_ci card + 1); 13108c2ecf20Sopenharmony_ci retval = -EIO; 13118c2ecf20Sopenharmony_ci goto end; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci switch (sig) { 13158c2ecf20Sopenharmony_ci case 0xa5: 13168c2ecf20Sopenharmony_ci case 0xbb: 13178c2ecf20Sopenharmony_ci case 0xdd: 13188c2ecf20Sopenharmony_ci board->port_count = (portcount == 4) ? 4 : 8; 13198c2ecf20Sopenharmony_ci board->shift_count = 12; 13208c2ecf20Sopenharmony_ci break; 13218c2ecf20Sopenharmony_ci case 0xcc: 13228c2ecf20Sopenharmony_ci case 0xee: 13238c2ecf20Sopenharmony_ci board->port_count = 16; 13248c2ecf20Sopenharmony_ci board->shift_count = 11; 13258c2ecf20Sopenharmony_ci break; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "-Done\n"); 13288c2ecf20Sopenharmony_ci *signature = sig; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ciend: 13318c2ecf20Sopenharmony_ci return retval; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_cistatic int load_firmware(struct pci_dev *pdev, 13358c2ecf20Sopenharmony_ci const unsigned int index, const unsigned int signature) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci struct isi_board *board = pci_get_drvdata(pdev); 13388c2ecf20Sopenharmony_ci const struct firmware *fw; 13398c2ecf20Sopenharmony_ci unsigned long base = board->base; 13408c2ecf20Sopenharmony_ci unsigned int a; 13418c2ecf20Sopenharmony_ci u16 word_count, status; 13428c2ecf20Sopenharmony_ci int retval = -EIO; 13438c2ecf20Sopenharmony_ci char *name; 13448c2ecf20Sopenharmony_ci u8 *data; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci struct stframe { 13478c2ecf20Sopenharmony_ci u16 addr; 13488c2ecf20Sopenharmony_ci u16 count; 13498c2ecf20Sopenharmony_ci u8 data[0]; 13508c2ecf20Sopenharmony_ci } *frame; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci switch (signature) { 13538c2ecf20Sopenharmony_ci case 0xa5: 13548c2ecf20Sopenharmony_ci name = "isi608.bin"; 13558c2ecf20Sopenharmony_ci break; 13568c2ecf20Sopenharmony_ci case 0xbb: 13578c2ecf20Sopenharmony_ci name = "isi608em.bin"; 13588c2ecf20Sopenharmony_ci break; 13598c2ecf20Sopenharmony_ci case 0xcc: 13608c2ecf20Sopenharmony_ci name = "isi616em.bin"; 13618c2ecf20Sopenharmony_ci break; 13628c2ecf20Sopenharmony_ci case 0xdd: 13638c2ecf20Sopenharmony_ci name = "isi4608.bin"; 13648c2ecf20Sopenharmony_ci break; 13658c2ecf20Sopenharmony_ci case 0xee: 13668c2ecf20Sopenharmony_ci name = "isi4616.bin"; 13678c2ecf20Sopenharmony_ci break; 13688c2ecf20Sopenharmony_ci default: 13698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unknown signature.\n"); 13708c2ecf20Sopenharmony_ci goto end; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci retval = request_firmware(&fw, name, &pdev->dev); 13748c2ecf20Sopenharmony_ci if (retval) 13758c2ecf20Sopenharmony_ci goto end; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci retval = -EIO; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci for (frame = (struct stframe *)fw->data; 13808c2ecf20Sopenharmony_ci frame < (struct stframe *)(fw->data + fw->size); 13818c2ecf20Sopenharmony_ci frame = (struct stframe *)((u8 *)(frame + 1) + 13828c2ecf20Sopenharmony_ci frame->count)) { 13838c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 13848c2ecf20Sopenharmony_ci goto errrelfw; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci outw(0xf0, base); /* start upload sequence */ 13878c2ecf20Sopenharmony_ci outw(0x00, base); 13888c2ecf20Sopenharmony_ci outw(frame->addr, base); /* lsb of address */ 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci word_count = frame->count / 2 + frame->count % 2; 13918c2ecf20Sopenharmony_ci outw(word_count, base); 13928c2ecf20Sopenharmony_ci InterruptTheCard(base); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci udelay(100); /* 0x2f */ 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 13978c2ecf20Sopenharmony_ci goto errrelfw; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci status = inw(base + 0x4); 14008c2ecf20Sopenharmony_ci if (status != 0) { 14018c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Card%d rejected load header:\n" 14028c2ecf20Sopenharmony_ci "Address:0x%x\n" 14038c2ecf20Sopenharmony_ci "Count:0x%x\n" 14048c2ecf20Sopenharmony_ci "Status:0x%x\n", 14058c2ecf20Sopenharmony_ci index + 1, frame->addr, frame->count, status); 14068c2ecf20Sopenharmony_ci goto errrelfw; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci outsw(base, frame->data, word_count); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci InterruptTheCard(base); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci udelay(50); /* 0x0f */ 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 14158c2ecf20Sopenharmony_ci goto errrelfw; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci status = inw(base + 0x4); 14188c2ecf20Sopenharmony_ci if (status != 0) { 14198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Card%d got out of sync.Card " 14208c2ecf20Sopenharmony_ci "Status:0x%x\n", index + 1, status); 14218c2ecf20Sopenharmony_ci goto errrelfw; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci/* XXX: should we test it by reading it back and comparing with original like 14268c2ecf20Sopenharmony_ci * in load firmware package? */ 14278c2ecf20Sopenharmony_ci for (frame = (struct stframe *)fw->data; 14288c2ecf20Sopenharmony_ci frame < (struct stframe *)(fw->data + fw->size); 14298c2ecf20Sopenharmony_ci frame = (struct stframe *)((u8 *)(frame + 1) + 14308c2ecf20Sopenharmony_ci frame->count)) { 14318c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 14328c2ecf20Sopenharmony_ci goto errrelfw; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci outw(0xf1, base); /* start download sequence */ 14358c2ecf20Sopenharmony_ci outw(0x00, base); 14368c2ecf20Sopenharmony_ci outw(frame->addr, base); /* lsb of address */ 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci word_count = (frame->count >> 1) + frame->count % 2; 14398c2ecf20Sopenharmony_ci outw(word_count + 1, base); 14408c2ecf20Sopenharmony_ci InterruptTheCard(base); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci udelay(50); /* 0xf */ 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 14458c2ecf20Sopenharmony_ci goto errrelfw; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci status = inw(base + 0x4); 14488c2ecf20Sopenharmony_ci if (status != 0) { 14498c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Card%d rejected verify header:\n" 14508c2ecf20Sopenharmony_ci "Address:0x%x\n" 14518c2ecf20Sopenharmony_ci "Count:0x%x\n" 14528c2ecf20Sopenharmony_ci "Status: 0x%x\n", 14538c2ecf20Sopenharmony_ci index + 1, frame->addr, frame->count, status); 14548c2ecf20Sopenharmony_ci goto errrelfw; 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci data = kmalloc_array(word_count, 2, GFP_KERNEL); 14588c2ecf20Sopenharmony_ci if (data == NULL) { 14598c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Card%d, firmware upload " 14608c2ecf20Sopenharmony_ci "failed, not enough memory\n", index + 1); 14618c2ecf20Sopenharmony_ci goto errrelfw; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci inw(base); 14648c2ecf20Sopenharmony_ci insw(base, data, word_count); 14658c2ecf20Sopenharmony_ci InterruptTheCard(base); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci for (a = 0; a < frame->count; a++) 14688c2ecf20Sopenharmony_ci if (data[a] != frame->data[a]) { 14698c2ecf20Sopenharmony_ci kfree(data); 14708c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Card%d, firmware upload " 14718c2ecf20Sopenharmony_ci "failed\n", index + 1); 14728c2ecf20Sopenharmony_ci goto errrelfw; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci kfree(data); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci udelay(50); /* 0xf */ 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 14798c2ecf20Sopenharmony_ci goto errrelfw; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci status = inw(base + 0x4); 14828c2ecf20Sopenharmony_ci if (status != 0) { 14838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Card%d verify got out of sync. " 14848c2ecf20Sopenharmony_ci "Card Status:0x%x\n", index + 1, status); 14858c2ecf20Sopenharmony_ci goto errrelfw; 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci /* xfer ctrl */ 14908c2ecf20Sopenharmony_ci if (WaitTillCardIsFree(base)) 14918c2ecf20Sopenharmony_ci goto errrelfw; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci outw(0xf2, base); 14948c2ecf20Sopenharmony_ci outw(0x800, base); 14958c2ecf20Sopenharmony_ci outw(0x0, base); 14968c2ecf20Sopenharmony_ci outw(0x0, base); 14978c2ecf20Sopenharmony_ci InterruptTheCard(base); 14988c2ecf20Sopenharmony_ci outw(0x0, base + 0x4); /* for ISI4608 cards */ 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci board->status |= FIRMWARE_LOADED; 15018c2ecf20Sopenharmony_ci retval = 0; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cierrrelfw: 15048c2ecf20Sopenharmony_ci release_firmware(fw); 15058c2ecf20Sopenharmony_ciend: 15068c2ecf20Sopenharmony_ci return retval; 15078c2ecf20Sopenharmony_ci} 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci/* 15108c2ecf20Sopenharmony_ci * Insmod can set static symbols so keep these static 15118c2ecf20Sopenharmony_ci */ 15128c2ecf20Sopenharmony_cistatic unsigned int card_count; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_cistatic int isicom_probe(struct pci_dev *pdev, 15158c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 15168c2ecf20Sopenharmony_ci{ 15178c2ecf20Sopenharmony_ci unsigned int signature, index; 15188c2ecf20Sopenharmony_ci int retval = -EPERM; 15198c2ecf20Sopenharmony_ci struct isi_board *board = NULL; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci if (card_count >= BOARD_COUNT) 15228c2ecf20Sopenharmony_ci goto err; 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci retval = pci_enable_device(pdev); 15258c2ecf20Sopenharmony_ci if (retval) { 15268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable\n"); 15278c2ecf20Sopenharmony_ci goto err; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci /* allot the first empty slot in the array */ 15338c2ecf20Sopenharmony_ci for (index = 0; index < BOARD_COUNT; index++) { 15348c2ecf20Sopenharmony_ci if (isi_card[index].base == 0) { 15358c2ecf20Sopenharmony_ci board = &isi_card[index]; 15368c2ecf20Sopenharmony_ci break; 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci } 15398c2ecf20Sopenharmony_ci if (index == BOARD_COUNT) { 15408c2ecf20Sopenharmony_ci retval = -ENODEV; 15418c2ecf20Sopenharmony_ci goto err_disable; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci board->index = index; 15458c2ecf20Sopenharmony_ci board->base = pci_resource_start(pdev, 3); 15468c2ecf20Sopenharmony_ci board->irq = pdev->irq; 15478c2ecf20Sopenharmony_ci card_count++; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, board); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci retval = pci_request_region(pdev, 3, ISICOM_NAME); 15528c2ecf20Sopenharmony_ci if (retval) { 15538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d " 15548c2ecf20Sopenharmony_ci "will be disabled.\n", board->base, board->base + 15, 15558c2ecf20Sopenharmony_ci index + 1); 15568c2ecf20Sopenharmony_ci retval = -EBUSY; 15578c2ecf20Sopenharmony_ci goto errdec; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci retval = request_irq(board->irq, isicom_interrupt, 15618c2ecf20Sopenharmony_ci IRQF_SHARED, ISICOM_NAME, board); 15628c2ecf20Sopenharmony_ci if (retval < 0) { 15638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not install handler at Irq %d. " 15648c2ecf20Sopenharmony_ci "Card%d will be disabled.\n", board->irq, index + 1); 15658c2ecf20Sopenharmony_ci goto errunrr; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci retval = reset_card(pdev, index, &signature); 15698c2ecf20Sopenharmony_ci if (retval < 0) 15708c2ecf20Sopenharmony_ci goto errunri; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci retval = load_firmware(pdev, index, signature); 15738c2ecf20Sopenharmony_ci if (retval < 0) 15748c2ecf20Sopenharmony_ci goto errunri; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci for (index = 0; index < board->port_count; index++) { 15778c2ecf20Sopenharmony_ci struct tty_port *tport = &board->ports[index].port; 15788c2ecf20Sopenharmony_ci tty_port_init(tport); 15798c2ecf20Sopenharmony_ci tport->ops = &isicom_port_ops; 15808c2ecf20Sopenharmony_ci tport->close_delay = 50 * HZ/100; 15818c2ecf20Sopenharmony_ci tport->closing_wait = 3000 * HZ/100; 15828c2ecf20Sopenharmony_ci tty_port_register_device(tport, isicom_normal, 15838c2ecf20Sopenharmony_ci board->index * 16 + index, &pdev->dev); 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci return 0; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_cierrunri: 15898c2ecf20Sopenharmony_ci free_irq(board->irq, board); 15908c2ecf20Sopenharmony_cierrunrr: 15918c2ecf20Sopenharmony_ci pci_release_region(pdev, 3); 15928c2ecf20Sopenharmony_cierrdec: 15938c2ecf20Sopenharmony_ci board->base = 0; 15948c2ecf20Sopenharmony_ci card_count--; 15958c2ecf20Sopenharmony_cierr_disable: 15968c2ecf20Sopenharmony_ci pci_disable_device(pdev); 15978c2ecf20Sopenharmony_cierr: 15988c2ecf20Sopenharmony_ci return retval; 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_cistatic void isicom_remove(struct pci_dev *pdev) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci struct isi_board *board = pci_get_drvdata(pdev); 16048c2ecf20Sopenharmony_ci unsigned int i; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci for (i = 0; i < board->port_count; i++) { 16078c2ecf20Sopenharmony_ci tty_unregister_device(isicom_normal, board->index * 16 + i); 16088c2ecf20Sopenharmony_ci tty_port_destroy(&board->ports[i].port); 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci free_irq(board->irq, board); 16128c2ecf20Sopenharmony_ci pci_release_region(pdev, 3); 16138c2ecf20Sopenharmony_ci board->base = 0; 16148c2ecf20Sopenharmony_ci card_count--; 16158c2ecf20Sopenharmony_ci pci_disable_device(pdev); 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_cistatic int __init isicom_init(void) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci int retval, idx, channel; 16218c2ecf20Sopenharmony_ci struct isi_port *port; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci for (idx = 0; idx < BOARD_COUNT; idx++) { 16248c2ecf20Sopenharmony_ci port = &isi_ports[idx * 16]; 16258c2ecf20Sopenharmony_ci isi_card[idx].ports = port; 16268c2ecf20Sopenharmony_ci spin_lock_init(&isi_card[idx].card_lock); 16278c2ecf20Sopenharmony_ci for (channel = 0; channel < 16; channel++, port++) { 16288c2ecf20Sopenharmony_ci port->magic = ISICOM_MAGIC; 16298c2ecf20Sopenharmony_ci port->card = &isi_card[idx]; 16308c2ecf20Sopenharmony_ci port->channel = channel; 16318c2ecf20Sopenharmony_ci port->status = 0; 16328c2ecf20Sopenharmony_ci /* . . . */ 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci isi_card[idx].base = 0; 16358c2ecf20Sopenharmony_ci isi_card[idx].irq = 0; 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci /* tty driver structure initialization */ 16398c2ecf20Sopenharmony_ci isicom_normal = alloc_tty_driver(PORT_COUNT); 16408c2ecf20Sopenharmony_ci if (!isicom_normal) { 16418c2ecf20Sopenharmony_ci retval = -ENOMEM; 16428c2ecf20Sopenharmony_ci goto error; 16438c2ecf20Sopenharmony_ci } 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci isicom_normal->name = "ttyM"; 16468c2ecf20Sopenharmony_ci isicom_normal->major = ISICOM_NMAJOR; 16478c2ecf20Sopenharmony_ci isicom_normal->minor_start = 0; 16488c2ecf20Sopenharmony_ci isicom_normal->type = TTY_DRIVER_TYPE_SERIAL; 16498c2ecf20Sopenharmony_ci isicom_normal->subtype = SERIAL_TYPE_NORMAL; 16508c2ecf20Sopenharmony_ci isicom_normal->init_termios = tty_std_termios; 16518c2ecf20Sopenharmony_ci isicom_normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | 16528c2ecf20Sopenharmony_ci CLOCAL; 16538c2ecf20Sopenharmony_ci isicom_normal->flags = TTY_DRIVER_REAL_RAW | 16548c2ecf20Sopenharmony_ci TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK; 16558c2ecf20Sopenharmony_ci tty_set_operations(isicom_normal, &isicom_ops); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci retval = tty_register_driver(isicom_normal); 16588c2ecf20Sopenharmony_ci if (retval) { 16598c2ecf20Sopenharmony_ci pr_debug("Couldn't register the dialin driver\n"); 16608c2ecf20Sopenharmony_ci goto err_puttty; 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci retval = pci_register_driver(&isicom_driver); 16648c2ecf20Sopenharmony_ci if (retval < 0) { 16658c2ecf20Sopenharmony_ci pr_err("Unable to register pci driver.\n"); 16668c2ecf20Sopenharmony_ci goto err_unrtty; 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci mod_timer(&tx, jiffies + 1); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci return 0; 16728c2ecf20Sopenharmony_cierr_unrtty: 16738c2ecf20Sopenharmony_ci tty_unregister_driver(isicom_normal); 16748c2ecf20Sopenharmony_cierr_puttty: 16758c2ecf20Sopenharmony_ci put_tty_driver(isicom_normal); 16768c2ecf20Sopenharmony_cierror: 16778c2ecf20Sopenharmony_ci return retval; 16788c2ecf20Sopenharmony_ci} 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_cistatic void __exit isicom_exit(void) 16818c2ecf20Sopenharmony_ci{ 16828c2ecf20Sopenharmony_ci del_timer_sync(&tx); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci pci_unregister_driver(&isicom_driver); 16858c2ecf20Sopenharmony_ci tty_unregister_driver(isicom_normal); 16868c2ecf20Sopenharmony_ci put_tty_driver(isicom_normal); 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_cimodule_init(isicom_init); 16908c2ecf20Sopenharmony_cimodule_exit(isicom_exit); 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ciMODULE_AUTHOR("MultiTech"); 16938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech"); 16948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 16958c2ecf20Sopenharmony_ciMODULE_FIRMWARE("isi608.bin"); 16968c2ecf20Sopenharmony_ciMODULE_FIRMWARE("isi608em.bin"); 16978c2ecf20Sopenharmony_ciMODULE_FIRMWARE("isi616em.bin"); 16988c2ecf20Sopenharmony_ciMODULE_FIRMWARE("isi4608.bin"); 16998c2ecf20Sopenharmony_ciMODULE_FIRMWARE("isi4616.bin"); 1700