18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#undef BLOCKMOVE 38c2ecf20Sopenharmony_ci#define Z_WAKE 48c2ecf20Sopenharmony_ci#undef Z_EXT_CHARS_IN_BUFFER 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * This file contains the driver for the Cyclades async multiport 88c2ecf20Sopenharmony_ci * serial boards. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>. 118c2ecf20Sopenharmony_ci * Modified and maintained by Marcio Saito <marcio@cyclades.com>. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Much of the design and some of the code came from serial.c 168c2ecf20Sopenharmony_ci * which was copyright (C) 1991, 1992 Linus Torvalds. It was 178c2ecf20Sopenharmony_ci * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, 188c2ecf20Sopenharmony_ci * and then fixed as suggested by Michael K. Johnson 12/12/92. 198c2ecf20Sopenharmony_ci * Converted to pci probing and cleaned up by Jiri Slaby. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define CY_VERSION "2.6" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* If you need to install more boards than NR_CARDS, change the constant 268c2ecf20Sopenharmony_ci in the definition below. No other change is necessary to support up to 278c2ecf20Sopenharmony_ci eight boards. Beyond that you'll have to extend cy_isa_addresses. */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define NR_CARDS 4 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci If the total number of ports is larger than NR_PORTS, change this 338c2ecf20Sopenharmony_ci constant in the definition below. No other change is necessary to 348c2ecf20Sopenharmony_ci support more boards/ports. */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define NR_PORTS 256 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define ZO_V1 0 398c2ecf20Sopenharmony_ci#define ZO_V2 1 408c2ecf20Sopenharmony_ci#define ZE_V1 2 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define SERIAL_PARANOIA_CHECK 438c2ecf20Sopenharmony_ci#undef CY_DEBUG_OPEN 448c2ecf20Sopenharmony_ci#undef CY_DEBUG_THROTTLE 458c2ecf20Sopenharmony_ci#undef CY_DEBUG_OTHER 468c2ecf20Sopenharmony_ci#undef CY_DEBUG_IO 478c2ecf20Sopenharmony_ci#undef CY_DEBUG_COUNT 488c2ecf20Sopenharmony_ci#undef CY_DEBUG_DTR 498c2ecf20Sopenharmony_ci#undef CY_DEBUG_INTERRUPTS 508c2ecf20Sopenharmony_ci#undef CY_16Y_HACK 518c2ecf20Sopenharmony_ci#undef CY_ENABLE_MONITORING 528c2ecf20Sopenharmony_ci#undef CY_PCI_DEBUG 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * Include section 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci#include <linux/module.h> 588c2ecf20Sopenharmony_ci#include <linux/errno.h> 598c2ecf20Sopenharmony_ci#include <linux/signal.h> 608c2ecf20Sopenharmony_ci#include <linux/sched.h> 618c2ecf20Sopenharmony_ci#include <linux/timer.h> 628c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 638c2ecf20Sopenharmony_ci#include <linux/tty.h> 648c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 658c2ecf20Sopenharmony_ci#include <linux/serial.h> 668c2ecf20Sopenharmony_ci#include <linux/major.h> 678c2ecf20Sopenharmony_ci#include <linux/string.h> 688c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 698c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 708c2ecf20Sopenharmony_ci#include <linux/cyclades.h> 718c2ecf20Sopenharmony_ci#include <linux/mm.h> 728c2ecf20Sopenharmony_ci#include <linux/ioport.h> 738c2ecf20Sopenharmony_ci#include <linux/init.h> 748c2ecf20Sopenharmony_ci#include <linux/delay.h> 758c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 768c2ecf20Sopenharmony_ci#include <linux/bitops.h> 778c2ecf20Sopenharmony_ci#include <linux/firmware.h> 788c2ecf20Sopenharmony_ci#include <linux/device.h> 798c2ecf20Sopenharmony_ci#include <linux/slab.h> 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#include <linux/io.h> 828c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#include <linux/kernel.h> 858c2ecf20Sopenharmony_ci#include <linux/pci.h> 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#include <linux/stat.h> 888c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 898c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void cy_send_xchar(struct tty_struct *tty, char ch); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#ifndef SERIAL_XMIT_SIZE 948c2ecf20Sopenharmony_ci#define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096)) 958c2ecf20Sopenharmony_ci#endif 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* firmware stuff */ 988c2ecf20Sopenharmony_ci#define ZL_MAX_BLOCKS 16 998c2ecf20Sopenharmony_ci#define DRIVER_VERSION 0x02010203 1008c2ecf20Sopenharmony_ci#define RAM_SIZE 0x80000 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cienum zblock_type { 1038c2ecf20Sopenharmony_ci ZBLOCK_PRG = 0, 1048c2ecf20Sopenharmony_ci ZBLOCK_FPGA = 1 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistruct zfile_header { 1088c2ecf20Sopenharmony_ci char name[64]; 1098c2ecf20Sopenharmony_ci char date[32]; 1108c2ecf20Sopenharmony_ci char aux[32]; 1118c2ecf20Sopenharmony_ci u32 n_config; 1128c2ecf20Sopenharmony_ci u32 config_offset; 1138c2ecf20Sopenharmony_ci u32 n_blocks; 1148c2ecf20Sopenharmony_ci u32 block_offset; 1158c2ecf20Sopenharmony_ci u32 reserved[9]; 1168c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct zfile_config { 1198c2ecf20Sopenharmony_ci char name[64]; 1208c2ecf20Sopenharmony_ci u32 mailbox; 1218c2ecf20Sopenharmony_ci u32 function; 1228c2ecf20Sopenharmony_ci u32 n_blocks; 1238c2ecf20Sopenharmony_ci u32 block_list[ZL_MAX_BLOCKS]; 1248c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistruct zfile_block { 1278c2ecf20Sopenharmony_ci u32 type; 1288c2ecf20Sopenharmony_ci u32 file_offset; 1298c2ecf20Sopenharmony_ci u32 ram_offset; 1308c2ecf20Sopenharmony_ci u32 size; 1318c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct tty_driver *cy_serial_driver; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA 1368c2ecf20Sopenharmony_ci/* This is the address lookup table. The driver will probe for 1378c2ecf20Sopenharmony_ci Cyclom-Y/ISA boards at all addresses in here. If you want the 1388c2ecf20Sopenharmony_ci driver to probe addresses at a different address, add it to 1398c2ecf20Sopenharmony_ci this table. If the driver is probing some other board and 1408c2ecf20Sopenharmony_ci causing problems, remove the offending address from this table. 1418c2ecf20Sopenharmony_ci*/ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic unsigned int cy_isa_addresses[] = { 1448c2ecf20Sopenharmony_ci 0xD0000, 1458c2ecf20Sopenharmony_ci 0xD2000, 1468c2ecf20Sopenharmony_ci 0xD4000, 1478c2ecf20Sopenharmony_ci 0xD6000, 1488c2ecf20Sopenharmony_ci 0xD8000, 1498c2ecf20Sopenharmony_ci 0xDA000, 1508c2ecf20Sopenharmony_ci 0xDC000, 1518c2ecf20Sopenharmony_ci 0xDE000, 1528c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses) 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic long maddr[NR_CARDS]; 1588c2ecf20Sopenharmony_cistatic int irq[NR_CARDS]; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cimodule_param_hw_array(maddr, long, iomem, NULL, 0); 1618c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#endif /* CONFIG_ISA */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* This is the per-card data structure containing address, irq, number of 1668c2ecf20Sopenharmony_ci channels, etc. This driver supports a maximum of NR_CARDS cards. 1678c2ecf20Sopenharmony_ci*/ 1688c2ecf20Sopenharmony_cistatic struct cyclades_card cy_card[NR_CARDS]; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int cy_next_channel; /* next minor available */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci * This is used to look up the divisor speeds and the timeouts 1748c2ecf20Sopenharmony_ci * We're normally limited to 15 distinct baud rates. The extra 1758c2ecf20Sopenharmony_ci * are accessed via settings in info->port.flags. 1768c2ecf20Sopenharmony_ci * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1778c2ecf20Sopenharmony_ci * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1788c2ecf20Sopenharmony_ci * HI VHI 1798c2ecf20Sopenharmony_ci * 20 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_cistatic const int baud_table[] = { 1828c2ecf20Sopenharmony_ci 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1838c2ecf20Sopenharmony_ci 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, 1848c2ecf20Sopenharmony_ci 230400, 0 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const char baud_co_25[] = { /* 25 MHz clock option table */ 1888c2ecf20Sopenharmony_ci /* value => 00 01 02 03 04 */ 1898c2ecf20Sopenharmony_ci /* divide by 8 32 128 512 2048 */ 1908c2ecf20Sopenharmony_ci 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 1918c2ecf20Sopenharmony_ci 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic const char baud_bpr_25[] = { /* 25 MHz baud rate period table */ 1958c2ecf20Sopenharmony_ci 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, 1968c2ecf20Sopenharmony_ci 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic const char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ 2008c2ecf20Sopenharmony_ci /* value => 00 01 02 03 04 */ 2018c2ecf20Sopenharmony_ci /* divide by 8 32 128 512 2048 */ 2028c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 2038c2ecf20Sopenharmony_ci 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 2048c2ecf20Sopenharmony_ci 0x00 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic const char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ 2088c2ecf20Sopenharmony_ci 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, 2098c2ecf20Sopenharmony_ci 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, 2108c2ecf20Sopenharmony_ci 0x21 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic const char baud_cor3[] = { /* receive threshold */ 2148c2ecf20Sopenharmony_ci 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 2158c2ecf20Sopenharmony_ci 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 2168c2ecf20Sopenharmony_ci 0x07 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* 2208c2ecf20Sopenharmony_ci * The Cyclades driver implements HW flow control as any serial driver. 2218c2ecf20Sopenharmony_ci * The cyclades_port structure member rflow and the vector rflow_thr 2228c2ecf20Sopenharmony_ci * allows us to take advantage of a special feature in the CD1400 to avoid 2238c2ecf20Sopenharmony_ci * data loss even when the system interrupt latency is too high. These flags 2248c2ecf20Sopenharmony_ci * are to be used only with very special applications. Setting these flags 2258c2ecf20Sopenharmony_ci * requires the use of a special cable (DTR and RTS reversed). In the new 2268c2ecf20Sopenharmony_ci * CD1400-based boards (rev. 6.00 or later), there is no need for special 2278c2ecf20Sopenharmony_ci * cables. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic const char rflow_thr[] = { /* rflow threshold */ 2318c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2328c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 2338c2ecf20Sopenharmony_ci 0x0a 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* The Cyclom-Ye has placed the sequential chips in non-sequential 2378c2ecf20Sopenharmony_ci * address order. This look-up table overcomes that problem. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_cistatic const unsigned int cy_chip_offset[] = { 0x0000, 2408c2ecf20Sopenharmony_ci 0x0400, 2418c2ecf20Sopenharmony_ci 0x0800, 2428c2ecf20Sopenharmony_ci 0x0C00, 2438c2ecf20Sopenharmony_ci 0x0200, 2448c2ecf20Sopenharmony_ci 0x0600, 2458c2ecf20Sopenharmony_ci 0x0A00, 2468c2ecf20Sopenharmony_ci 0x0E00 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* PCI related definitions */ 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 2528c2ecf20Sopenharmony_cistatic const struct pci_device_id cy_pci_dev_id[] = { 2538c2ecf20Sopenharmony_ci /* PCI < 1Mb */ 2548c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, 2558c2ecf20Sopenharmony_ci /* PCI > 1Mb */ 2568c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) }, 2578c2ecf20Sopenharmony_ci /* 4Y PCI < 1Mb */ 2588c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) }, 2598c2ecf20Sopenharmony_ci /* 4Y PCI > 1Mb */ 2608c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) }, 2618c2ecf20Sopenharmony_ci /* 8Y PCI < 1Mb */ 2628c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) }, 2638c2ecf20Sopenharmony_ci /* 8Y PCI > 1Mb */ 2648c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) }, 2658c2ecf20Sopenharmony_ci /* Z PCI < 1Mb */ 2668c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) }, 2678c2ecf20Sopenharmony_ci /* Z PCI > 1Mb */ 2688c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) }, 2698c2ecf20Sopenharmony_ci { } /* end of table */ 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cy_pci_dev_id); 2728c2ecf20Sopenharmony_ci#endif 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void cy_start(struct tty_struct *); 2758c2ecf20Sopenharmony_cistatic void cy_set_line_char(struct cyclades_port *, struct tty_struct *); 2768c2ecf20Sopenharmony_cistatic int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32); 2778c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA 2788c2ecf20Sopenharmony_cistatic unsigned detect_isa_irq(void __iomem *); 2798c2ecf20Sopenharmony_ci#endif /* CONFIG_ISA */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR 2828c2ecf20Sopenharmony_cistatic void cyz_poll(struct timer_list *); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* The Cyclades-Z polling cycle is defined by this variable */ 2858c2ecf20Sopenharmony_cistatic long cyz_polling_cycle = CZ_DEF_POLL; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic DEFINE_TIMER(cyz_timerlist, cyz_poll); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci#else /* CONFIG_CYZ_INTR */ 2908c2ecf20Sopenharmony_cistatic void cyz_rx_restart(struct timer_list *); 2918c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct cyclades_card *card = port->card; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic u8 cyy_readb(struct cyclades_port *port, u32 reg) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct cyclades_card *card = port->card; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return readb(port->u.cyy.base_addr + (reg << card->bus_index)); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic inline bool cy_is_Z(struct cyclades_card *card) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci return card->num_chips == (unsigned int)-1; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci return readl(&ctl_addr->init_ctrl) & (1 << 17); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic inline bool cyz_fpga_loaded(struct cyclades_card *card) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci return __cyz_fpga_loaded(card->ctl_addr.p9060); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic bool cyz_is_loaded(struct cyclades_card *card) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) && 3278c2ecf20Sopenharmony_ci readl(&fw_id->signature) == ZFIRM_ID; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int serial_paranoia_check(struct cyclades_port *info, 3318c2ecf20Sopenharmony_ci const char *name, const char *routine) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci#ifdef SERIAL_PARANOIA_CHECK 3348c2ecf20Sopenharmony_ci if (!info) { 3358c2ecf20Sopenharmony_ci printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) " 3368c2ecf20Sopenharmony_ci "in %s\n", name, routine); 3378c2ecf20Sopenharmony_ci return 1; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (info->magic != CYCLADES_MAGIC) { 3418c2ecf20Sopenharmony_ci printk(KERN_WARNING "cyc Warning: bad magic number for serial " 3428c2ecf20Sopenharmony_ci "struct (%s) in %s\n", name, routine); 3438c2ecf20Sopenharmony_ci return 1; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci#endif 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci/***********************************************************/ 3508c2ecf20Sopenharmony_ci/********* Start of block of Cyclom-Y specific code ********/ 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* This routine waits up to 1000 micro-seconds for the previous 3538c2ecf20Sopenharmony_ci command to the Cirrus chip to complete and then issues the 3548c2ecf20Sopenharmony_ci new command. An error is returned if the previous command 3558c2ecf20Sopenharmony_ci didn't finish within the time limit. 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci This function is only called from inside spinlock-protected code. 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_cistatic int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci void __iomem *ccr = base_addr + (CyCCR << index); 3628c2ecf20Sopenharmony_ci unsigned int i; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Check to see that the previous command has completed */ 3658c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 3668c2ecf20Sopenharmony_ci if (readb(ccr) == 0) 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci udelay(10L); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci /* if the CCR never cleared, the previous command 3718c2ecf20Sopenharmony_ci didn't finish within the "reasonable time" */ 3728c2ecf20Sopenharmony_ci if (i == 100) 3738c2ecf20Sopenharmony_ci return -1; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* Issue the new command */ 3768c2ecf20Sopenharmony_ci cy_writeb(ccr, cmd); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci return __cyy_issue_cmd(port->u.cyy.base_addr, cmd, 3848c2ecf20Sopenharmony_ci port->card->bus_index); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA 3888c2ecf20Sopenharmony_ci/* ISA interrupt detection code */ 3898c2ecf20Sopenharmony_cistatic unsigned detect_isa_irq(void __iomem *address) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci int irq; 3928c2ecf20Sopenharmony_ci unsigned long irqs, flags; 3938c2ecf20Sopenharmony_ci int save_xir, save_car; 3948c2ecf20Sopenharmony_ci int index = 0; /* IRQ probing is only for ISA */ 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* forget possible initially masked and pending IRQ */ 3978c2ecf20Sopenharmony_ci irq = probe_irq_off(probe_irq_on()); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* Clear interrupts on the board first */ 4008c2ecf20Sopenharmony_ci cy_writeb(address + (Cy_ClrIntr << index), 0); 4018c2ecf20Sopenharmony_ci /* Cy_ClrIntr is 0x1800 */ 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci irqs = probe_irq_on(); 4048c2ecf20Sopenharmony_ci /* Wait ... */ 4058c2ecf20Sopenharmony_ci msleep(5); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* Enable the Tx interrupts on the CD1400 */ 4088c2ecf20Sopenharmony_ci local_irq_save(flags); 4098c2ecf20Sopenharmony_ci cy_writeb(address + (CyCAR << index), 0); 4108c2ecf20Sopenharmony_ci __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci cy_writeb(address + (CyCAR << index), 0); 4138c2ecf20Sopenharmony_ci cy_writeb(address + (CySRER << index), 4148c2ecf20Sopenharmony_ci readb(address + (CySRER << index)) | CyTxRdy); 4158c2ecf20Sopenharmony_ci local_irq_restore(flags); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Wait ... */ 4188c2ecf20Sopenharmony_ci msleep(5); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Check which interrupt is in use */ 4218c2ecf20Sopenharmony_ci irq = probe_irq_off(irqs); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Clean up */ 4248c2ecf20Sopenharmony_ci save_xir = (u_char) readb(address + (CyTIR << index)); 4258c2ecf20Sopenharmony_ci save_car = readb(address + (CyCAR << index)); 4268c2ecf20Sopenharmony_ci cy_writeb(address + (CyCAR << index), (save_xir & 0x3)); 4278c2ecf20Sopenharmony_ci cy_writeb(address + (CySRER << index), 4288c2ecf20Sopenharmony_ci readb(address + (CySRER << index)) & ~CyTxRdy); 4298c2ecf20Sopenharmony_ci cy_writeb(address + (CyTIR << index), (save_xir & 0x3f)); 4308c2ecf20Sopenharmony_ci cy_writeb(address + (CyCAR << index), (save_car)); 4318c2ecf20Sopenharmony_ci cy_writeb(address + (Cy_ClrIntr << index), 0); 4328c2ecf20Sopenharmony_ci /* Cy_ClrIntr is 0x1800 */ 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return (irq > 0) ? irq : 0; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci#endif /* CONFIG_ISA */ 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic void cyy_chip_rx(struct cyclades_card *cinfo, int chip, 4398c2ecf20Sopenharmony_ci void __iomem *base_addr) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct cyclades_port *info; 4428c2ecf20Sopenharmony_ci struct tty_port *port; 4438c2ecf20Sopenharmony_ci int len, index = cinfo->bus_index; 4448c2ecf20Sopenharmony_ci u8 ivr, save_xir, channel, save_car, data, char_count; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS 4478c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip); 4488c2ecf20Sopenharmony_ci#endif 4498c2ecf20Sopenharmony_ci /* determine the channel & change to that context */ 4508c2ecf20Sopenharmony_ci save_xir = readb(base_addr + (CyRIR << index)); 4518c2ecf20Sopenharmony_ci channel = save_xir & CyIRChannel; 4528c2ecf20Sopenharmony_ci info = &cinfo->ports[channel + chip * 4]; 4538c2ecf20Sopenharmony_ci port = &info->port; 4548c2ecf20Sopenharmony_ci save_car = cyy_readb(info, CyCAR); 4558c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, save_xir); 4568c2ecf20Sopenharmony_ci ivr = cyy_readb(info, CyRIVR) & CyIVRMask; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* there is an open port for this data */ 4598c2ecf20Sopenharmony_ci if (ivr == CyIVRRxEx) { /* exception */ 4608c2ecf20Sopenharmony_ci data = cyy_readb(info, CyRDSR); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* For statistics only */ 4638c2ecf20Sopenharmony_ci if (data & CyBREAK) 4648c2ecf20Sopenharmony_ci info->icount.brk++; 4658c2ecf20Sopenharmony_ci else if (data & CyFRAME) 4668c2ecf20Sopenharmony_ci info->icount.frame++; 4678c2ecf20Sopenharmony_ci else if (data & CyPARITY) 4688c2ecf20Sopenharmony_ci info->icount.parity++; 4698c2ecf20Sopenharmony_ci else if (data & CyOVERRUN) 4708c2ecf20Sopenharmony_ci info->icount.overrun++; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (data & info->ignore_status_mask) { 4738c2ecf20Sopenharmony_ci info->icount.rx++; 4748c2ecf20Sopenharmony_ci return; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci if (tty_buffer_request_room(port, 1)) { 4778c2ecf20Sopenharmony_ci if (data & info->read_status_mask) { 4788c2ecf20Sopenharmony_ci if (data & CyBREAK) { 4798c2ecf20Sopenharmony_ci tty_insert_flip_char(port, 4808c2ecf20Sopenharmony_ci cyy_readb(info, CyRDSR), 4818c2ecf20Sopenharmony_ci TTY_BREAK); 4828c2ecf20Sopenharmony_ci info->icount.rx++; 4838c2ecf20Sopenharmony_ci if (port->flags & ASYNC_SAK) { 4848c2ecf20Sopenharmony_ci struct tty_struct *tty = 4858c2ecf20Sopenharmony_ci tty_port_tty_get(port); 4868c2ecf20Sopenharmony_ci if (tty) { 4878c2ecf20Sopenharmony_ci do_SAK(tty); 4888c2ecf20Sopenharmony_ci tty_kref_put(tty); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci } else if (data & CyFRAME) { 4928c2ecf20Sopenharmony_ci tty_insert_flip_char(port, 4938c2ecf20Sopenharmony_ci cyy_readb(info, CyRDSR), 4948c2ecf20Sopenharmony_ci TTY_FRAME); 4958c2ecf20Sopenharmony_ci info->icount.rx++; 4968c2ecf20Sopenharmony_ci info->idle_stats.frame_errs++; 4978c2ecf20Sopenharmony_ci } else if (data & CyPARITY) { 4988c2ecf20Sopenharmony_ci /* Pieces of seven... */ 4998c2ecf20Sopenharmony_ci tty_insert_flip_char(port, 5008c2ecf20Sopenharmony_ci cyy_readb(info, CyRDSR), 5018c2ecf20Sopenharmony_ci TTY_PARITY); 5028c2ecf20Sopenharmony_ci info->icount.rx++; 5038c2ecf20Sopenharmony_ci info->idle_stats.parity_errs++; 5048c2ecf20Sopenharmony_ci } else if (data & CyOVERRUN) { 5058c2ecf20Sopenharmony_ci tty_insert_flip_char(port, 0, 5068c2ecf20Sopenharmony_ci TTY_OVERRUN); 5078c2ecf20Sopenharmony_ci info->icount.rx++; 5088c2ecf20Sopenharmony_ci /* If the flip buffer itself is 5098c2ecf20Sopenharmony_ci overflowing, we still lose 5108c2ecf20Sopenharmony_ci the next incoming character. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci tty_insert_flip_char(port, 5138c2ecf20Sopenharmony_ci cyy_readb(info, CyRDSR), 5148c2ecf20Sopenharmony_ci TTY_FRAME); 5158c2ecf20Sopenharmony_ci info->icount.rx++; 5168c2ecf20Sopenharmony_ci info->idle_stats.overruns++; 5178c2ecf20Sopenharmony_ci /* These two conditions may imply */ 5188c2ecf20Sopenharmony_ci /* a normal read should be done. */ 5198c2ecf20Sopenharmony_ci /* } else if(data & CyTIMEOUT) { */ 5208c2ecf20Sopenharmony_ci /* } else if(data & CySPECHAR) { */ 5218c2ecf20Sopenharmony_ci } else { 5228c2ecf20Sopenharmony_ci tty_insert_flip_char(port, 0, 5238c2ecf20Sopenharmony_ci TTY_NORMAL); 5248c2ecf20Sopenharmony_ci info->icount.rx++; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci } else { 5278c2ecf20Sopenharmony_ci tty_insert_flip_char(port, 0, TTY_NORMAL); 5288c2ecf20Sopenharmony_ci info->icount.rx++; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci } else { 5318c2ecf20Sopenharmony_ci /* there was a software buffer overrun and nothing 5328c2ecf20Sopenharmony_ci * could be done about it!!! */ 5338c2ecf20Sopenharmony_ci info->icount.buf_overrun++; 5348c2ecf20Sopenharmony_ci info->idle_stats.overruns++; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci } else { /* normal character reception */ 5378c2ecf20Sopenharmony_ci /* load # chars available from the chip */ 5388c2ecf20Sopenharmony_ci char_count = cyy_readb(info, CyRDCR); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci#ifdef CY_ENABLE_MONITORING 5418c2ecf20Sopenharmony_ci ++info->mon.int_count; 5428c2ecf20Sopenharmony_ci info->mon.char_count += char_count; 5438c2ecf20Sopenharmony_ci if (char_count > info->mon.char_max) 5448c2ecf20Sopenharmony_ci info->mon.char_max = char_count; 5458c2ecf20Sopenharmony_ci info->mon.char_last = char_count; 5468c2ecf20Sopenharmony_ci#endif 5478c2ecf20Sopenharmony_ci len = tty_buffer_request_room(port, char_count); 5488c2ecf20Sopenharmony_ci while (len--) { 5498c2ecf20Sopenharmony_ci data = cyy_readb(info, CyRDSR); 5508c2ecf20Sopenharmony_ci tty_insert_flip_char(port, data, TTY_NORMAL); 5518c2ecf20Sopenharmony_ci info->idle_stats.recv_bytes++; 5528c2ecf20Sopenharmony_ci info->icount.rx++; 5538c2ecf20Sopenharmony_ci#ifdef CY_16Y_HACK 5548c2ecf20Sopenharmony_ci udelay(10L); 5558c2ecf20Sopenharmony_ci#endif 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci info->idle_stats.recv_idle = jiffies; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci tty_flip_buffer_push(port); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* end of service */ 5628c2ecf20Sopenharmony_ci cyy_writeb(info, CyRIR, save_xir & 0x3f); 5638c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, save_car); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, 5678c2ecf20Sopenharmony_ci void __iomem *base_addr) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct cyclades_port *info; 5708c2ecf20Sopenharmony_ci struct tty_struct *tty; 5718c2ecf20Sopenharmony_ci int char_count, index = cinfo->bus_index; 5728c2ecf20Sopenharmony_ci u8 save_xir, channel, save_car, outch; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Since we only get here when the transmit buffer 5758c2ecf20Sopenharmony_ci is empty, we know we can always stuff a dozen 5768c2ecf20Sopenharmony_ci characters. */ 5778c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS 5788c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip); 5798c2ecf20Sopenharmony_ci#endif 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* determine the channel & change to that context */ 5828c2ecf20Sopenharmony_ci save_xir = readb(base_addr + (CyTIR << index)); 5838c2ecf20Sopenharmony_ci channel = save_xir & CyIRChannel; 5848c2ecf20Sopenharmony_ci save_car = readb(base_addr + (CyCAR << index)); 5858c2ecf20Sopenharmony_ci cy_writeb(base_addr + (CyCAR << index), save_xir); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci info = &cinfo->ports[channel + chip * 4]; 5888c2ecf20Sopenharmony_ci tty = tty_port_tty_get(&info->port); 5898c2ecf20Sopenharmony_ci if (tty == NULL) { 5908c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); 5918c2ecf20Sopenharmony_ci goto end; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* load the on-chip space for outbound data */ 5958c2ecf20Sopenharmony_ci char_count = info->xmit_fifo_size; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (info->x_char) { /* send special char */ 5988c2ecf20Sopenharmony_ci outch = info->x_char; 5998c2ecf20Sopenharmony_ci cyy_writeb(info, CyTDR, outch); 6008c2ecf20Sopenharmony_ci char_count--; 6018c2ecf20Sopenharmony_ci info->icount.tx++; 6028c2ecf20Sopenharmony_ci info->x_char = 0; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (info->breakon || info->breakoff) { 6068c2ecf20Sopenharmony_ci if (info->breakon) { 6078c2ecf20Sopenharmony_ci cyy_writeb(info, CyTDR, 0); 6088c2ecf20Sopenharmony_ci cyy_writeb(info, CyTDR, 0x81); 6098c2ecf20Sopenharmony_ci info->breakon = 0; 6108c2ecf20Sopenharmony_ci char_count -= 2; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci if (info->breakoff) { 6138c2ecf20Sopenharmony_ci cyy_writeb(info, CyTDR, 0); 6148c2ecf20Sopenharmony_ci cyy_writeb(info, CyTDR, 0x83); 6158c2ecf20Sopenharmony_ci info->breakoff = 0; 6168c2ecf20Sopenharmony_ci char_count -= 2; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci while (char_count-- > 0) { 6218c2ecf20Sopenharmony_ci if (!info->xmit_cnt) { 6228c2ecf20Sopenharmony_ci if (cyy_readb(info, CySRER) & CyTxMpty) { 6238c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, 6248c2ecf20Sopenharmony_ci cyy_readb(info, CySRER) & ~CyTxMpty); 6258c2ecf20Sopenharmony_ci } else { 6268c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, CyTxMpty | 6278c2ecf20Sopenharmony_ci (cyy_readb(info, CySRER) & ~CyTxRdy)); 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci goto done; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci if (info->port.xmit_buf == NULL) { 6328c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, 6338c2ecf20Sopenharmony_ci cyy_readb(info, CySRER) & ~CyTxRdy); 6348c2ecf20Sopenharmony_ci goto done; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci if (tty->stopped || tty->hw_stopped) { 6378c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, 6388c2ecf20Sopenharmony_ci cyy_readb(info, CySRER) & ~CyTxRdy); 6398c2ecf20Sopenharmony_ci goto done; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci /* Because the Embedded Transmit Commands have been enabled, 6428c2ecf20Sopenharmony_ci * we must check to see if the escape character, NULL, is being 6438c2ecf20Sopenharmony_ci * sent. If it is, we must ensure that there is room for it to 6448c2ecf20Sopenharmony_ci * be doubled in the output stream. Therefore we no longer 6458c2ecf20Sopenharmony_ci * advance the pointer when the character is fetched, but 6468c2ecf20Sopenharmony_ci * rather wait until after the check for a NULL output 6478c2ecf20Sopenharmony_ci * character. This is necessary because there may not be room 6488c2ecf20Sopenharmony_ci * for the two chars needed to send a NULL.) 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ci outch = info->port.xmit_buf[info->xmit_tail]; 6518c2ecf20Sopenharmony_ci if (outch) { 6528c2ecf20Sopenharmony_ci info->xmit_cnt--; 6538c2ecf20Sopenharmony_ci info->xmit_tail = (info->xmit_tail + 1) & 6548c2ecf20Sopenharmony_ci (SERIAL_XMIT_SIZE - 1); 6558c2ecf20Sopenharmony_ci cyy_writeb(info, CyTDR, outch); 6568c2ecf20Sopenharmony_ci info->icount.tx++; 6578c2ecf20Sopenharmony_ci } else { 6588c2ecf20Sopenharmony_ci if (char_count > 1) { 6598c2ecf20Sopenharmony_ci info->xmit_cnt--; 6608c2ecf20Sopenharmony_ci info->xmit_tail = (info->xmit_tail + 1) & 6618c2ecf20Sopenharmony_ci (SERIAL_XMIT_SIZE - 1); 6628c2ecf20Sopenharmony_ci cyy_writeb(info, CyTDR, outch); 6638c2ecf20Sopenharmony_ci cyy_writeb(info, CyTDR, 0); 6648c2ecf20Sopenharmony_ci info->icount.tx++; 6658c2ecf20Sopenharmony_ci char_count--; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cidone: 6718c2ecf20Sopenharmony_ci tty_wakeup(tty); 6728c2ecf20Sopenharmony_ci tty_kref_put(tty); 6738c2ecf20Sopenharmony_ciend: 6748c2ecf20Sopenharmony_ci /* end of service */ 6758c2ecf20Sopenharmony_ci cyy_writeb(info, CyTIR, save_xir & 0x3f); 6768c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, save_car); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void cyy_chip_modem(struct cyclades_card *cinfo, int chip, 6808c2ecf20Sopenharmony_ci void __iomem *base_addr) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct cyclades_port *info; 6838c2ecf20Sopenharmony_ci struct tty_struct *tty; 6848c2ecf20Sopenharmony_ci int index = cinfo->bus_index; 6858c2ecf20Sopenharmony_ci u8 save_xir, channel, save_car, mdm_change, mdm_status; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* determine the channel & change to that context */ 6888c2ecf20Sopenharmony_ci save_xir = readb(base_addr + (CyMIR << index)); 6898c2ecf20Sopenharmony_ci channel = save_xir & CyIRChannel; 6908c2ecf20Sopenharmony_ci info = &cinfo->ports[channel + chip * 4]; 6918c2ecf20Sopenharmony_ci save_car = cyy_readb(info, CyCAR); 6928c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, save_xir); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci mdm_change = cyy_readb(info, CyMISR); 6958c2ecf20Sopenharmony_ci mdm_status = cyy_readb(info, CyMSVR1); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci tty = tty_port_tty_get(&info->port); 6988c2ecf20Sopenharmony_ci if (!tty) 6998c2ecf20Sopenharmony_ci goto end; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (mdm_change & CyANY_DELTA) { 7028c2ecf20Sopenharmony_ci /* For statistics only */ 7038c2ecf20Sopenharmony_ci if (mdm_change & CyDCD) 7048c2ecf20Sopenharmony_ci info->icount.dcd++; 7058c2ecf20Sopenharmony_ci if (mdm_change & CyCTS) 7068c2ecf20Sopenharmony_ci info->icount.cts++; 7078c2ecf20Sopenharmony_ci if (mdm_change & CyDSR) 7088c2ecf20Sopenharmony_ci info->icount.dsr++; 7098c2ecf20Sopenharmony_ci if (mdm_change & CyRI) 7108c2ecf20Sopenharmony_ci info->icount.rng++; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci wake_up_interruptible(&info->port.delta_msr_wait); 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if ((mdm_change & CyDCD) && tty_port_check_carrier(&info->port)) { 7168c2ecf20Sopenharmony_ci if (mdm_status & CyDCD) 7178c2ecf20Sopenharmony_ci wake_up_interruptible(&info->port.open_wait); 7188c2ecf20Sopenharmony_ci else 7198c2ecf20Sopenharmony_ci tty_hangup(tty); 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci if ((mdm_change & CyCTS) && tty_port_cts_enabled(&info->port)) { 7228c2ecf20Sopenharmony_ci if (tty->hw_stopped) { 7238c2ecf20Sopenharmony_ci if (mdm_status & CyCTS) { 7248c2ecf20Sopenharmony_ci /* cy_start isn't used 7258c2ecf20Sopenharmony_ci because... !!! */ 7268c2ecf20Sopenharmony_ci tty->hw_stopped = 0; 7278c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, 7288c2ecf20Sopenharmony_ci cyy_readb(info, CySRER) | CyTxRdy); 7298c2ecf20Sopenharmony_ci tty_wakeup(tty); 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci } else { 7328c2ecf20Sopenharmony_ci if (!(mdm_status & CyCTS)) { 7338c2ecf20Sopenharmony_ci /* cy_stop isn't used 7348c2ecf20Sopenharmony_ci because ... !!! */ 7358c2ecf20Sopenharmony_ci tty->hw_stopped = 1; 7368c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, 7378c2ecf20Sopenharmony_ci cyy_readb(info, CySRER) & ~CyTxRdy); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci/* if (mdm_change & CyDSR) { 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci if (mdm_change & CyRI) { 7448c2ecf20Sopenharmony_ci }*/ 7458c2ecf20Sopenharmony_ci tty_kref_put(tty); 7468c2ecf20Sopenharmony_ciend: 7478c2ecf20Sopenharmony_ci /* end of service */ 7488c2ecf20Sopenharmony_ci cyy_writeb(info, CyMIR, save_xir & 0x3f); 7498c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, save_car); 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci/* The real interrupt service routine is called 7538c2ecf20Sopenharmony_ci whenever the card wants its hand held--chars 7548c2ecf20Sopenharmony_ci received, out buffer empty, modem change, etc. 7558c2ecf20Sopenharmony_ci */ 7568c2ecf20Sopenharmony_cistatic irqreturn_t cyy_interrupt(int irq, void *dev_id) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci int status; 7598c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = dev_id; 7608c2ecf20Sopenharmony_ci void __iomem *base_addr, *card_base_addr; 7618c2ecf20Sopenharmony_ci unsigned int chip, too_many, had_work; 7628c2ecf20Sopenharmony_ci int index; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (unlikely(cinfo == NULL)) { 7658c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS 7668c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n", 7678c2ecf20Sopenharmony_ci irq); 7688c2ecf20Sopenharmony_ci#endif 7698c2ecf20Sopenharmony_ci return IRQ_NONE; /* spurious interrupt */ 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci card_base_addr = cinfo->base_addr; 7738c2ecf20Sopenharmony_ci index = cinfo->bus_index; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* card was not initialized yet (e.g. DEBUG_SHIRQ) */ 7768c2ecf20Sopenharmony_ci if (unlikely(card_base_addr == NULL)) 7778c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* This loop checks all chips in the card. Make a note whenever 7808c2ecf20Sopenharmony_ci _any_ chip had some work to do, as this is considered an 7818c2ecf20Sopenharmony_ci indication that there will be more to do. Only when no chip 7828c2ecf20Sopenharmony_ci has any work does this outermost loop exit. 7838c2ecf20Sopenharmony_ci */ 7848c2ecf20Sopenharmony_ci do { 7858c2ecf20Sopenharmony_ci had_work = 0; 7868c2ecf20Sopenharmony_ci for (chip = 0; chip < cinfo->num_chips; chip++) { 7878c2ecf20Sopenharmony_ci base_addr = cinfo->base_addr + 7888c2ecf20Sopenharmony_ci (cy_chip_offset[chip] << index); 7898c2ecf20Sopenharmony_ci too_many = 0; 7908c2ecf20Sopenharmony_ci while ((status = readb(base_addr + 7918c2ecf20Sopenharmony_ci (CySVRR << index))) != 0x00) { 7928c2ecf20Sopenharmony_ci had_work++; 7938c2ecf20Sopenharmony_ci /* The purpose of the following test is to ensure that 7948c2ecf20Sopenharmony_ci no chip can monopolize the driver. This forces the 7958c2ecf20Sopenharmony_ci chips to be checked in a round-robin fashion (after 7968c2ecf20Sopenharmony_ci draining each of a bunch (1000) of characters). 7978c2ecf20Sopenharmony_ci */ 7988c2ecf20Sopenharmony_ci if (1000 < too_many++) 7998c2ecf20Sopenharmony_ci break; 8008c2ecf20Sopenharmony_ci spin_lock(&cinfo->card_lock); 8018c2ecf20Sopenharmony_ci if (status & CySRReceive) /* rx intr */ 8028c2ecf20Sopenharmony_ci cyy_chip_rx(cinfo, chip, base_addr); 8038c2ecf20Sopenharmony_ci if (status & CySRTransmit) /* tx intr */ 8048c2ecf20Sopenharmony_ci cyy_chip_tx(cinfo, chip, base_addr); 8058c2ecf20Sopenharmony_ci if (status & CySRModem) /* modem intr */ 8068c2ecf20Sopenharmony_ci cyy_chip_modem(cinfo, chip, base_addr); 8078c2ecf20Sopenharmony_ci spin_unlock(&cinfo->card_lock); 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci } while (had_work); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* clear interrupts */ 8138c2ecf20Sopenharmony_ci spin_lock(&cinfo->card_lock); 8148c2ecf20Sopenharmony_ci cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0); 8158c2ecf20Sopenharmony_ci /* Cy_ClrIntr is 0x1800 */ 8168c2ecf20Sopenharmony_ci spin_unlock(&cinfo->card_lock); 8178c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8188c2ecf20Sopenharmony_ci} /* cyy_interrupt */ 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, 8218c2ecf20Sopenharmony_ci unsigned int clear) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct cyclades_card *card = info->card; 8248c2ecf20Sopenharmony_ci int channel = info->line - card->first_line; 8258c2ecf20Sopenharmony_ci u32 rts, dtr, msvrr, msvrd; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci channel &= 0x03; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (info->rtsdtr_inv) { 8308c2ecf20Sopenharmony_ci msvrr = CyMSVR2; 8318c2ecf20Sopenharmony_ci msvrd = CyMSVR1; 8328c2ecf20Sopenharmony_ci rts = CyDTR; 8338c2ecf20Sopenharmony_ci dtr = CyRTS; 8348c2ecf20Sopenharmony_ci } else { 8358c2ecf20Sopenharmony_ci msvrr = CyMSVR1; 8368c2ecf20Sopenharmony_ci msvrd = CyMSVR2; 8378c2ecf20Sopenharmony_ci rts = CyRTS; 8388c2ecf20Sopenharmony_ci dtr = CyDTR; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci if (set & TIOCM_RTS) { 8418c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel); 8428c2ecf20Sopenharmony_ci cyy_writeb(info, msvrr, rts); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci if (clear & TIOCM_RTS) { 8458c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel); 8468c2ecf20Sopenharmony_ci cyy_writeb(info, msvrr, ~rts); 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci if (set & TIOCM_DTR) { 8498c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel); 8508c2ecf20Sopenharmony_ci cyy_writeb(info, msvrd, dtr); 8518c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR 8528c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); 8538c2ecf20Sopenharmony_ci printk(KERN_DEBUG " status: 0x%x, 0x%x\n", 8548c2ecf20Sopenharmony_ci cyy_readb(info, CyMSVR1), 8558c2ecf20Sopenharmony_ci cyy_readb(info, CyMSVR2)); 8568c2ecf20Sopenharmony_ci#endif 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci if (clear & TIOCM_DTR) { 8598c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel); 8608c2ecf20Sopenharmony_ci cyy_writeb(info, msvrd, ~dtr); 8618c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR 8628c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); 8638c2ecf20Sopenharmony_ci printk(KERN_DEBUG " status: 0x%x, 0x%x\n", 8648c2ecf20Sopenharmony_ci cyy_readb(info, CyMSVR1), 8658c2ecf20Sopenharmony_ci cyy_readb(info, CyMSVR2)); 8668c2ecf20Sopenharmony_ci#endif 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci/***********************************************************/ 8718c2ecf20Sopenharmony_ci/********* End of block of Cyclom-Y specific code **********/ 8728c2ecf20Sopenharmony_ci/******** Start of block of Cyclades-Z specific code *******/ 8738c2ecf20Sopenharmony_ci/***********************************************************/ 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic int 8768c2ecf20Sopenharmony_cicyz_fetch_msg(struct cyclades_card *cinfo, 8778c2ecf20Sopenharmony_ci __u32 *channel, __u8 *cmd, __u32 *param) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; 8808c2ecf20Sopenharmony_ci unsigned long loc_doorbell; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell); 8838c2ecf20Sopenharmony_ci if (loc_doorbell) { 8848c2ecf20Sopenharmony_ci *cmd = (char)(0xff & loc_doorbell); 8858c2ecf20Sopenharmony_ci *channel = readl(&board_ctrl->fwcmd_channel); 8868c2ecf20Sopenharmony_ci *param = (__u32) readl(&board_ctrl->fwcmd_param); 8878c2ecf20Sopenharmony_ci cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff); 8888c2ecf20Sopenharmony_ci return 1; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci return 0; 8918c2ecf20Sopenharmony_ci} /* cyz_fetch_msg */ 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic int 8948c2ecf20Sopenharmony_cicyz_issue_cmd(struct cyclades_card *cinfo, 8958c2ecf20Sopenharmony_ci __u32 channel, __u8 cmd, __u32 param) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; 8988c2ecf20Sopenharmony_ci __u32 __iomem *pci_doorbell; 8998c2ecf20Sopenharmony_ci unsigned int index; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (!cyz_is_loaded(cinfo)) 9028c2ecf20Sopenharmony_ci return -1; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci index = 0; 9058c2ecf20Sopenharmony_ci pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell; 9068c2ecf20Sopenharmony_ci while ((readl(pci_doorbell) & 0xff) != 0) { 9078c2ecf20Sopenharmony_ci if (index++ == 1000) 9088c2ecf20Sopenharmony_ci return (int)(readl(pci_doorbell) & 0xff); 9098c2ecf20Sopenharmony_ci udelay(50L); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci cy_writel(&board_ctrl->hcmd_channel, channel); 9128c2ecf20Sopenharmony_ci cy_writel(&board_ctrl->hcmd_param, param); 9138c2ecf20Sopenharmony_ci cy_writel(pci_doorbell, (long)cmd); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci} /* cyz_issue_cmd */ 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic void cyz_handle_rx(struct cyclades_port *info) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; 9218c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = info->card; 9228c2ecf20Sopenharmony_ci struct tty_port *port = &info->port; 9238c2ecf20Sopenharmony_ci unsigned int char_count; 9248c2ecf20Sopenharmony_ci int len; 9258c2ecf20Sopenharmony_ci#ifdef BLOCKMOVE 9268c2ecf20Sopenharmony_ci unsigned char *buf; 9278c2ecf20Sopenharmony_ci#else 9288c2ecf20Sopenharmony_ci char data; 9298c2ecf20Sopenharmony_ci#endif 9308c2ecf20Sopenharmony_ci __u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci rx_get = new_rx_get = readl(&buf_ctrl->rx_get); 9338c2ecf20Sopenharmony_ci rx_put = readl(&buf_ctrl->rx_put); 9348c2ecf20Sopenharmony_ci rx_bufsize = readl(&buf_ctrl->rx_bufsize); 9358c2ecf20Sopenharmony_ci rx_bufaddr = readl(&buf_ctrl->rx_bufaddr); 9368c2ecf20Sopenharmony_ci if (rx_put >= rx_get) 9378c2ecf20Sopenharmony_ci char_count = rx_put - rx_get; 9388c2ecf20Sopenharmony_ci else 9398c2ecf20Sopenharmony_ci char_count = rx_put - rx_get + rx_bufsize; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (!char_count) 9428c2ecf20Sopenharmony_ci return; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci#ifdef CY_ENABLE_MONITORING 9458c2ecf20Sopenharmony_ci info->mon.int_count++; 9468c2ecf20Sopenharmony_ci info->mon.char_count += char_count; 9478c2ecf20Sopenharmony_ci if (char_count > info->mon.char_max) 9488c2ecf20Sopenharmony_ci info->mon.char_max = char_count; 9498c2ecf20Sopenharmony_ci info->mon.char_last = char_count; 9508c2ecf20Sopenharmony_ci#endif 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci#ifdef BLOCKMOVE 9538c2ecf20Sopenharmony_ci /* we'd like to use memcpy(t, f, n) and memset(s, c, count) 9548c2ecf20Sopenharmony_ci for performance, but because of buffer boundaries, there 9558c2ecf20Sopenharmony_ci may be several steps to the operation */ 9568c2ecf20Sopenharmony_ci while (1) { 9578c2ecf20Sopenharmony_ci len = tty_prepare_flip_string(port, &buf, 9588c2ecf20Sopenharmony_ci char_count); 9598c2ecf20Sopenharmony_ci if (!len) 9608c2ecf20Sopenharmony_ci break; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci len = min_t(unsigned int, min(len, char_count), 9638c2ecf20Sopenharmony_ci rx_bufsize - new_rx_get); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci memcpy_fromio(buf, cinfo->base_addr + 9668c2ecf20Sopenharmony_ci rx_bufaddr + new_rx_get, len); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci new_rx_get = (new_rx_get + len) & 9698c2ecf20Sopenharmony_ci (rx_bufsize - 1); 9708c2ecf20Sopenharmony_ci char_count -= len; 9718c2ecf20Sopenharmony_ci info->icount.rx += len; 9728c2ecf20Sopenharmony_ci info->idle_stats.recv_bytes += len; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci#else 9758c2ecf20Sopenharmony_ci len = tty_buffer_request_room(port, char_count); 9768c2ecf20Sopenharmony_ci while (len--) { 9778c2ecf20Sopenharmony_ci data = readb(cinfo->base_addr + rx_bufaddr + 9788c2ecf20Sopenharmony_ci new_rx_get); 9798c2ecf20Sopenharmony_ci new_rx_get = (new_rx_get + 1) & 9808c2ecf20Sopenharmony_ci (rx_bufsize - 1); 9818c2ecf20Sopenharmony_ci tty_insert_flip_char(port, data, TTY_NORMAL); 9828c2ecf20Sopenharmony_ci info->idle_stats.recv_bytes++; 9838c2ecf20Sopenharmony_ci info->icount.rx++; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci#endif 9868c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 9878c2ecf20Sopenharmony_ci /* Recalculate the number of chars in the RX buffer and issue 9888c2ecf20Sopenharmony_ci a cmd in case it's higher than the RX high water mark */ 9898c2ecf20Sopenharmony_ci rx_put = readl(&buf_ctrl->rx_put); 9908c2ecf20Sopenharmony_ci if (rx_put >= rx_get) 9918c2ecf20Sopenharmony_ci char_count = rx_put - rx_get; 9928c2ecf20Sopenharmony_ci else 9938c2ecf20Sopenharmony_ci char_count = rx_put - rx_get + rx_bufsize; 9948c2ecf20Sopenharmony_ci if (char_count >= readl(&buf_ctrl->rx_threshold) && 9958c2ecf20Sopenharmony_ci !timer_pending(&info->rx_full_timer)) 9968c2ecf20Sopenharmony_ci mod_timer(&info->rx_full_timer, jiffies + 1); 9978c2ecf20Sopenharmony_ci#endif 9988c2ecf20Sopenharmony_ci info->idle_stats.recv_idle = jiffies; 9998c2ecf20Sopenharmony_ci tty_flip_buffer_push(&info->port); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* Update rx_get */ 10028c2ecf20Sopenharmony_ci cy_writel(&buf_ctrl->rx_get, new_rx_get); 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic void cyz_handle_tx(struct cyclades_port *info) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; 10088c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = info->card; 10098c2ecf20Sopenharmony_ci struct tty_struct *tty; 10108c2ecf20Sopenharmony_ci u8 data; 10118c2ecf20Sopenharmony_ci unsigned int char_count; 10128c2ecf20Sopenharmony_ci#ifdef BLOCKMOVE 10138c2ecf20Sopenharmony_ci int small_count; 10148c2ecf20Sopenharmony_ci#endif 10158c2ecf20Sopenharmony_ci __u32 tx_put, tx_get, tx_bufsize, tx_bufaddr; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (info->xmit_cnt <= 0) /* Nothing to transmit */ 10188c2ecf20Sopenharmony_ci return; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci tx_get = readl(&buf_ctrl->tx_get); 10218c2ecf20Sopenharmony_ci tx_put = readl(&buf_ctrl->tx_put); 10228c2ecf20Sopenharmony_ci tx_bufsize = readl(&buf_ctrl->tx_bufsize); 10238c2ecf20Sopenharmony_ci tx_bufaddr = readl(&buf_ctrl->tx_bufaddr); 10248c2ecf20Sopenharmony_ci if (tx_put >= tx_get) 10258c2ecf20Sopenharmony_ci char_count = tx_get - tx_put - 1 + tx_bufsize; 10268c2ecf20Sopenharmony_ci else 10278c2ecf20Sopenharmony_ci char_count = tx_get - tx_put - 1; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (!char_count) 10308c2ecf20Sopenharmony_ci return; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci tty = tty_port_tty_get(&info->port); 10338c2ecf20Sopenharmony_ci if (tty == NULL) 10348c2ecf20Sopenharmony_ci goto ztxdone; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (info->x_char) { /* send special char */ 10378c2ecf20Sopenharmony_ci data = info->x_char; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); 10408c2ecf20Sopenharmony_ci tx_put = (tx_put + 1) & (tx_bufsize - 1); 10418c2ecf20Sopenharmony_ci info->x_char = 0; 10428c2ecf20Sopenharmony_ci char_count--; 10438c2ecf20Sopenharmony_ci info->icount.tx++; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci#ifdef BLOCKMOVE 10468c2ecf20Sopenharmony_ci while (0 < (small_count = min_t(unsigned int, 10478c2ecf20Sopenharmony_ci tx_bufsize - tx_put, min_t(unsigned int, 10488c2ecf20Sopenharmony_ci (SERIAL_XMIT_SIZE - info->xmit_tail), 10498c2ecf20Sopenharmony_ci min_t(unsigned int, info->xmit_cnt, 10508c2ecf20Sopenharmony_ci char_count))))) { 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put), 10538c2ecf20Sopenharmony_ci &info->port.xmit_buf[info->xmit_tail], 10548c2ecf20Sopenharmony_ci small_count); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci tx_put = (tx_put + small_count) & (tx_bufsize - 1); 10578c2ecf20Sopenharmony_ci char_count -= small_count; 10588c2ecf20Sopenharmony_ci info->icount.tx += small_count; 10598c2ecf20Sopenharmony_ci info->xmit_cnt -= small_count; 10608c2ecf20Sopenharmony_ci info->xmit_tail = (info->xmit_tail + small_count) & 10618c2ecf20Sopenharmony_ci (SERIAL_XMIT_SIZE - 1); 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci#else 10648c2ecf20Sopenharmony_ci while (info->xmit_cnt && char_count) { 10658c2ecf20Sopenharmony_ci data = info->port.xmit_buf[info->xmit_tail]; 10668c2ecf20Sopenharmony_ci info->xmit_cnt--; 10678c2ecf20Sopenharmony_ci info->xmit_tail = (info->xmit_tail + 1) & 10688c2ecf20Sopenharmony_ci (SERIAL_XMIT_SIZE - 1); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); 10718c2ecf20Sopenharmony_ci tx_put = (tx_put + 1) & (tx_bufsize - 1); 10728c2ecf20Sopenharmony_ci char_count--; 10738c2ecf20Sopenharmony_ci info->icount.tx++; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci#endif 10768c2ecf20Sopenharmony_ci tty_wakeup(tty); 10778c2ecf20Sopenharmony_ci tty_kref_put(tty); 10788c2ecf20Sopenharmony_ciztxdone: 10798c2ecf20Sopenharmony_ci /* Update tx_put */ 10808c2ecf20Sopenharmony_ci cy_writel(&buf_ctrl->tx_put, tx_put); 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic void cyz_handle_cmd(struct cyclades_card *cinfo) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; 10868c2ecf20Sopenharmony_ci struct cyclades_port *info; 10878c2ecf20Sopenharmony_ci __u32 channel, param, fw_ver; 10888c2ecf20Sopenharmony_ci __u8 cmd; 10898c2ecf20Sopenharmony_ci int special_count; 10908c2ecf20Sopenharmony_ci int delta_count; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci fw_ver = readl(&board_ctrl->fw_version); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { 10958c2ecf20Sopenharmony_ci special_count = 0; 10968c2ecf20Sopenharmony_ci delta_count = 0; 10978c2ecf20Sopenharmony_ci info = &cinfo->ports[channel]; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci switch (cmd) { 11008c2ecf20Sopenharmony_ci case C_CM_PR_ERROR: 11018c2ecf20Sopenharmony_ci tty_insert_flip_char(&info->port, 0, TTY_PARITY); 11028c2ecf20Sopenharmony_ci info->icount.rx++; 11038c2ecf20Sopenharmony_ci special_count++; 11048c2ecf20Sopenharmony_ci break; 11058c2ecf20Sopenharmony_ci case C_CM_FR_ERROR: 11068c2ecf20Sopenharmony_ci tty_insert_flip_char(&info->port, 0, TTY_FRAME); 11078c2ecf20Sopenharmony_ci info->icount.rx++; 11088c2ecf20Sopenharmony_ci special_count++; 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci case C_CM_RXBRK: 11118c2ecf20Sopenharmony_ci tty_insert_flip_char(&info->port, 0, TTY_BREAK); 11128c2ecf20Sopenharmony_ci info->icount.rx++; 11138c2ecf20Sopenharmony_ci special_count++; 11148c2ecf20Sopenharmony_ci break; 11158c2ecf20Sopenharmony_ci case C_CM_MDCD: 11168c2ecf20Sopenharmony_ci info->icount.dcd++; 11178c2ecf20Sopenharmony_ci delta_count++; 11188c2ecf20Sopenharmony_ci if (tty_port_check_carrier(&info->port)) { 11198c2ecf20Sopenharmony_ci u32 dcd = fw_ver > 241 ? param : 11208c2ecf20Sopenharmony_ci readl(&info->u.cyz.ch_ctrl->rs_status); 11218c2ecf20Sopenharmony_ci if (dcd & C_RS_DCD) 11228c2ecf20Sopenharmony_ci wake_up_interruptible(&info->port.open_wait); 11238c2ecf20Sopenharmony_ci else 11248c2ecf20Sopenharmony_ci tty_port_tty_hangup(&info->port, false); 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci break; 11278c2ecf20Sopenharmony_ci case C_CM_MCTS: 11288c2ecf20Sopenharmony_ci info->icount.cts++; 11298c2ecf20Sopenharmony_ci delta_count++; 11308c2ecf20Sopenharmony_ci break; 11318c2ecf20Sopenharmony_ci case C_CM_MRI: 11328c2ecf20Sopenharmony_ci info->icount.rng++; 11338c2ecf20Sopenharmony_ci delta_count++; 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci case C_CM_MDSR: 11368c2ecf20Sopenharmony_ci info->icount.dsr++; 11378c2ecf20Sopenharmony_ci delta_count++; 11388c2ecf20Sopenharmony_ci break; 11398c2ecf20Sopenharmony_ci#ifdef Z_WAKE 11408c2ecf20Sopenharmony_ci case C_CM_IOCTLW: 11418c2ecf20Sopenharmony_ci complete(&info->shutdown_wait); 11428c2ecf20Sopenharmony_ci break; 11438c2ecf20Sopenharmony_ci#endif 11448c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 11458c2ecf20Sopenharmony_ci case C_CM_RXHIWM: 11468c2ecf20Sopenharmony_ci case C_CM_RXNNDT: 11478c2ecf20Sopenharmony_ci case C_CM_INTBACK2: 11488c2ecf20Sopenharmony_ci /* Reception Interrupt */ 11498c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS 11508c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " 11518c2ecf20Sopenharmony_ci "port %ld\n", info->card, channel); 11528c2ecf20Sopenharmony_ci#endif 11538c2ecf20Sopenharmony_ci cyz_handle_rx(info); 11548c2ecf20Sopenharmony_ci break; 11558c2ecf20Sopenharmony_ci case C_CM_TXBEMPTY: 11568c2ecf20Sopenharmony_ci case C_CM_TXLOWWM: 11578c2ecf20Sopenharmony_ci case C_CM_INTBACK: 11588c2ecf20Sopenharmony_ci /* Transmission Interrupt */ 11598c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS 11608c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " 11618c2ecf20Sopenharmony_ci "port %ld\n", info->card, channel); 11628c2ecf20Sopenharmony_ci#endif 11638c2ecf20Sopenharmony_ci cyz_handle_tx(info); 11648c2ecf20Sopenharmony_ci break; 11658c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 11668c2ecf20Sopenharmony_ci case C_CM_FATAL: 11678c2ecf20Sopenharmony_ci /* should do something with this !!! */ 11688c2ecf20Sopenharmony_ci break; 11698c2ecf20Sopenharmony_ci default: 11708c2ecf20Sopenharmony_ci break; 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci if (delta_count) 11738c2ecf20Sopenharmony_ci wake_up_interruptible(&info->port.delta_msr_wait); 11748c2ecf20Sopenharmony_ci if (special_count) 11758c2ecf20Sopenharmony_ci tty_flip_buffer_push(&info->port); 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 11808c2ecf20Sopenharmony_cistatic irqreturn_t cyz_interrupt(int irq, void *dev_id) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = dev_id; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (unlikely(!cyz_is_loaded(cinfo))) { 11858c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS 11868c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyz_interrupt: board not yet loaded " 11878c2ecf20Sopenharmony_ci "(IRQ%d).\n", irq); 11888c2ecf20Sopenharmony_ci#endif 11898c2ecf20Sopenharmony_ci return IRQ_NONE; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* Handle the interrupts */ 11938c2ecf20Sopenharmony_ci cyz_handle_cmd(cinfo); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11968c2ecf20Sopenharmony_ci} /* cyz_interrupt */ 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic void cyz_rx_restart(struct timer_list *t) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci struct cyclades_port *info = from_timer(info, t, rx_full_timer); 12018c2ecf20Sopenharmony_ci struct cyclades_card *card = info->card; 12028c2ecf20Sopenharmony_ci int retval; 12038c2ecf20Sopenharmony_ci __u32 channel = info->line - card->first_line; 12048c2ecf20Sopenharmony_ci unsigned long flags; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 12078c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L); 12088c2ecf20Sopenharmony_ci if (retval != 0) { 12098c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n", 12108c2ecf20Sopenharmony_ci info->line, retval); 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci#else /* CONFIG_CYZ_INTR */ 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic void cyz_poll(struct timer_list *unused) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct cyclades_card *cinfo; 12208c2ecf20Sopenharmony_ci struct cyclades_port *info; 12218c2ecf20Sopenharmony_ci unsigned long expires = jiffies + HZ; 12228c2ecf20Sopenharmony_ci unsigned int port, card; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci for (card = 0; card < NR_CARDS; card++) { 12258c2ecf20Sopenharmony_ci cinfo = &cy_card[card]; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (!cy_is_Z(cinfo)) 12288c2ecf20Sopenharmony_ci continue; 12298c2ecf20Sopenharmony_ci if (!cyz_is_loaded(cinfo)) 12308c2ecf20Sopenharmony_ci continue; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* Skip first polling cycle to avoid racing conditions with the FW */ 12338c2ecf20Sopenharmony_ci if (!cinfo->intr_enabled) { 12348c2ecf20Sopenharmony_ci cinfo->intr_enabled = 1; 12358c2ecf20Sopenharmony_ci continue; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci cyz_handle_cmd(cinfo); 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci for (port = 0; port < cinfo->nports; port++) { 12418c2ecf20Sopenharmony_ci info = &cinfo->ports[port]; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci if (!info->throttle) 12448c2ecf20Sopenharmony_ci cyz_handle_rx(info); 12458c2ecf20Sopenharmony_ci cyz_handle_tx(info); 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci /* poll every 'cyz_polling_cycle' period */ 12488c2ecf20Sopenharmony_ci expires = jiffies + cyz_polling_cycle; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci mod_timer(&cyz_timerlist, expires); 12518c2ecf20Sopenharmony_ci} /* cyz_poll */ 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci/********** End of block of Cyclades-Z specific code *********/ 12568c2ecf20Sopenharmony_ci/***********************************************************/ 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci/* This is called whenever a port becomes active; 12598c2ecf20Sopenharmony_ci interrupts are enabled and DTR & RTS are turned on. 12608c2ecf20Sopenharmony_ci */ 12618c2ecf20Sopenharmony_cistatic int cy_startup(struct cyclades_port *info, struct tty_struct *tty) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct cyclades_card *card; 12648c2ecf20Sopenharmony_ci unsigned long flags; 12658c2ecf20Sopenharmony_ci int retval = 0; 12668c2ecf20Sopenharmony_ci int channel; 12678c2ecf20Sopenharmony_ci unsigned long page; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci card = info->card; 12708c2ecf20Sopenharmony_ci channel = info->line - card->first_line; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci page = get_zeroed_page(GFP_KERNEL); 12738c2ecf20Sopenharmony_ci if (!page) 12748c2ecf20Sopenharmony_ci return -ENOMEM; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (tty_port_initialized(&info->port)) 12798c2ecf20Sopenharmony_ci goto errout; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (!info->type) { 12828c2ecf20Sopenharmony_ci set_bit(TTY_IO_ERROR, &tty->flags); 12838c2ecf20Sopenharmony_ci goto errout; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (info->port.xmit_buf) 12878c2ecf20Sopenharmony_ci free_page(page); 12888c2ecf20Sopenharmony_ci else 12898c2ecf20Sopenharmony_ci info->port.xmit_buf = (unsigned char *)page; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci cy_set_line_char(info, tty); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 12968c2ecf20Sopenharmony_ci channel &= 0x03; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci cyy_writeb(info, CyRTPR, 13038c2ecf20Sopenharmony_ci (info->default_timeout ? info->default_timeout : 0x02)); 13048c2ecf20Sopenharmony_ci /* 10ms rx timeout */ 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData); 13118c2ecf20Sopenharmony_ci } else { 13128c2ecf20Sopenharmony_ci struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci if (!cyz_is_loaded(card)) 13158c2ecf20Sopenharmony_ci return -ENODEV; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN 13188c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc startup Z card %d, channel %d, " 13198c2ecf20Sopenharmony_ci "base_addr %p\n", card, channel, card->base_addr); 13208c2ecf20Sopenharmony_ci#endif 13218c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE); 13248c2ecf20Sopenharmony_ci#ifdef Z_WAKE 13258c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 13268c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->intr_enable, 13278c2ecf20Sopenharmony_ci C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | 13288c2ecf20Sopenharmony_ci C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD); 13298c2ecf20Sopenharmony_ci#else 13308c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->intr_enable, 13318c2ecf20Sopenharmony_ci C_IN_IOCTLW | C_IN_MDCD); 13328c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 13338c2ecf20Sopenharmony_ci#else 13348c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 13358c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->intr_enable, 13368c2ecf20Sopenharmony_ci C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | 13378c2ecf20Sopenharmony_ci C_IN_RXNNDT | C_IN_MDCD); 13388c2ecf20Sopenharmony_ci#else 13398c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD); 13408c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 13418c2ecf20Sopenharmony_ci#endif /* Z_WAKE */ 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); 13448c2ecf20Sopenharmony_ci if (retval != 0) { 13458c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:startup(1) retval on ttyC%d was " 13468c2ecf20Sopenharmony_ci "%x\n", info->line, retval); 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci /* Flush RX buffers before raising DTR and RTS */ 13508c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_RX, 0L); 13518c2ecf20Sopenharmony_ci if (retval != 0) { 13528c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:startup(2) retval on ttyC%d was " 13538c2ecf20Sopenharmony_ci "%x\n", info->line, retval); 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* set timeout !!! */ 13578c2ecf20Sopenharmony_ci /* set RTS and DTR !!! */ 13588c2ecf20Sopenharmony_ci tty_port_raise_dtr_rts(&info->port); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* enable send, recv, modem !!! */ 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci tty_port_set_initialized(&info->port, 1); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci clear_bit(TTY_IO_ERROR, &tty->flags); 13668c2ecf20Sopenharmony_ci info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; 13678c2ecf20Sopenharmony_ci info->breakon = info->breakoff = 0; 13688c2ecf20Sopenharmony_ci memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); 13698c2ecf20Sopenharmony_ci info->idle_stats.in_use = 13708c2ecf20Sopenharmony_ci info->idle_stats.recv_idle = 13718c2ecf20Sopenharmony_ci info->idle_stats.xmit_idle = jiffies; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN 13768c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc startup done\n"); 13778c2ecf20Sopenharmony_ci#endif 13788c2ecf20Sopenharmony_ci return 0; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cierrout: 13818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 13828c2ecf20Sopenharmony_ci free_page(page); 13838c2ecf20Sopenharmony_ci return retval; 13848c2ecf20Sopenharmony_ci} /* startup */ 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_cistatic void start_xmit(struct cyclades_port *info) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci struct cyclades_card *card = info->card; 13898c2ecf20Sopenharmony_ci unsigned long flags; 13908c2ecf20Sopenharmony_ci int channel = info->line - card->first_line; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 13938c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 13948c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel & 0x03); 13958c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); 13968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 13978c2ecf20Sopenharmony_ci } else { 13988c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 13998c2ecf20Sopenharmony_ci int retval; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 14028c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L); 14038c2ecf20Sopenharmony_ci if (retval != 0) { 14048c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was " 14058c2ecf20Sopenharmony_ci "%x\n", info->line, retval); 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 14088c2ecf20Sopenharmony_ci#else /* CONFIG_CYZ_INTR */ 14098c2ecf20Sopenharmony_ci /* Don't have to do anything at this time */ 14108c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci} /* start_xmit */ 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci/* 14158c2ecf20Sopenharmony_ci * This routine shuts down a serial port; interrupts are disabled, 14168c2ecf20Sopenharmony_ci * and DTR is dropped if the hangup on close termio flag is on. 14178c2ecf20Sopenharmony_ci */ 14188c2ecf20Sopenharmony_cistatic void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct cyclades_card *card; 14218c2ecf20Sopenharmony_ci unsigned long flags; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci if (!tty_port_initialized(&info->port)) 14248c2ecf20Sopenharmony_ci return; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci card = info->card; 14278c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 14288c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci /* Clear delta_msr_wait queue to avoid mem leaks. */ 14318c2ecf20Sopenharmony_ci wake_up_interruptible(&info->port.delta_msr_wait); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (info->port.xmit_buf) { 14348c2ecf20Sopenharmony_ci unsigned char *temp; 14358c2ecf20Sopenharmony_ci temp = info->port.xmit_buf; 14368c2ecf20Sopenharmony_ci info->port.xmit_buf = NULL; 14378c2ecf20Sopenharmony_ci free_page((unsigned long)temp); 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci if (C_HUPCL(tty)) 14408c2ecf20Sopenharmony_ci cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); 14438c2ecf20Sopenharmony_ci /* it may be appropriate to clear _XMIT at 14448c2ecf20Sopenharmony_ci some later date (after testing)!!! */ 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci set_bit(TTY_IO_ERROR, &tty->flags); 14478c2ecf20Sopenharmony_ci tty_port_set_initialized(&info->port, 0); 14488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 14498c2ecf20Sopenharmony_ci } else { 14508c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN 14518c2ecf20Sopenharmony_ci int channel = info->line - card->first_line; 14528c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " 14538c2ecf20Sopenharmony_ci "base_addr %p\n", card, channel, card->base_addr); 14548c2ecf20Sopenharmony_ci#endif 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (!cyz_is_loaded(card)) 14578c2ecf20Sopenharmony_ci return; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (info->port.xmit_buf) { 14628c2ecf20Sopenharmony_ci unsigned char *temp; 14638c2ecf20Sopenharmony_ci temp = info->port.xmit_buf; 14648c2ecf20Sopenharmony_ci info->port.xmit_buf = NULL; 14658c2ecf20Sopenharmony_ci free_page((unsigned long)temp); 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci if (C_HUPCL(tty)) 14698c2ecf20Sopenharmony_ci tty_port_lower_dtr_rts(&info->port); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci set_bit(TTY_IO_ERROR, &tty->flags); 14728c2ecf20Sopenharmony_ci tty_port_set_initialized(&info->port, 0); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN 14788c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc shutdown done\n"); 14798c2ecf20Sopenharmony_ci#endif 14808c2ecf20Sopenharmony_ci} /* shutdown */ 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci/* 14838c2ecf20Sopenharmony_ci * ------------------------------------------------------------ 14848c2ecf20Sopenharmony_ci * cy_open() and friends 14858c2ecf20Sopenharmony_ci * ------------------------------------------------------------ 14868c2ecf20Sopenharmony_ci */ 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci/* 14898c2ecf20Sopenharmony_ci * This routine is called whenever a serial port is opened. It 14908c2ecf20Sopenharmony_ci * performs the serial-specific initialization for the tty structure. 14918c2ecf20Sopenharmony_ci */ 14928c2ecf20Sopenharmony_cistatic int cy_open(struct tty_struct *tty, struct file *filp) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci struct cyclades_port *info; 14958c2ecf20Sopenharmony_ci unsigned int i, line = tty->index; 14968c2ecf20Sopenharmony_ci int retval; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci for (i = 0; i < NR_CARDS; i++) 14998c2ecf20Sopenharmony_ci if (line < cy_card[i].first_line + cy_card[i].nports && 15008c2ecf20Sopenharmony_ci line >= cy_card[i].first_line) 15018c2ecf20Sopenharmony_ci break; 15028c2ecf20Sopenharmony_ci if (i >= NR_CARDS) 15038c2ecf20Sopenharmony_ci return -ENODEV; 15048c2ecf20Sopenharmony_ci info = &cy_card[i].ports[line - cy_card[i].first_line]; 15058c2ecf20Sopenharmony_ci if (info->line < 0) 15068c2ecf20Sopenharmony_ci return -ENODEV; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci /* If the card's firmware hasn't been loaded, 15098c2ecf20Sopenharmony_ci treat it as absent from the system. This 15108c2ecf20Sopenharmony_ci will make the user pay attention. 15118c2ecf20Sopenharmony_ci */ 15128c2ecf20Sopenharmony_ci if (cy_is_Z(info->card)) { 15138c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = info->card; 15148c2ecf20Sopenharmony_ci struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (!cyz_is_loaded(cinfo)) { 15178c2ecf20Sopenharmony_ci if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) && 15188c2ecf20Sopenharmony_ci readl(&firm_id->signature) == 15198c2ecf20Sopenharmony_ci ZFIRM_HLT) { 15208c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:Cyclades-Z Error: you " 15218c2ecf20Sopenharmony_ci "need an external power supply for " 15228c2ecf20Sopenharmony_ci "this number of ports.\nFirmware " 15238c2ecf20Sopenharmony_ci "halted.\n"); 15248c2ecf20Sopenharmony_ci } else { 15258c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:Cyclades-Z firmware not " 15268c2ecf20Sopenharmony_ci "yet loaded\n"); 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci return -ENODEV; 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 15318c2ecf20Sopenharmony_ci else { 15328c2ecf20Sopenharmony_ci /* In case this Z board is operating in interrupt mode, its 15338c2ecf20Sopenharmony_ci interrupts should be enabled as soon as the first open 15348c2ecf20Sopenharmony_ci happens to one of its ports. */ 15358c2ecf20Sopenharmony_ci if (!cinfo->intr_enabled) { 15368c2ecf20Sopenharmony_ci u16 intr; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* Enable interrupts on the PLX chip */ 15398c2ecf20Sopenharmony_ci intr = readw(&cinfo->ctl_addr.p9060-> 15408c2ecf20Sopenharmony_ci intr_ctrl_stat) | 0x0900; 15418c2ecf20Sopenharmony_ci cy_writew(&cinfo->ctl_addr.p9060-> 15428c2ecf20Sopenharmony_ci intr_ctrl_stat, intr); 15438c2ecf20Sopenharmony_ci /* Enable interrupts on the FW */ 15448c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(cinfo, 0, 15458c2ecf20Sopenharmony_ci C_CM_IRQ_ENBL, 0L); 15468c2ecf20Sopenharmony_ci if (retval != 0) { 15478c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:IRQ enable retval " 15488c2ecf20Sopenharmony_ci "was %x\n", retval); 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci cinfo->intr_enabled = 1; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 15548c2ecf20Sopenharmony_ci /* Make sure this Z port really exists in hardware */ 15558c2ecf20Sopenharmony_ci if (info->line > (cinfo->first_line + cinfo->nports - 1)) 15568c2ecf20Sopenharmony_ci return -ENODEV; 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER 15598c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line); 15608c2ecf20Sopenharmony_ci#endif 15618c2ecf20Sopenharmony_ci tty->driver_data = info; 15628c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_open")) 15638c2ecf20Sopenharmony_ci return -ENODEV; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN 15668c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line, 15678c2ecf20Sopenharmony_ci info->port.count); 15688c2ecf20Sopenharmony_ci#endif 15698c2ecf20Sopenharmony_ci info->port.count++; 15708c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_COUNT 15718c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n", 15728c2ecf20Sopenharmony_ci current->pid, info->port.count); 15738c2ecf20Sopenharmony_ci#endif 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci /* 15768c2ecf20Sopenharmony_ci * Start up serial port 15778c2ecf20Sopenharmony_ci */ 15788c2ecf20Sopenharmony_ci retval = cy_startup(info, tty); 15798c2ecf20Sopenharmony_ci if (retval) 15808c2ecf20Sopenharmony_ci return retval; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci retval = tty_port_block_til_ready(&info->port, tty, filp); 15838c2ecf20Sopenharmony_ci if (retval) { 15848c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN 15858c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready " 15868c2ecf20Sopenharmony_ci "with %d\n", retval); 15878c2ecf20Sopenharmony_ci#endif 15888c2ecf20Sopenharmony_ci return retval; 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci info->throttle = 0; 15928c2ecf20Sopenharmony_ci tty_port_tty_set(&info->port, tty); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN 15958c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_open done\n"); 15968c2ecf20Sopenharmony_ci#endif 15978c2ecf20Sopenharmony_ci return 0; 15988c2ecf20Sopenharmony_ci} /* cy_open */ 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci/* 16018c2ecf20Sopenharmony_ci * cy_wait_until_sent() --- wait until the transmitter is empty 16028c2ecf20Sopenharmony_ci */ 16038c2ecf20Sopenharmony_cistatic void cy_wait_until_sent(struct tty_struct *tty, int timeout) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci struct cyclades_card *card; 16068c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 16078c2ecf20Sopenharmony_ci unsigned long orig_jiffies; 16088c2ecf20Sopenharmony_ci int char_time; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_wait_until_sent")) 16118c2ecf20Sopenharmony_ci return; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (info->xmit_fifo_size == 0) 16148c2ecf20Sopenharmony_ci return; /* Just in case.... */ 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci orig_jiffies = jiffies; 16178c2ecf20Sopenharmony_ci /* 16188c2ecf20Sopenharmony_ci * Set the check interval to be 1/5 of the estimated time to 16198c2ecf20Sopenharmony_ci * send a single character, and make it at least 1. The check 16208c2ecf20Sopenharmony_ci * interval should also be less than the timeout. 16218c2ecf20Sopenharmony_ci * 16228c2ecf20Sopenharmony_ci * Note: we have to use pretty tight timings here to satisfy 16238c2ecf20Sopenharmony_ci * the NIST-PCTS. 16248c2ecf20Sopenharmony_ci */ 16258c2ecf20Sopenharmony_ci char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; 16268c2ecf20Sopenharmony_ci char_time = char_time / 5; 16278c2ecf20Sopenharmony_ci if (char_time <= 0) 16288c2ecf20Sopenharmony_ci char_time = 1; 16298c2ecf20Sopenharmony_ci if (timeout < 0) 16308c2ecf20Sopenharmony_ci timeout = 0; 16318c2ecf20Sopenharmony_ci if (timeout) 16328c2ecf20Sopenharmony_ci char_time = min(char_time, timeout); 16338c2ecf20Sopenharmony_ci /* 16348c2ecf20Sopenharmony_ci * If the transmitter hasn't cleared in twice the approximate 16358c2ecf20Sopenharmony_ci * amount of time to send the entire FIFO, it probably won't 16368c2ecf20Sopenharmony_ci * ever clear. This assumes the UART isn't doing flow 16378c2ecf20Sopenharmony_ci * control, which is currently the case. Hence, if it ever 16388c2ecf20Sopenharmony_ci * takes longer than info->timeout, this is probably due to a 16398c2ecf20Sopenharmony_ci * UART bug of some kind. So, we clamp the timeout parameter at 16408c2ecf20Sopenharmony_ci * 2*info->timeout. 16418c2ecf20Sopenharmony_ci */ 16428c2ecf20Sopenharmony_ci if (!timeout || timeout > 2 * info->timeout) 16438c2ecf20Sopenharmony_ci timeout = 2 * info->timeout; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci card = info->card; 16468c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 16478c2ecf20Sopenharmony_ci while (cyy_readb(info, CySRER) & CyTxRdy) { 16488c2ecf20Sopenharmony_ci if (msleep_interruptible(jiffies_to_msecs(char_time))) 16498c2ecf20Sopenharmony_ci break; 16508c2ecf20Sopenharmony_ci if (timeout && time_after(jiffies, orig_jiffies + 16518c2ecf20Sopenharmony_ci timeout)) 16528c2ecf20Sopenharmony_ci break; 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_ci } 16558c2ecf20Sopenharmony_ci /* Run one more char cycle */ 16568c2ecf20Sopenharmony_ci msleep_interruptible(jiffies_to_msecs(char_time * 5)); 16578c2ecf20Sopenharmony_ci} 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cistatic void cy_flush_buffer(struct tty_struct *tty) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 16628c2ecf20Sopenharmony_ci struct cyclades_card *card; 16638c2ecf20Sopenharmony_ci int channel, retval; 16648c2ecf20Sopenharmony_ci unsigned long flags; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO 16678c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line); 16688c2ecf20Sopenharmony_ci#endif 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) 16718c2ecf20Sopenharmony_ci return; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci card = info->card; 16748c2ecf20Sopenharmony_ci channel = info->line - card->first_line; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 16778c2ecf20Sopenharmony_ci info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; 16788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci if (cy_is_Z(card)) { /* If it is a Z card, flush the on-board 16818c2ecf20Sopenharmony_ci buffers as well */ 16828c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 16838c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L); 16848c2ecf20Sopenharmony_ci if (retval != 0) { 16858c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d " 16868c2ecf20Sopenharmony_ci "was %x\n", info->line, retval); 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci tty_wakeup(tty); 16918c2ecf20Sopenharmony_ci} /* cy_flush_buffer */ 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic void cy_do_close(struct tty_port *port) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci struct cyclades_port *info = container_of(port, struct cyclades_port, 16978c2ecf20Sopenharmony_ci port); 16988c2ecf20Sopenharmony_ci struct cyclades_card *card; 16998c2ecf20Sopenharmony_ci unsigned long flags; 17008c2ecf20Sopenharmony_ci int channel; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci card = info->card; 17038c2ecf20Sopenharmony_ci channel = info->line - card->first_line; 17048c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 17078c2ecf20Sopenharmony_ci /* Stop accepting input */ 17088c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel & 0x03); 17098c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData); 17108c2ecf20Sopenharmony_ci if (tty_port_initialized(&info->port)) { 17118c2ecf20Sopenharmony_ci /* Waiting for on-board buffers to be empty before 17128c2ecf20Sopenharmony_ci closing the port */ 17138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 17148c2ecf20Sopenharmony_ci cy_wait_until_sent(port->tty, info->timeout); 17158c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 17168c2ecf20Sopenharmony_ci } 17178c2ecf20Sopenharmony_ci } else { 17188c2ecf20Sopenharmony_ci#ifdef Z_WAKE 17198c2ecf20Sopenharmony_ci /* Waiting for on-board buffers to be empty before closing 17208c2ecf20Sopenharmony_ci the port */ 17218c2ecf20Sopenharmony_ci struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; 17228c2ecf20Sopenharmony_ci int retval; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) { 17258c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L); 17268c2ecf20Sopenharmony_ci if (retval != 0) { 17278c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_close retval on " 17288c2ecf20Sopenharmony_ci "ttyC%d was %x\n", info->line, retval); 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 17318c2ecf20Sopenharmony_ci wait_for_completion_interruptible(&info->shutdown_wait); 17328c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 17338c2ecf20Sopenharmony_ci } 17348c2ecf20Sopenharmony_ci#endif 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 17378c2ecf20Sopenharmony_ci cy_shutdown(info, port->tty); 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci/* 17418c2ecf20Sopenharmony_ci * This routine is called when a particular tty device is closed. 17428c2ecf20Sopenharmony_ci */ 17438c2ecf20Sopenharmony_cistatic void cy_close(struct tty_struct *tty, struct file *filp) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 17468c2ecf20Sopenharmony_ci if (!info || serial_paranoia_check(info, tty->name, "cy_close")) 17478c2ecf20Sopenharmony_ci return; 17488c2ecf20Sopenharmony_ci tty_port_close(&info->port, tty, filp); 17498c2ecf20Sopenharmony_ci} /* cy_close */ 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci/* This routine gets called when tty_write has put something into 17528c2ecf20Sopenharmony_ci * the write_queue. The characters may come from user space or 17538c2ecf20Sopenharmony_ci * kernel space. 17548c2ecf20Sopenharmony_ci * 17558c2ecf20Sopenharmony_ci * This routine will return the number of characters actually 17568c2ecf20Sopenharmony_ci * accepted for writing. 17578c2ecf20Sopenharmony_ci * 17588c2ecf20Sopenharmony_ci * If the port is not already transmitting stuff, start it off by 17598c2ecf20Sopenharmony_ci * enabling interrupts. The interrupt service routine will then 17608c2ecf20Sopenharmony_ci * ensure that the characters are sent. 17618c2ecf20Sopenharmony_ci * If the port is already active, there is no need to kick it. 17628c2ecf20Sopenharmony_ci * 17638c2ecf20Sopenharmony_ci */ 17648c2ecf20Sopenharmony_cistatic int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) 17658c2ecf20Sopenharmony_ci{ 17668c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 17678c2ecf20Sopenharmony_ci unsigned long flags; 17688c2ecf20Sopenharmony_ci int c, ret = 0; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO 17718c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_write ttyC%d\n", info->line); 17728c2ecf20Sopenharmony_ci#endif 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_write")) 17758c2ecf20Sopenharmony_ci return 0; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (!info->port.xmit_buf) 17788c2ecf20Sopenharmony_ci return 0; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->card->card_lock, flags); 17818c2ecf20Sopenharmony_ci while (1) { 17828c2ecf20Sopenharmony_ci c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1)); 17838c2ecf20Sopenharmony_ci c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head)); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci if (c <= 0) 17868c2ecf20Sopenharmony_ci break; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci memcpy(info->port.xmit_buf + info->xmit_head, buf, c); 17898c2ecf20Sopenharmony_ci info->xmit_head = (info->xmit_head + c) & 17908c2ecf20Sopenharmony_ci (SERIAL_XMIT_SIZE - 1); 17918c2ecf20Sopenharmony_ci info->xmit_cnt += c; 17928c2ecf20Sopenharmony_ci buf += c; 17938c2ecf20Sopenharmony_ci count -= c; 17948c2ecf20Sopenharmony_ci ret += c; 17958c2ecf20Sopenharmony_ci } 17968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->card->card_lock, flags); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci info->idle_stats.xmit_bytes += ret; 17998c2ecf20Sopenharmony_ci info->idle_stats.xmit_idle = jiffies; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) 18028c2ecf20Sopenharmony_ci start_xmit(info); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci return ret; 18058c2ecf20Sopenharmony_ci} /* cy_write */ 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci/* 18088c2ecf20Sopenharmony_ci * This routine is called by the kernel to write a single 18098c2ecf20Sopenharmony_ci * character to the tty device. If the kernel uses this routine, 18108c2ecf20Sopenharmony_ci * it must call the flush_chars() routine (if defined) when it is 18118c2ecf20Sopenharmony_ci * done stuffing characters into the driver. If there is no room 18128c2ecf20Sopenharmony_ci * in the queue, the character is ignored. 18138c2ecf20Sopenharmony_ci */ 18148c2ecf20Sopenharmony_cistatic int cy_put_char(struct tty_struct *tty, unsigned char ch) 18158c2ecf20Sopenharmony_ci{ 18168c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 18178c2ecf20Sopenharmony_ci unsigned long flags; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO 18208c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_put_char ttyC%d\n", info->line); 18218c2ecf20Sopenharmony_ci#endif 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_put_char")) 18248c2ecf20Sopenharmony_ci return 0; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci if (!info->port.xmit_buf) 18278c2ecf20Sopenharmony_ci return 0; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->card->card_lock, flags); 18308c2ecf20Sopenharmony_ci if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) { 18318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->card->card_lock, flags); 18328c2ecf20Sopenharmony_ci return 0; 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci info->port.xmit_buf[info->xmit_head++] = ch; 18368c2ecf20Sopenharmony_ci info->xmit_head &= SERIAL_XMIT_SIZE - 1; 18378c2ecf20Sopenharmony_ci info->xmit_cnt++; 18388c2ecf20Sopenharmony_ci info->idle_stats.xmit_bytes++; 18398c2ecf20Sopenharmony_ci info->idle_stats.xmit_idle = jiffies; 18408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->card->card_lock, flags); 18418c2ecf20Sopenharmony_ci return 1; 18428c2ecf20Sopenharmony_ci} /* cy_put_char */ 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci/* 18458c2ecf20Sopenharmony_ci * This routine is called by the kernel after it has written a 18468c2ecf20Sopenharmony_ci * series of characters to the tty device using put_char(). 18478c2ecf20Sopenharmony_ci */ 18488c2ecf20Sopenharmony_cistatic void cy_flush_chars(struct tty_struct *tty) 18498c2ecf20Sopenharmony_ci{ 18508c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO 18538c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_flush_chars ttyC%d\n", info->line); 18548c2ecf20Sopenharmony_ci#endif 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_flush_chars")) 18578c2ecf20Sopenharmony_ci return; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || 18608c2ecf20Sopenharmony_ci !info->port.xmit_buf) 18618c2ecf20Sopenharmony_ci return; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci start_xmit(info); 18648c2ecf20Sopenharmony_ci} /* cy_flush_chars */ 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci/* 18678c2ecf20Sopenharmony_ci * This routine returns the numbers of characters the tty driver 18688c2ecf20Sopenharmony_ci * will accept for queuing to be written. This number is subject 18698c2ecf20Sopenharmony_ci * to change as output buffers get emptied, or if the output flow 18708c2ecf20Sopenharmony_ci * control is activated. 18718c2ecf20Sopenharmony_ci */ 18728c2ecf20Sopenharmony_cistatic int cy_write_room(struct tty_struct *tty) 18738c2ecf20Sopenharmony_ci{ 18748c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 18758c2ecf20Sopenharmony_ci int ret; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO 18788c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_write_room ttyC%d\n", info->line); 18798c2ecf20Sopenharmony_ci#endif 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_write_room")) 18828c2ecf20Sopenharmony_ci return 0; 18838c2ecf20Sopenharmony_ci ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; 18848c2ecf20Sopenharmony_ci if (ret < 0) 18858c2ecf20Sopenharmony_ci ret = 0; 18868c2ecf20Sopenharmony_ci return ret; 18878c2ecf20Sopenharmony_ci} /* cy_write_room */ 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_cistatic int cy_chars_in_buffer(struct tty_struct *tty) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) 18948c2ecf20Sopenharmony_ci return 0; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci#ifdef Z_EXT_CHARS_IN_BUFFER 18978c2ecf20Sopenharmony_ci if (!cy_is_Z(info->card)) { 18988c2ecf20Sopenharmony_ci#endif /* Z_EXT_CHARS_IN_BUFFER */ 18998c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO 19008c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", 19018c2ecf20Sopenharmony_ci info->line, info->xmit_cnt); 19028c2ecf20Sopenharmony_ci#endif 19038c2ecf20Sopenharmony_ci return info->xmit_cnt; 19048c2ecf20Sopenharmony_ci#ifdef Z_EXT_CHARS_IN_BUFFER 19058c2ecf20Sopenharmony_ci } else { 19068c2ecf20Sopenharmony_ci struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; 19078c2ecf20Sopenharmony_ci int char_count; 19088c2ecf20Sopenharmony_ci __u32 tx_put, tx_get, tx_bufsize; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci tx_get = readl(&buf_ctrl->tx_get); 19118c2ecf20Sopenharmony_ci tx_put = readl(&buf_ctrl->tx_put); 19128c2ecf20Sopenharmony_ci tx_bufsize = readl(&buf_ctrl->tx_bufsize); 19138c2ecf20Sopenharmony_ci if (tx_put >= tx_get) 19148c2ecf20Sopenharmony_ci char_count = tx_put - tx_get; 19158c2ecf20Sopenharmony_ci else 19168c2ecf20Sopenharmony_ci char_count = tx_put - tx_get + tx_bufsize; 19178c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO 19188c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", 19198c2ecf20Sopenharmony_ci info->line, info->xmit_cnt + char_count); 19208c2ecf20Sopenharmony_ci#endif 19218c2ecf20Sopenharmony_ci return info->xmit_cnt + char_count; 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci#endif /* Z_EXT_CHARS_IN_BUFFER */ 19248c2ecf20Sopenharmony_ci} /* cy_chars_in_buffer */ 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci/* 19278c2ecf20Sopenharmony_ci * ------------------------------------------------------------ 19288c2ecf20Sopenharmony_ci * cy_ioctl() and friends 19298c2ecf20Sopenharmony_ci * ------------------------------------------------------------ 19308c2ecf20Sopenharmony_ci */ 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_cistatic void cyy_baud_calc(struct cyclades_port *info, __u32 baud) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci int co, co_val, bpr; 19358c2ecf20Sopenharmony_ci __u32 cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 : 19368c2ecf20Sopenharmony_ci 25000000); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci if (baud == 0) { 19398c2ecf20Sopenharmony_ci info->tbpr = info->tco = info->rbpr = info->rco = 0; 19408c2ecf20Sopenharmony_ci return; 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci /* determine which prescaler to use */ 19448c2ecf20Sopenharmony_ci for (co = 4, co_val = 2048; co; co--, co_val >>= 2) { 19458c2ecf20Sopenharmony_ci if (cy_clock / co_val / baud > 63) 19468c2ecf20Sopenharmony_ci break; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci bpr = (cy_clock / co_val * 2 / baud + 1) / 2; 19508c2ecf20Sopenharmony_ci if (bpr > 255) 19518c2ecf20Sopenharmony_ci bpr = 255; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci info->tbpr = info->rbpr = bpr; 19548c2ecf20Sopenharmony_ci info->tco = info->rco = co; 19558c2ecf20Sopenharmony_ci} 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci/* 19588c2ecf20Sopenharmony_ci * This routine finds or computes the various line characteristics. 19598c2ecf20Sopenharmony_ci * It used to be called config_setup 19608c2ecf20Sopenharmony_ci */ 19618c2ecf20Sopenharmony_cistatic void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) 19628c2ecf20Sopenharmony_ci{ 19638c2ecf20Sopenharmony_ci struct cyclades_card *card; 19648c2ecf20Sopenharmony_ci unsigned long flags; 19658c2ecf20Sopenharmony_ci int channel; 19668c2ecf20Sopenharmony_ci unsigned cflag, iflag; 19678c2ecf20Sopenharmony_ci int baud, baud_rate = 0; 19688c2ecf20Sopenharmony_ci int i; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci if (info->line == -1) 19718c2ecf20Sopenharmony_ci return; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci cflag = tty->termios.c_cflag; 19748c2ecf20Sopenharmony_ci iflag = tty->termios.c_iflag; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci card = info->card; 19778c2ecf20Sopenharmony_ci channel = info->line - card->first_line; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 19808c2ecf20Sopenharmony_ci u32 cflags; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci /* baud rate */ 19838c2ecf20Sopenharmony_ci baud = tty_get_baud_rate(tty); 19848c2ecf20Sopenharmony_ci if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == 19858c2ecf20Sopenharmony_ci ASYNC_SPD_CUST) { 19868c2ecf20Sopenharmony_ci if (info->custom_divisor) 19878c2ecf20Sopenharmony_ci baud_rate = info->baud / info->custom_divisor; 19888c2ecf20Sopenharmony_ci else 19898c2ecf20Sopenharmony_ci baud_rate = info->baud; 19908c2ecf20Sopenharmony_ci } else if (baud > CD1400_MAX_SPEED) { 19918c2ecf20Sopenharmony_ci baud = CD1400_MAX_SPEED; 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci /* find the baud index */ 19948c2ecf20Sopenharmony_ci for (i = 0; i < 20; i++) { 19958c2ecf20Sopenharmony_ci if (baud == baud_table[i]) 19968c2ecf20Sopenharmony_ci break; 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci if (i == 20) 19998c2ecf20Sopenharmony_ci i = 19; /* CD1400_MAX_SPEED */ 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == 20028c2ecf20Sopenharmony_ci ASYNC_SPD_CUST) { 20038c2ecf20Sopenharmony_ci cyy_baud_calc(info, baud_rate); 20048c2ecf20Sopenharmony_ci } else { 20058c2ecf20Sopenharmony_ci if (info->chip_rev >= CD1400_REV_J) { 20068c2ecf20Sopenharmony_ci /* It is a CD1400 rev. J or later */ 20078c2ecf20Sopenharmony_ci info->tbpr = baud_bpr_60[i]; /* Tx BPR */ 20088c2ecf20Sopenharmony_ci info->tco = baud_co_60[i]; /* Tx CO */ 20098c2ecf20Sopenharmony_ci info->rbpr = baud_bpr_60[i]; /* Rx BPR */ 20108c2ecf20Sopenharmony_ci info->rco = baud_co_60[i]; /* Rx CO */ 20118c2ecf20Sopenharmony_ci } else { 20128c2ecf20Sopenharmony_ci info->tbpr = baud_bpr_25[i]; /* Tx BPR */ 20138c2ecf20Sopenharmony_ci info->tco = baud_co_25[i]; /* Tx CO */ 20148c2ecf20Sopenharmony_ci info->rbpr = baud_bpr_25[i]; /* Rx BPR */ 20158c2ecf20Sopenharmony_ci info->rco = baud_co_25[i]; /* Rx CO */ 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci } 20188c2ecf20Sopenharmony_ci if (baud_table[i] == 134) { 20198c2ecf20Sopenharmony_ci /* get it right for 134.5 baud */ 20208c2ecf20Sopenharmony_ci info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 20218c2ecf20Sopenharmony_ci 2; 20228c2ecf20Sopenharmony_ci } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == 20238c2ecf20Sopenharmony_ci ASYNC_SPD_CUST) { 20248c2ecf20Sopenharmony_ci info->timeout = (info->xmit_fifo_size * HZ * 15 / 20258c2ecf20Sopenharmony_ci baud_rate) + 2; 20268c2ecf20Sopenharmony_ci } else if (baud_table[i]) { 20278c2ecf20Sopenharmony_ci info->timeout = (info->xmit_fifo_size * HZ * 15 / 20288c2ecf20Sopenharmony_ci baud_table[i]) + 2; 20298c2ecf20Sopenharmony_ci /* this needs to be propagated into the card info */ 20308c2ecf20Sopenharmony_ci } else { 20318c2ecf20Sopenharmony_ci info->timeout = 0; 20328c2ecf20Sopenharmony_ci } 20338c2ecf20Sopenharmony_ci /* By tradition (is it a standard?) a baud rate of zero 20348c2ecf20Sopenharmony_ci implies the line should be/has been closed. A bit 20358c2ecf20Sopenharmony_ci later in this routine such a test is performed. */ 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci /* byte size and parity */ 20388c2ecf20Sopenharmony_ci info->cor5 = 0; 20398c2ecf20Sopenharmony_ci info->cor4 = 0; 20408c2ecf20Sopenharmony_ci /* receive threshold */ 20418c2ecf20Sopenharmony_ci info->cor3 = (info->default_threshold ? 20428c2ecf20Sopenharmony_ci info->default_threshold : baud_cor3[i]); 20438c2ecf20Sopenharmony_ci info->cor2 = CyETC; 20448c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 20458c2ecf20Sopenharmony_ci case CS5: 20468c2ecf20Sopenharmony_ci info->cor1 = Cy_5_BITS; 20478c2ecf20Sopenharmony_ci break; 20488c2ecf20Sopenharmony_ci case CS6: 20498c2ecf20Sopenharmony_ci info->cor1 = Cy_6_BITS; 20508c2ecf20Sopenharmony_ci break; 20518c2ecf20Sopenharmony_ci case CS7: 20528c2ecf20Sopenharmony_ci info->cor1 = Cy_7_BITS; 20538c2ecf20Sopenharmony_ci break; 20548c2ecf20Sopenharmony_ci case CS8: 20558c2ecf20Sopenharmony_ci info->cor1 = Cy_8_BITS; 20568c2ecf20Sopenharmony_ci break; 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci if (cflag & CSTOPB) 20598c2ecf20Sopenharmony_ci info->cor1 |= Cy_2_STOP; 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci if (cflag & PARENB) { 20628c2ecf20Sopenharmony_ci if (cflag & PARODD) 20638c2ecf20Sopenharmony_ci info->cor1 |= CyPARITY_O; 20648c2ecf20Sopenharmony_ci else 20658c2ecf20Sopenharmony_ci info->cor1 |= CyPARITY_E; 20668c2ecf20Sopenharmony_ci } else 20678c2ecf20Sopenharmony_ci info->cor1 |= CyPARITY_NONE; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci /* CTS flow control flag */ 20708c2ecf20Sopenharmony_ci tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); 20718c2ecf20Sopenharmony_ci if (cflag & CRTSCTS) 20728c2ecf20Sopenharmony_ci info->cor2 |= CyCtsAE; 20738c2ecf20Sopenharmony_ci else 20748c2ecf20Sopenharmony_ci info->cor2 &= ~CyCtsAE; 20758c2ecf20Sopenharmony_ci tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci /*********************************************** 20788c2ecf20Sopenharmony_ci The hardware option, CyRtsAO, presents RTS when 20798c2ecf20Sopenharmony_ci the chip has characters to send. Since most modems 20808c2ecf20Sopenharmony_ci use RTS as reverse (inbound) flow control, this 20818c2ecf20Sopenharmony_ci option is not used. If inbound flow control is 20828c2ecf20Sopenharmony_ci necessary, DTR can be programmed to provide the 20838c2ecf20Sopenharmony_ci appropriate signals for use with a non-standard 20848c2ecf20Sopenharmony_ci cable. Contact Marcio Saito for details. 20858c2ecf20Sopenharmony_ci ***********************************************/ 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci channel &= 0x03; 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 20908c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci /* tx and rx baud rate */ 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci cyy_writeb(info, CyTCOR, info->tco); 20958c2ecf20Sopenharmony_ci cyy_writeb(info, CyTBPR, info->tbpr); 20968c2ecf20Sopenharmony_ci cyy_writeb(info, CyRCOR, info->rco); 20978c2ecf20Sopenharmony_ci cyy_writeb(info, CyRBPR, info->rbpr); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci /* set line characteristics according configuration */ 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci cyy_writeb(info, CySCHR1, START_CHAR(tty)); 21028c2ecf20Sopenharmony_ci cyy_writeb(info, CySCHR2, STOP_CHAR(tty)); 21038c2ecf20Sopenharmony_ci cyy_writeb(info, CyCOR1, info->cor1); 21048c2ecf20Sopenharmony_ci cyy_writeb(info, CyCOR2, info->cor2); 21058c2ecf20Sopenharmony_ci cyy_writeb(info, CyCOR3, info->cor3); 21068c2ecf20Sopenharmony_ci cyy_writeb(info, CyCOR4, info->cor4); 21078c2ecf20Sopenharmony_ci cyy_writeb(info, CyCOR5, info->cor5); 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | 21108c2ecf20Sopenharmony_ci CyCOR3ch); 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci /* !!! Is this needed? */ 21138c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel); 21148c2ecf20Sopenharmony_ci cyy_writeb(info, CyRTPR, 21158c2ecf20Sopenharmony_ci (info->default_timeout ? info->default_timeout : 0x02)); 21168c2ecf20Sopenharmony_ci /* 10ms rx timeout */ 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci cflags = CyCTS; 21198c2ecf20Sopenharmony_ci if (!C_CLOCAL(tty)) 21208c2ecf20Sopenharmony_ci cflags |= CyDSR | CyRI | CyDCD; 21218c2ecf20Sopenharmony_ci /* without modem intr */ 21228c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh); 21238c2ecf20Sopenharmony_ci /* act on 1->0 modem transitions */ 21248c2ecf20Sopenharmony_ci if ((cflag & CRTSCTS) && info->rflow) 21258c2ecf20Sopenharmony_ci cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]); 21268c2ecf20Sopenharmony_ci else 21278c2ecf20Sopenharmony_ci cyy_writeb(info, CyMCOR1, cflags); 21288c2ecf20Sopenharmony_ci /* act on 0->1 modem transitions */ 21298c2ecf20Sopenharmony_ci cyy_writeb(info, CyMCOR2, cflags); 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci if (i == 0) /* baud rate is zero, turn off line */ 21328c2ecf20Sopenharmony_ci cyy_change_rts_dtr(info, 0, TIOCM_DTR); 21338c2ecf20Sopenharmony_ci else 21348c2ecf20Sopenharmony_ci cyy_change_rts_dtr(info, TIOCM_DTR, 0); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci clear_bit(TTY_IO_ERROR, &tty->flags); 21378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci } else { 21408c2ecf20Sopenharmony_ci struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; 21418c2ecf20Sopenharmony_ci __u32 sw_flow; 21428c2ecf20Sopenharmony_ci int retval; 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci if (!cyz_is_loaded(card)) 21458c2ecf20Sopenharmony_ci return; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci /* baud rate */ 21488c2ecf20Sopenharmony_ci baud = tty_get_baud_rate(tty); 21498c2ecf20Sopenharmony_ci if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == 21508c2ecf20Sopenharmony_ci ASYNC_SPD_CUST) { 21518c2ecf20Sopenharmony_ci if (info->custom_divisor) 21528c2ecf20Sopenharmony_ci baud_rate = info->baud / info->custom_divisor; 21538c2ecf20Sopenharmony_ci else 21548c2ecf20Sopenharmony_ci baud_rate = info->baud; 21558c2ecf20Sopenharmony_ci } else if (baud > CYZ_MAX_SPEED) { 21568c2ecf20Sopenharmony_ci baud = CYZ_MAX_SPEED; 21578c2ecf20Sopenharmony_ci } 21588c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_baud, baud); 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci if (baud == 134) { 21618c2ecf20Sopenharmony_ci /* get it right for 134.5 baud */ 21628c2ecf20Sopenharmony_ci info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 21638c2ecf20Sopenharmony_ci 2; 21648c2ecf20Sopenharmony_ci } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == 21658c2ecf20Sopenharmony_ci ASYNC_SPD_CUST) { 21668c2ecf20Sopenharmony_ci info->timeout = (info->xmit_fifo_size * HZ * 15 / 21678c2ecf20Sopenharmony_ci baud_rate) + 2; 21688c2ecf20Sopenharmony_ci } else if (baud) { 21698c2ecf20Sopenharmony_ci info->timeout = (info->xmit_fifo_size * HZ * 15 / 21708c2ecf20Sopenharmony_ci baud) + 2; 21718c2ecf20Sopenharmony_ci /* this needs to be propagated into the card info */ 21728c2ecf20Sopenharmony_ci } else { 21738c2ecf20Sopenharmony_ci info->timeout = 0; 21748c2ecf20Sopenharmony_ci } 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci /* byte size and parity */ 21778c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 21788c2ecf20Sopenharmony_ci case CS5: 21798c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_data_l, C_DL_CS5); 21808c2ecf20Sopenharmony_ci break; 21818c2ecf20Sopenharmony_ci case CS6: 21828c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_data_l, C_DL_CS6); 21838c2ecf20Sopenharmony_ci break; 21848c2ecf20Sopenharmony_ci case CS7: 21858c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_data_l, C_DL_CS7); 21868c2ecf20Sopenharmony_ci break; 21878c2ecf20Sopenharmony_ci case CS8: 21888c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_data_l, C_DL_CS8); 21898c2ecf20Sopenharmony_ci break; 21908c2ecf20Sopenharmony_ci } 21918c2ecf20Sopenharmony_ci if (cflag & CSTOPB) { 21928c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_data_l, 21938c2ecf20Sopenharmony_ci readl(&ch_ctrl->comm_data_l) | C_DL_2STOP); 21948c2ecf20Sopenharmony_ci } else { 21958c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_data_l, 21968c2ecf20Sopenharmony_ci readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci if (cflag & PARENB) { 21998c2ecf20Sopenharmony_ci if (cflag & PARODD) 22008c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_parity, C_PR_ODD); 22018c2ecf20Sopenharmony_ci else 22028c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN); 22038c2ecf20Sopenharmony_ci } else 22048c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->comm_parity, C_PR_NONE); 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci /* CTS flow control flag */ 22078c2ecf20Sopenharmony_ci if (cflag & CRTSCTS) { 22088c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->hw_flow, 22098c2ecf20Sopenharmony_ci readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); 22108c2ecf20Sopenharmony_ci } else { 22118c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->hw_flow, readl(&ch_ctrl->hw_flow) & 22128c2ecf20Sopenharmony_ci ~(C_RS_CTS | C_RS_RTS)); 22138c2ecf20Sopenharmony_ci } 22148c2ecf20Sopenharmony_ci /* As the HW flow control is done in firmware, the driver 22158c2ecf20Sopenharmony_ci doesn't need to care about it */ 22168c2ecf20Sopenharmony_ci tty_port_set_cts_flow(&info->port, 0); 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci /* XON/XOFF/XANY flow control flags */ 22198c2ecf20Sopenharmony_ci sw_flow = 0; 22208c2ecf20Sopenharmony_ci if (iflag & IXON) { 22218c2ecf20Sopenharmony_ci sw_flow |= C_FL_OXX; 22228c2ecf20Sopenharmony_ci if (iflag & IXANY) 22238c2ecf20Sopenharmony_ci sw_flow |= C_FL_OIXANY; 22248c2ecf20Sopenharmony_ci } 22258c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->sw_flow, sw_flow); 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); 22288c2ecf20Sopenharmony_ci if (retval != 0) { 22298c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:set_line_char retval on ttyC%d " 22308c2ecf20Sopenharmony_ci "was %x\n", info->line, retval); 22318c2ecf20Sopenharmony_ci } 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci /* CD sensitivity */ 22348c2ecf20Sopenharmony_ci tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci if (baud == 0) { /* baud rate is zero, turn off line */ 22378c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->rs_control, 22388c2ecf20Sopenharmony_ci readl(&ch_ctrl->rs_control) & ~C_RS_DTR); 22398c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR 22408c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:set_line_char dropping Z DTR\n"); 22418c2ecf20Sopenharmony_ci#endif 22428c2ecf20Sopenharmony_ci } else { 22438c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->rs_control, 22448c2ecf20Sopenharmony_ci readl(&ch_ctrl->rs_control) | C_RS_DTR); 22458c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR 22468c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:set_line_char raising Z DTR\n"); 22478c2ecf20Sopenharmony_ci#endif 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); 22518c2ecf20Sopenharmony_ci if (retval != 0) { 22528c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d " 22538c2ecf20Sopenharmony_ci "was %x\n", info->line, retval); 22548c2ecf20Sopenharmony_ci } 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci clear_bit(TTY_IO_ERROR, &tty->flags); 22578c2ecf20Sopenharmony_ci } 22588c2ecf20Sopenharmony_ci} /* set_line_char */ 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_cistatic int cy_get_serial_info(struct tty_struct *tty, 22618c2ecf20Sopenharmony_ci struct serial_struct *ss) 22628c2ecf20Sopenharmony_ci{ 22638c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 22648c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = info->card; 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_ioctl")) 22678c2ecf20Sopenharmony_ci return -ENODEV; 22688c2ecf20Sopenharmony_ci ss->type = info->type; 22698c2ecf20Sopenharmony_ci ss->line = info->line; 22708c2ecf20Sopenharmony_ci ss->port = (info->card - cy_card) * 0x100 + info->line - 22718c2ecf20Sopenharmony_ci cinfo->first_line; 22728c2ecf20Sopenharmony_ci ss->irq = cinfo->irq; 22738c2ecf20Sopenharmony_ci ss->flags = info->port.flags; 22748c2ecf20Sopenharmony_ci ss->close_delay = info->port.close_delay; 22758c2ecf20Sopenharmony_ci ss->closing_wait = info->port.closing_wait; 22768c2ecf20Sopenharmony_ci ss->baud_base = info->baud; 22778c2ecf20Sopenharmony_ci ss->custom_divisor = info->custom_divisor; 22788c2ecf20Sopenharmony_ci return 0; 22798c2ecf20Sopenharmony_ci} 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_cistatic int cy_set_serial_info(struct tty_struct *tty, 22828c2ecf20Sopenharmony_ci struct serial_struct *ss) 22838c2ecf20Sopenharmony_ci{ 22848c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 22858c2ecf20Sopenharmony_ci int old_flags; 22868c2ecf20Sopenharmony_ci int ret; 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_ioctl")) 22898c2ecf20Sopenharmony_ci return -ENODEV; 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci mutex_lock(&info->port.mutex); 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci old_flags = info->port.flags; 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 22968c2ecf20Sopenharmony_ci if (ss->close_delay != info->port.close_delay || 22978c2ecf20Sopenharmony_ci ss->baud_base != info->baud || 22988c2ecf20Sopenharmony_ci (ss->flags & ASYNC_FLAGS & 22998c2ecf20Sopenharmony_ci ~ASYNC_USR_MASK) != 23008c2ecf20Sopenharmony_ci (info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) 23018c2ecf20Sopenharmony_ci { 23028c2ecf20Sopenharmony_ci mutex_unlock(&info->port.mutex); 23038c2ecf20Sopenharmony_ci return -EPERM; 23048c2ecf20Sopenharmony_ci } 23058c2ecf20Sopenharmony_ci info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) | 23068c2ecf20Sopenharmony_ci (ss->flags & ASYNC_USR_MASK); 23078c2ecf20Sopenharmony_ci info->baud = ss->baud_base; 23088c2ecf20Sopenharmony_ci info->custom_divisor = ss->custom_divisor; 23098c2ecf20Sopenharmony_ci goto check_and_exit; 23108c2ecf20Sopenharmony_ci } 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci /* 23138c2ecf20Sopenharmony_ci * OK, past this point, all the error checking has been done. 23148c2ecf20Sopenharmony_ci * At this point, we start making changes..... 23158c2ecf20Sopenharmony_ci */ 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci info->baud = ss->baud_base; 23188c2ecf20Sopenharmony_ci info->custom_divisor = ss->custom_divisor; 23198c2ecf20Sopenharmony_ci info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | 23208c2ecf20Sopenharmony_ci (ss->flags & ASYNC_FLAGS); 23218c2ecf20Sopenharmony_ci info->port.close_delay = ss->close_delay * HZ / 100; 23228c2ecf20Sopenharmony_ci info->port.closing_wait = ss->closing_wait * HZ / 100; 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_cicheck_and_exit: 23258c2ecf20Sopenharmony_ci if (tty_port_initialized(&info->port)) { 23268c2ecf20Sopenharmony_ci if ((ss->flags ^ old_flags) & ASYNC_SPD_MASK) { 23278c2ecf20Sopenharmony_ci /* warn about deprecation unless clearing */ 23288c2ecf20Sopenharmony_ci if (ss->flags & ASYNC_SPD_MASK) 23298c2ecf20Sopenharmony_ci dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n"); 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci cy_set_line_char(info, tty); 23328c2ecf20Sopenharmony_ci ret = 0; 23338c2ecf20Sopenharmony_ci } else { 23348c2ecf20Sopenharmony_ci ret = cy_startup(info, tty); 23358c2ecf20Sopenharmony_ci } 23368c2ecf20Sopenharmony_ci mutex_unlock(&info->port.mutex); 23378c2ecf20Sopenharmony_ci return ret; 23388c2ecf20Sopenharmony_ci} /* set_serial_info */ 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci/* 23418c2ecf20Sopenharmony_ci * get_lsr_info - get line status register info 23428c2ecf20Sopenharmony_ci * 23438c2ecf20Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically 23448c2ecf20Sopenharmony_ci * is emptied. On bus types like RS485, the transmitter must 23458c2ecf20Sopenharmony_ci * release the bus after transmitting. This must be done when 23468c2ecf20Sopenharmony_ci * the transmit shift register is empty, not be done when the 23478c2ecf20Sopenharmony_ci * transmit holding register is empty. This functionality 23488c2ecf20Sopenharmony_ci * allows an RS485 driver to be written in user space. 23498c2ecf20Sopenharmony_ci */ 23508c2ecf20Sopenharmony_cistatic int get_lsr_info(struct cyclades_port *info, unsigned int __user *value) 23518c2ecf20Sopenharmony_ci{ 23528c2ecf20Sopenharmony_ci struct cyclades_card *card = info->card; 23538c2ecf20Sopenharmony_ci unsigned int result; 23548c2ecf20Sopenharmony_ci unsigned long flags; 23558c2ecf20Sopenharmony_ci u8 status; 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 23588c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 23598c2ecf20Sopenharmony_ci status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty); 23608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 23618c2ecf20Sopenharmony_ci result = (status ? 0 : TIOCSER_TEMT); 23628c2ecf20Sopenharmony_ci } else { 23638c2ecf20Sopenharmony_ci /* Not supported yet */ 23648c2ecf20Sopenharmony_ci return -EINVAL; 23658c2ecf20Sopenharmony_ci } 23668c2ecf20Sopenharmony_ci return put_user(result, value); 23678c2ecf20Sopenharmony_ci} 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_cistatic int cy_tiocmget(struct tty_struct *tty) 23708c2ecf20Sopenharmony_ci{ 23718c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 23728c2ecf20Sopenharmony_ci struct cyclades_card *card; 23738c2ecf20Sopenharmony_ci int result; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, __func__)) 23768c2ecf20Sopenharmony_ci return -ENODEV; 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci card = info->card; 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 23818c2ecf20Sopenharmony_ci unsigned long flags; 23828c2ecf20Sopenharmony_ci int channel = info->line - card->first_line; 23838c2ecf20Sopenharmony_ci u8 status; 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 23868c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel & 0x03); 23878c2ecf20Sopenharmony_ci status = cyy_readb(info, CyMSVR1); 23888c2ecf20Sopenharmony_ci status |= cyy_readb(info, CyMSVR2); 23898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci if (info->rtsdtr_inv) { 23928c2ecf20Sopenharmony_ci result = ((status & CyRTS) ? TIOCM_DTR : 0) | 23938c2ecf20Sopenharmony_ci ((status & CyDTR) ? TIOCM_RTS : 0); 23948c2ecf20Sopenharmony_ci } else { 23958c2ecf20Sopenharmony_ci result = ((status & CyRTS) ? TIOCM_RTS : 0) | 23968c2ecf20Sopenharmony_ci ((status & CyDTR) ? TIOCM_DTR : 0); 23978c2ecf20Sopenharmony_ci } 23988c2ecf20Sopenharmony_ci result |= ((status & CyDCD) ? TIOCM_CAR : 0) | 23998c2ecf20Sopenharmony_ci ((status & CyRI) ? TIOCM_RNG : 0) | 24008c2ecf20Sopenharmony_ci ((status & CyDSR) ? TIOCM_DSR : 0) | 24018c2ecf20Sopenharmony_ci ((status & CyCTS) ? TIOCM_CTS : 0); 24028c2ecf20Sopenharmony_ci } else { 24038c2ecf20Sopenharmony_ci u32 lstatus; 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci if (!cyz_is_loaded(card)) { 24068c2ecf20Sopenharmony_ci result = -ENODEV; 24078c2ecf20Sopenharmony_ci goto end; 24088c2ecf20Sopenharmony_ci } 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); 24118c2ecf20Sopenharmony_ci result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | 24128c2ecf20Sopenharmony_ci ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | 24138c2ecf20Sopenharmony_ci ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | 24148c2ecf20Sopenharmony_ci ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | 24158c2ecf20Sopenharmony_ci ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | 24168c2ecf20Sopenharmony_ci ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); 24178c2ecf20Sopenharmony_ci } 24188c2ecf20Sopenharmony_ciend: 24198c2ecf20Sopenharmony_ci return result; 24208c2ecf20Sopenharmony_ci} /* cy_tiomget */ 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_cistatic int 24238c2ecf20Sopenharmony_cicy_tiocmset(struct tty_struct *tty, 24248c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear) 24258c2ecf20Sopenharmony_ci{ 24268c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 24278c2ecf20Sopenharmony_ci struct cyclades_card *card; 24288c2ecf20Sopenharmony_ci unsigned long flags; 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, __func__)) 24318c2ecf20Sopenharmony_ci return -ENODEV; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci card = info->card; 24348c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 24358c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 24368c2ecf20Sopenharmony_ci cyy_change_rts_dtr(info, set, clear); 24378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 24388c2ecf20Sopenharmony_ci } else { 24398c2ecf20Sopenharmony_ci struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; 24408c2ecf20Sopenharmony_ci int retval, channel = info->line - card->first_line; 24418c2ecf20Sopenharmony_ci u32 rs; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci if (!cyz_is_loaded(card)) 24448c2ecf20Sopenharmony_ci return -ENODEV; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 24478c2ecf20Sopenharmony_ci rs = readl(&ch_ctrl->rs_control); 24488c2ecf20Sopenharmony_ci if (set & TIOCM_RTS) 24498c2ecf20Sopenharmony_ci rs |= C_RS_RTS; 24508c2ecf20Sopenharmony_ci if (clear & TIOCM_RTS) 24518c2ecf20Sopenharmony_ci rs &= ~C_RS_RTS; 24528c2ecf20Sopenharmony_ci if (set & TIOCM_DTR) { 24538c2ecf20Sopenharmony_ci rs |= C_RS_DTR; 24548c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR 24558c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n"); 24568c2ecf20Sopenharmony_ci#endif 24578c2ecf20Sopenharmony_ci } 24588c2ecf20Sopenharmony_ci if (clear & TIOCM_DTR) { 24598c2ecf20Sopenharmony_ci rs &= ~C_RS_DTR; 24608c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR 24618c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:set_modem_info clearing " 24628c2ecf20Sopenharmony_ci "Z DTR\n"); 24638c2ecf20Sopenharmony_ci#endif 24648c2ecf20Sopenharmony_ci } 24658c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->rs_control, rs); 24668c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); 24678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 24688c2ecf20Sopenharmony_ci if (retval != 0) { 24698c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d " 24708c2ecf20Sopenharmony_ci "was %x\n", info->line, retval); 24718c2ecf20Sopenharmony_ci } 24728c2ecf20Sopenharmony_ci } 24738c2ecf20Sopenharmony_ci return 0; 24748c2ecf20Sopenharmony_ci} 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci/* 24778c2ecf20Sopenharmony_ci * cy_break() --- routine which turns the break handling on or off 24788c2ecf20Sopenharmony_ci */ 24798c2ecf20Sopenharmony_cistatic int cy_break(struct tty_struct *tty, int break_state) 24808c2ecf20Sopenharmony_ci{ 24818c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 24828c2ecf20Sopenharmony_ci struct cyclades_card *card; 24838c2ecf20Sopenharmony_ci unsigned long flags; 24848c2ecf20Sopenharmony_ci int retval = 0; 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_break")) 24878c2ecf20Sopenharmony_ci return -EINVAL; 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci card = info->card; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 24928c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 24938c2ecf20Sopenharmony_ci /* Let the transmit ISR take care of this (since it 24948c2ecf20Sopenharmony_ci requires stuffing characters into the output stream). 24958c2ecf20Sopenharmony_ci */ 24968c2ecf20Sopenharmony_ci if (break_state == -1) { 24978c2ecf20Sopenharmony_ci if (!info->breakon) { 24988c2ecf20Sopenharmony_ci info->breakon = 1; 24998c2ecf20Sopenharmony_ci if (!info->xmit_cnt) { 25008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 25018c2ecf20Sopenharmony_ci start_xmit(info); 25028c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ci } 25058c2ecf20Sopenharmony_ci } else { 25068c2ecf20Sopenharmony_ci if (!info->breakoff) { 25078c2ecf20Sopenharmony_ci info->breakoff = 1; 25088c2ecf20Sopenharmony_ci if (!info->xmit_cnt) { 25098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 25108c2ecf20Sopenharmony_ci start_xmit(info); 25118c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 25128c2ecf20Sopenharmony_ci } 25138c2ecf20Sopenharmony_ci } 25148c2ecf20Sopenharmony_ci } 25158c2ecf20Sopenharmony_ci } else { 25168c2ecf20Sopenharmony_ci if (break_state == -1) { 25178c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, 25188c2ecf20Sopenharmony_ci info->line - card->first_line, 25198c2ecf20Sopenharmony_ci C_CM_SET_BREAK, 0L); 25208c2ecf20Sopenharmony_ci if (retval != 0) { 25218c2ecf20Sopenharmony_ci printk(KERN_ERR "cyc:cy_break (set) retval on " 25228c2ecf20Sopenharmony_ci "ttyC%d was %x\n", info->line, retval); 25238c2ecf20Sopenharmony_ci } 25248c2ecf20Sopenharmony_ci } else { 25258c2ecf20Sopenharmony_ci retval = cyz_issue_cmd(card, 25268c2ecf20Sopenharmony_ci info->line - card->first_line, 25278c2ecf20Sopenharmony_ci C_CM_CLR_BREAK, 0L); 25288c2ecf20Sopenharmony_ci if (retval != 0) { 25298c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_break (clr) retval " 25308c2ecf20Sopenharmony_ci "on ttyC%d was %x\n", info->line, 25318c2ecf20Sopenharmony_ci retval); 25328c2ecf20Sopenharmony_ci } 25338c2ecf20Sopenharmony_ci } 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 25368c2ecf20Sopenharmony_ci return retval; 25378c2ecf20Sopenharmony_ci} /* cy_break */ 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_cistatic int set_threshold(struct cyclades_port *info, unsigned long value) 25408c2ecf20Sopenharmony_ci{ 25418c2ecf20Sopenharmony_ci struct cyclades_card *card = info->card; 25428c2ecf20Sopenharmony_ci unsigned long flags; 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 25458c2ecf20Sopenharmony_ci info->cor3 &= ~CyREC_FIFO; 25468c2ecf20Sopenharmony_ci info->cor3 |= value & CyREC_FIFO; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 25498c2ecf20Sopenharmony_ci cyy_writeb(info, CyCOR3, info->cor3); 25508c2ecf20Sopenharmony_ci cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch); 25518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 25528c2ecf20Sopenharmony_ci } 25538c2ecf20Sopenharmony_ci return 0; 25548c2ecf20Sopenharmony_ci} /* set_threshold */ 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_cistatic int get_threshold(struct cyclades_port *info, 25578c2ecf20Sopenharmony_ci unsigned long __user *value) 25588c2ecf20Sopenharmony_ci{ 25598c2ecf20Sopenharmony_ci struct cyclades_card *card = info->card; 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 25628c2ecf20Sopenharmony_ci u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO; 25638c2ecf20Sopenharmony_ci return put_user(tmp, value); 25648c2ecf20Sopenharmony_ci } 25658c2ecf20Sopenharmony_ci return 0; 25668c2ecf20Sopenharmony_ci} /* get_threshold */ 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_cistatic int set_timeout(struct cyclades_port *info, unsigned long value) 25698c2ecf20Sopenharmony_ci{ 25708c2ecf20Sopenharmony_ci struct cyclades_card *card = info->card; 25718c2ecf20Sopenharmony_ci unsigned long flags; 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 25748c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 25758c2ecf20Sopenharmony_ci cyy_writeb(info, CyRTPR, value & 0xff); 25768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 25778c2ecf20Sopenharmony_ci } 25788c2ecf20Sopenharmony_ci return 0; 25798c2ecf20Sopenharmony_ci} /* set_timeout */ 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_cistatic int get_timeout(struct cyclades_port *info, 25828c2ecf20Sopenharmony_ci unsigned long __user *value) 25838c2ecf20Sopenharmony_ci{ 25848c2ecf20Sopenharmony_ci struct cyclades_card *card = info->card; 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 25878c2ecf20Sopenharmony_ci u8 tmp = cyy_readb(info, CyRTPR); 25888c2ecf20Sopenharmony_ci return put_user(tmp, value); 25898c2ecf20Sopenharmony_ci } 25908c2ecf20Sopenharmony_ci return 0; 25918c2ecf20Sopenharmony_ci} /* get_timeout */ 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_cistatic int cy_cflags_changed(struct cyclades_port *info, unsigned long arg, 25948c2ecf20Sopenharmony_ci struct cyclades_icount *cprev) 25958c2ecf20Sopenharmony_ci{ 25968c2ecf20Sopenharmony_ci struct cyclades_icount cnow; 25978c2ecf20Sopenharmony_ci unsigned long flags; 25988c2ecf20Sopenharmony_ci int ret; 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->card->card_lock, flags); 26018c2ecf20Sopenharmony_ci cnow = info->icount; /* atomic copy */ 26028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->card->card_lock, flags); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || 26058c2ecf20Sopenharmony_ci ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || 26068c2ecf20Sopenharmony_ci ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || 26078c2ecf20Sopenharmony_ci ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci *cprev = cnow; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci return ret; 26128c2ecf20Sopenharmony_ci} 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci/* 26158c2ecf20Sopenharmony_ci * This routine allows the tty driver to implement device- 26168c2ecf20Sopenharmony_ci * specific ioctl's. If the ioctl number passed in cmd is 26178c2ecf20Sopenharmony_ci * not recognized by the driver, it should return ENOIOCTLCMD. 26188c2ecf20Sopenharmony_ci */ 26198c2ecf20Sopenharmony_cistatic int 26208c2ecf20Sopenharmony_cicy_ioctl(struct tty_struct *tty, 26218c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 26228c2ecf20Sopenharmony_ci{ 26238c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 26248c2ecf20Sopenharmony_ci struct cyclades_icount cnow; /* kernel counter temps */ 26258c2ecf20Sopenharmony_ci int ret_val = 0; 26268c2ecf20Sopenharmony_ci unsigned long flags; 26278c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_ioctl")) 26308c2ecf20Sopenharmony_ci return -ENODEV; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER 26338c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", 26348c2ecf20Sopenharmony_ci info->line, cmd, arg); 26358c2ecf20Sopenharmony_ci#endif 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci switch (cmd) { 26388c2ecf20Sopenharmony_ci case CYGETMON: 26398c2ecf20Sopenharmony_ci if (copy_to_user(argp, &info->mon, sizeof(info->mon))) { 26408c2ecf20Sopenharmony_ci ret_val = -EFAULT; 26418c2ecf20Sopenharmony_ci break; 26428c2ecf20Sopenharmony_ci } 26438c2ecf20Sopenharmony_ci memset(&info->mon, 0, sizeof(info->mon)); 26448c2ecf20Sopenharmony_ci break; 26458c2ecf20Sopenharmony_ci case CYGETTHRESH: 26468c2ecf20Sopenharmony_ci ret_val = get_threshold(info, argp); 26478c2ecf20Sopenharmony_ci break; 26488c2ecf20Sopenharmony_ci case CYSETTHRESH: 26498c2ecf20Sopenharmony_ci ret_val = set_threshold(info, arg); 26508c2ecf20Sopenharmony_ci break; 26518c2ecf20Sopenharmony_ci case CYGETDEFTHRESH: 26528c2ecf20Sopenharmony_ci ret_val = put_user(info->default_threshold, 26538c2ecf20Sopenharmony_ci (unsigned long __user *)argp); 26548c2ecf20Sopenharmony_ci break; 26558c2ecf20Sopenharmony_ci case CYSETDEFTHRESH: 26568c2ecf20Sopenharmony_ci info->default_threshold = arg & 0x0f; 26578c2ecf20Sopenharmony_ci break; 26588c2ecf20Sopenharmony_ci case CYGETTIMEOUT: 26598c2ecf20Sopenharmony_ci ret_val = get_timeout(info, argp); 26608c2ecf20Sopenharmony_ci break; 26618c2ecf20Sopenharmony_ci case CYSETTIMEOUT: 26628c2ecf20Sopenharmony_ci ret_val = set_timeout(info, arg); 26638c2ecf20Sopenharmony_ci break; 26648c2ecf20Sopenharmony_ci case CYGETDEFTIMEOUT: 26658c2ecf20Sopenharmony_ci ret_val = put_user(info->default_timeout, 26668c2ecf20Sopenharmony_ci (unsigned long __user *)argp); 26678c2ecf20Sopenharmony_ci break; 26688c2ecf20Sopenharmony_ci case CYSETDEFTIMEOUT: 26698c2ecf20Sopenharmony_ci info->default_timeout = arg & 0xff; 26708c2ecf20Sopenharmony_ci break; 26718c2ecf20Sopenharmony_ci case CYSETRFLOW: 26728c2ecf20Sopenharmony_ci info->rflow = (int)arg; 26738c2ecf20Sopenharmony_ci break; 26748c2ecf20Sopenharmony_ci case CYGETRFLOW: 26758c2ecf20Sopenharmony_ci ret_val = info->rflow; 26768c2ecf20Sopenharmony_ci break; 26778c2ecf20Sopenharmony_ci case CYSETRTSDTR_INV: 26788c2ecf20Sopenharmony_ci info->rtsdtr_inv = (int)arg; 26798c2ecf20Sopenharmony_ci break; 26808c2ecf20Sopenharmony_ci case CYGETRTSDTR_INV: 26818c2ecf20Sopenharmony_ci ret_val = info->rtsdtr_inv; 26828c2ecf20Sopenharmony_ci break; 26838c2ecf20Sopenharmony_ci case CYGETCD1400VER: 26848c2ecf20Sopenharmony_ci ret_val = info->chip_rev; 26858c2ecf20Sopenharmony_ci break; 26868c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR 26878c2ecf20Sopenharmony_ci case CYZSETPOLLCYCLE: 26888c2ecf20Sopenharmony_ci if (arg > LONG_MAX / HZ) 26898c2ecf20Sopenharmony_ci return -ENODEV; 26908c2ecf20Sopenharmony_ci cyz_polling_cycle = (arg * HZ) / 1000; 26918c2ecf20Sopenharmony_ci break; 26928c2ecf20Sopenharmony_ci case CYZGETPOLLCYCLE: 26938c2ecf20Sopenharmony_ci ret_val = (cyz_polling_cycle * 1000) / HZ; 26948c2ecf20Sopenharmony_ci break; 26958c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 26968c2ecf20Sopenharmony_ci case CYSETWAIT: 26978c2ecf20Sopenharmony_ci info->port.closing_wait = (unsigned short)arg * HZ / 100; 26988c2ecf20Sopenharmony_ci break; 26998c2ecf20Sopenharmony_ci case CYGETWAIT: 27008c2ecf20Sopenharmony_ci ret_val = info->port.closing_wait / (HZ / 100); 27018c2ecf20Sopenharmony_ci break; 27028c2ecf20Sopenharmony_ci case TIOCSERGETLSR: /* Get line status register */ 27038c2ecf20Sopenharmony_ci ret_val = get_lsr_info(info, argp); 27048c2ecf20Sopenharmony_ci break; 27058c2ecf20Sopenharmony_ci /* 27068c2ecf20Sopenharmony_ci * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change 27078c2ecf20Sopenharmony_ci * - mask passed in arg for lines of interest 27088c2ecf20Sopenharmony_ci * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) 27098c2ecf20Sopenharmony_ci * Caller should use TIOCGICOUNT to see which one it was 27108c2ecf20Sopenharmony_ci */ 27118c2ecf20Sopenharmony_ci case TIOCMIWAIT: 27128c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->card->card_lock, flags); 27138c2ecf20Sopenharmony_ci /* note the counters on entry */ 27148c2ecf20Sopenharmony_ci cnow = info->icount; 27158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->card->card_lock, flags); 27168c2ecf20Sopenharmony_ci ret_val = wait_event_interruptible(info->port.delta_msr_wait, 27178c2ecf20Sopenharmony_ci cy_cflags_changed(info, arg, &cnow)); 27188c2ecf20Sopenharmony_ci break; 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci /* 27218c2ecf20Sopenharmony_ci * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) 27228c2ecf20Sopenharmony_ci * Return: write counters to the user passed counter struct 27238c2ecf20Sopenharmony_ci * NB: both 1->0 and 0->1 transitions are counted except for 27248c2ecf20Sopenharmony_ci * RI where only 0->1 is counted. 27258c2ecf20Sopenharmony_ci */ 27268c2ecf20Sopenharmony_ci default: 27278c2ecf20Sopenharmony_ci ret_val = -ENOIOCTLCMD; 27288c2ecf20Sopenharmony_ci } 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER 27318c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_ioctl done\n"); 27328c2ecf20Sopenharmony_ci#endif 27338c2ecf20Sopenharmony_ci return ret_val; 27348c2ecf20Sopenharmony_ci} /* cy_ioctl */ 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_cistatic int cy_get_icount(struct tty_struct *tty, 27378c2ecf20Sopenharmony_ci struct serial_icounter_struct *sic) 27388c2ecf20Sopenharmony_ci{ 27398c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 27408c2ecf20Sopenharmony_ci struct cyclades_icount cnow; /* Used to snapshot */ 27418c2ecf20Sopenharmony_ci unsigned long flags; 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->card->card_lock, flags); 27448c2ecf20Sopenharmony_ci cnow = info->icount; 27458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->card->card_lock, flags); 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_ci sic->cts = cnow.cts; 27488c2ecf20Sopenharmony_ci sic->dsr = cnow.dsr; 27498c2ecf20Sopenharmony_ci sic->rng = cnow.rng; 27508c2ecf20Sopenharmony_ci sic->dcd = cnow.dcd; 27518c2ecf20Sopenharmony_ci sic->rx = cnow.rx; 27528c2ecf20Sopenharmony_ci sic->tx = cnow.tx; 27538c2ecf20Sopenharmony_ci sic->frame = cnow.frame; 27548c2ecf20Sopenharmony_ci sic->overrun = cnow.overrun; 27558c2ecf20Sopenharmony_ci sic->parity = cnow.parity; 27568c2ecf20Sopenharmony_ci sic->brk = cnow.brk; 27578c2ecf20Sopenharmony_ci sic->buf_overrun = cnow.buf_overrun; 27588c2ecf20Sopenharmony_ci return 0; 27598c2ecf20Sopenharmony_ci} 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci/* 27628c2ecf20Sopenharmony_ci * This routine allows the tty driver to be notified when 27638c2ecf20Sopenharmony_ci * device's termios settings have changed. Note that a 27648c2ecf20Sopenharmony_ci * well-designed tty driver should be prepared to accept the case 27658c2ecf20Sopenharmony_ci * where old == NULL, and try to do something rational. 27668c2ecf20Sopenharmony_ci */ 27678c2ecf20Sopenharmony_cistatic void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) 27688c2ecf20Sopenharmony_ci{ 27698c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER 27728c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line); 27738c2ecf20Sopenharmony_ci#endif 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci cy_set_line_char(info, tty); 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { 27788c2ecf20Sopenharmony_ci tty->hw_stopped = 0; 27798c2ecf20Sopenharmony_ci cy_start(tty); 27808c2ecf20Sopenharmony_ci } 27818c2ecf20Sopenharmony_ci#if 0 27828c2ecf20Sopenharmony_ci /* 27838c2ecf20Sopenharmony_ci * No need to wake up processes in open wait, since they 27848c2ecf20Sopenharmony_ci * sample the CLOCAL flag once, and don't recheck it. 27858c2ecf20Sopenharmony_ci * XXX It's not clear whether the current behavior is correct 27868c2ecf20Sopenharmony_ci * or not. Hence, this may change..... 27878c2ecf20Sopenharmony_ci */ 27888c2ecf20Sopenharmony_ci if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty)) 27898c2ecf20Sopenharmony_ci wake_up_interruptible(&info->port.open_wait); 27908c2ecf20Sopenharmony_ci#endif 27918c2ecf20Sopenharmony_ci} /* cy_set_termios */ 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci/* This function is used to send a high-priority XON/XOFF character to 27948c2ecf20Sopenharmony_ci the device. 27958c2ecf20Sopenharmony_ci*/ 27968c2ecf20Sopenharmony_cistatic void cy_send_xchar(struct tty_struct *tty, char ch) 27978c2ecf20Sopenharmony_ci{ 27988c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 27998c2ecf20Sopenharmony_ci struct cyclades_card *card; 28008c2ecf20Sopenharmony_ci int channel; 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_send_xchar")) 28038c2ecf20Sopenharmony_ci return; 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci info->x_char = ch; 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci if (ch) 28088c2ecf20Sopenharmony_ci cy_start(tty); 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci card = info->card; 28118c2ecf20Sopenharmony_ci channel = info->line - card->first_line; 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci if (cy_is_Z(card)) { 28148c2ecf20Sopenharmony_ci if (ch == STOP_CHAR(tty)) 28158c2ecf20Sopenharmony_ci cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L); 28168c2ecf20Sopenharmony_ci else if (ch == START_CHAR(tty)) 28178c2ecf20Sopenharmony_ci cyz_issue_cmd(card, channel, C_CM_SENDXON, 0L); 28188c2ecf20Sopenharmony_ci } 28198c2ecf20Sopenharmony_ci} 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci/* This routine is called by the upper-layer tty layer to signal 28228c2ecf20Sopenharmony_ci that incoming characters should be throttled because the input 28238c2ecf20Sopenharmony_ci buffers are close to full. 28248c2ecf20Sopenharmony_ci */ 28258c2ecf20Sopenharmony_cistatic void cy_throttle(struct tty_struct *tty) 28268c2ecf20Sopenharmony_ci{ 28278c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 28288c2ecf20Sopenharmony_ci struct cyclades_card *card; 28298c2ecf20Sopenharmony_ci unsigned long flags; 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_THROTTLE 28328c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:throttle %s ...ttyC%d\n", tty_name(tty), 28338c2ecf20Sopenharmony_ci info->line); 28348c2ecf20Sopenharmony_ci#endif 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_throttle")) 28378c2ecf20Sopenharmony_ci return; 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci card = info->card; 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) { 28428c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) 28438c2ecf20Sopenharmony_ci cy_send_xchar(tty, STOP_CHAR(tty)); 28448c2ecf20Sopenharmony_ci else 28458c2ecf20Sopenharmony_ci info->throttle = 1; 28468c2ecf20Sopenharmony_ci } 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) { 28498c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 28508c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 28518c2ecf20Sopenharmony_ci cyy_change_rts_dtr(info, 0, TIOCM_RTS); 28528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 28538c2ecf20Sopenharmony_ci } else { 28548c2ecf20Sopenharmony_ci info->throttle = 1; 28558c2ecf20Sopenharmony_ci } 28568c2ecf20Sopenharmony_ci } 28578c2ecf20Sopenharmony_ci} /* cy_throttle */ 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci/* 28608c2ecf20Sopenharmony_ci * This routine notifies the tty driver that it should signal 28618c2ecf20Sopenharmony_ci * that characters can now be sent to the tty without fear of 28628c2ecf20Sopenharmony_ci * overrunning the input buffers of the line disciplines. 28638c2ecf20Sopenharmony_ci */ 28648c2ecf20Sopenharmony_cistatic void cy_unthrottle(struct tty_struct *tty) 28658c2ecf20Sopenharmony_ci{ 28668c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 28678c2ecf20Sopenharmony_ci struct cyclades_card *card; 28688c2ecf20Sopenharmony_ci unsigned long flags; 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_THROTTLE 28718c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:unthrottle %s ...ttyC%d\n", 28728c2ecf20Sopenharmony_ci tty_name(tty), info->line); 28738c2ecf20Sopenharmony_ci#endif 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) 28768c2ecf20Sopenharmony_ci return; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) { 28798c2ecf20Sopenharmony_ci if (info->x_char) 28808c2ecf20Sopenharmony_ci info->x_char = 0; 28818c2ecf20Sopenharmony_ci else 28828c2ecf20Sopenharmony_ci cy_send_xchar(tty, START_CHAR(tty)); 28838c2ecf20Sopenharmony_ci } 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) { 28868c2ecf20Sopenharmony_ci card = info->card; 28878c2ecf20Sopenharmony_ci if (!cy_is_Z(card)) { 28888c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->card_lock, flags); 28898c2ecf20Sopenharmony_ci cyy_change_rts_dtr(info, TIOCM_RTS, 0); 28908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->card_lock, flags); 28918c2ecf20Sopenharmony_ci } else { 28928c2ecf20Sopenharmony_ci info->throttle = 0; 28938c2ecf20Sopenharmony_ci } 28948c2ecf20Sopenharmony_ci } 28958c2ecf20Sopenharmony_ci} /* cy_unthrottle */ 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci/* cy_start and cy_stop provide software output flow control as a 28988c2ecf20Sopenharmony_ci function of XON/XOFF, software CTS, and other such stuff. 28998c2ecf20Sopenharmony_ci*/ 29008c2ecf20Sopenharmony_cistatic void cy_stop(struct tty_struct *tty) 29018c2ecf20Sopenharmony_ci{ 29028c2ecf20Sopenharmony_ci struct cyclades_card *cinfo; 29038c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 29048c2ecf20Sopenharmony_ci int channel; 29058c2ecf20Sopenharmony_ci unsigned long flags; 29068c2ecf20Sopenharmony_ci 29078c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER 29088c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_stop ttyC%d\n", info->line); 29098c2ecf20Sopenharmony_ci#endif 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_stop")) 29128c2ecf20Sopenharmony_ci return; 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci cinfo = info->card; 29158c2ecf20Sopenharmony_ci channel = info->line - cinfo->first_line; 29168c2ecf20Sopenharmony_ci if (!cy_is_Z(cinfo)) { 29178c2ecf20Sopenharmony_ci spin_lock_irqsave(&cinfo->card_lock, flags); 29188c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel & 0x03); 29198c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); 29208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cinfo->card_lock, flags); 29218c2ecf20Sopenharmony_ci } 29228c2ecf20Sopenharmony_ci} /* cy_stop */ 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_cistatic void cy_start(struct tty_struct *tty) 29258c2ecf20Sopenharmony_ci{ 29268c2ecf20Sopenharmony_ci struct cyclades_card *cinfo; 29278c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 29288c2ecf20Sopenharmony_ci int channel; 29298c2ecf20Sopenharmony_ci unsigned long flags; 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER 29328c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_start ttyC%d\n", info->line); 29338c2ecf20Sopenharmony_ci#endif 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_start")) 29368c2ecf20Sopenharmony_ci return; 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci cinfo = info->card; 29398c2ecf20Sopenharmony_ci channel = info->line - cinfo->first_line; 29408c2ecf20Sopenharmony_ci if (!cy_is_Z(cinfo)) { 29418c2ecf20Sopenharmony_ci spin_lock_irqsave(&cinfo->card_lock, flags); 29428c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel & 0x03); 29438c2ecf20Sopenharmony_ci cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); 29448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cinfo->card_lock, flags); 29458c2ecf20Sopenharmony_ci } 29468c2ecf20Sopenharmony_ci} /* cy_start */ 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci/* 29498c2ecf20Sopenharmony_ci * cy_hangup() --- called by tty_hangup() when a hangup is signaled. 29508c2ecf20Sopenharmony_ci */ 29518c2ecf20Sopenharmony_cistatic void cy_hangup(struct tty_struct *tty) 29528c2ecf20Sopenharmony_ci{ 29538c2ecf20Sopenharmony_ci struct cyclades_port *info = tty->driver_data; 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER 29568c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cyc:cy_hangup ttyC%d\n", info->line); 29578c2ecf20Sopenharmony_ci#endif 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci if (serial_paranoia_check(info, tty->name, "cy_hangup")) 29608c2ecf20Sopenharmony_ci return; 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_ci cy_flush_buffer(tty); 29638c2ecf20Sopenharmony_ci cy_shutdown(info, tty); 29648c2ecf20Sopenharmony_ci tty_port_hangup(&info->port); 29658c2ecf20Sopenharmony_ci} /* cy_hangup */ 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_cistatic int cyy_carrier_raised(struct tty_port *port) 29688c2ecf20Sopenharmony_ci{ 29698c2ecf20Sopenharmony_ci struct cyclades_port *info = container_of(port, struct cyclades_port, 29708c2ecf20Sopenharmony_ci port); 29718c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = info->card; 29728c2ecf20Sopenharmony_ci unsigned long flags; 29738c2ecf20Sopenharmony_ci int channel = info->line - cinfo->first_line; 29748c2ecf20Sopenharmony_ci u32 cd; 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci spin_lock_irqsave(&cinfo->card_lock, flags); 29778c2ecf20Sopenharmony_ci cyy_writeb(info, CyCAR, channel & 0x03); 29788c2ecf20Sopenharmony_ci cd = cyy_readb(info, CyMSVR1) & CyDCD; 29798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cinfo->card_lock, flags); 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci return cd; 29828c2ecf20Sopenharmony_ci} 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_cistatic void cyy_dtr_rts(struct tty_port *port, int raise) 29858c2ecf20Sopenharmony_ci{ 29868c2ecf20Sopenharmony_ci struct cyclades_port *info = container_of(port, struct cyclades_port, 29878c2ecf20Sopenharmony_ci port); 29888c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = info->card; 29898c2ecf20Sopenharmony_ci unsigned long flags; 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci spin_lock_irqsave(&cinfo->card_lock, flags); 29928c2ecf20Sopenharmony_ci cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0, 29938c2ecf20Sopenharmony_ci raise ? 0 : TIOCM_RTS | TIOCM_DTR); 29948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cinfo->card_lock, flags); 29958c2ecf20Sopenharmony_ci} 29968c2ecf20Sopenharmony_ci 29978c2ecf20Sopenharmony_cistatic int cyz_carrier_raised(struct tty_port *port) 29988c2ecf20Sopenharmony_ci{ 29998c2ecf20Sopenharmony_ci struct cyclades_port *info = container_of(port, struct cyclades_port, 30008c2ecf20Sopenharmony_ci port); 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD; 30038c2ecf20Sopenharmony_ci} 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_cistatic void cyz_dtr_rts(struct tty_port *port, int raise) 30068c2ecf20Sopenharmony_ci{ 30078c2ecf20Sopenharmony_ci struct cyclades_port *info = container_of(port, struct cyclades_port, 30088c2ecf20Sopenharmony_ci port); 30098c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = info->card; 30108c2ecf20Sopenharmony_ci struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; 30118c2ecf20Sopenharmony_ci int ret, channel = info->line - cinfo->first_line; 30128c2ecf20Sopenharmony_ci u32 rs; 30138c2ecf20Sopenharmony_ci 30148c2ecf20Sopenharmony_ci rs = readl(&ch_ctrl->rs_control); 30158c2ecf20Sopenharmony_ci if (raise) 30168c2ecf20Sopenharmony_ci rs |= C_RS_RTS | C_RS_DTR; 30178c2ecf20Sopenharmony_ci else 30188c2ecf20Sopenharmony_ci rs &= ~(C_RS_RTS | C_RS_DTR); 30198c2ecf20Sopenharmony_ci cy_writel(&ch_ctrl->rs_control, rs); 30208c2ecf20Sopenharmony_ci ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); 30218c2ecf20Sopenharmony_ci if (ret != 0) 30228c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: retval on ttyC%d was %x\n", 30238c2ecf20Sopenharmony_ci __func__, info->line, ret); 30248c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR 30258c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: raising Z DTR\n", __func__); 30268c2ecf20Sopenharmony_ci#endif 30278c2ecf20Sopenharmony_ci} 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_cistatic const struct tty_port_operations cyy_port_ops = { 30308c2ecf20Sopenharmony_ci .carrier_raised = cyy_carrier_raised, 30318c2ecf20Sopenharmony_ci .dtr_rts = cyy_dtr_rts, 30328c2ecf20Sopenharmony_ci .shutdown = cy_do_close, 30338c2ecf20Sopenharmony_ci}; 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_cistatic const struct tty_port_operations cyz_port_ops = { 30368c2ecf20Sopenharmony_ci .carrier_raised = cyz_carrier_raised, 30378c2ecf20Sopenharmony_ci .dtr_rts = cyz_dtr_rts, 30388c2ecf20Sopenharmony_ci .shutdown = cy_do_close, 30398c2ecf20Sopenharmony_ci}; 30408c2ecf20Sopenharmony_ci 30418c2ecf20Sopenharmony_ci/* 30428c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- 30438c2ecf20Sopenharmony_ci * cy_init() and friends 30448c2ecf20Sopenharmony_ci * 30458c2ecf20Sopenharmony_ci * cy_init() is called at boot-time to initialize the serial driver. 30468c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- 30478c2ecf20Sopenharmony_ci */ 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_cistatic int cy_init_card(struct cyclades_card *cinfo) 30508c2ecf20Sopenharmony_ci{ 30518c2ecf20Sopenharmony_ci struct cyclades_port *info; 30528c2ecf20Sopenharmony_ci unsigned int channel, port; 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci spin_lock_init(&cinfo->card_lock); 30558c2ecf20Sopenharmony_ci cinfo->intr_enabled = 0; 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports), 30588c2ecf20Sopenharmony_ci GFP_KERNEL); 30598c2ecf20Sopenharmony_ci if (cinfo->ports == NULL) { 30608c2ecf20Sopenharmony_ci printk(KERN_ERR "Cyclades: cannot allocate ports\n"); 30618c2ecf20Sopenharmony_ci return -ENOMEM; 30628c2ecf20Sopenharmony_ci } 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci for (channel = 0, port = cinfo->first_line; channel < cinfo->nports; 30658c2ecf20Sopenharmony_ci channel++, port++) { 30668c2ecf20Sopenharmony_ci info = &cinfo->ports[channel]; 30678c2ecf20Sopenharmony_ci tty_port_init(&info->port); 30688c2ecf20Sopenharmony_ci info->magic = CYCLADES_MAGIC; 30698c2ecf20Sopenharmony_ci info->card = cinfo; 30708c2ecf20Sopenharmony_ci info->line = port; 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_ci info->port.closing_wait = CLOSING_WAIT_DELAY; 30738c2ecf20Sopenharmony_ci info->port.close_delay = 5 * HZ / 10; 30748c2ecf20Sopenharmony_ci init_completion(&info->shutdown_wait); 30758c2ecf20Sopenharmony_ci 30768c2ecf20Sopenharmony_ci if (cy_is_Z(cinfo)) { 30778c2ecf20Sopenharmony_ci struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS; 30788c2ecf20Sopenharmony_ci struct ZFW_CTRL *zfw_ctrl; 30798c2ecf20Sopenharmony_ci 30808c2ecf20Sopenharmony_ci info->port.ops = &cyz_port_ops; 30818c2ecf20Sopenharmony_ci info->type = PORT_STARTECH; 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci zfw_ctrl = cinfo->base_addr + 30848c2ecf20Sopenharmony_ci (readl(&firm_id->zfwctrl_addr) & 0xfffff); 30858c2ecf20Sopenharmony_ci info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; 30868c2ecf20Sopenharmony_ci info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; 30878c2ecf20Sopenharmony_ci 30888c2ecf20Sopenharmony_ci if (cinfo->hw_ver == ZO_V1) 30898c2ecf20Sopenharmony_ci info->xmit_fifo_size = CYZ_FIFO_SIZE; 30908c2ecf20Sopenharmony_ci else 30918c2ecf20Sopenharmony_ci info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; 30928c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 30938c2ecf20Sopenharmony_ci timer_setup(&info->rx_full_timer, cyz_rx_restart, 0); 30948c2ecf20Sopenharmony_ci#endif 30958c2ecf20Sopenharmony_ci } else { 30968c2ecf20Sopenharmony_ci unsigned short chip_number; 30978c2ecf20Sopenharmony_ci int index = cinfo->bus_index; 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci info->port.ops = &cyy_port_ops; 31008c2ecf20Sopenharmony_ci info->type = PORT_CIRRUS; 31018c2ecf20Sopenharmony_ci info->xmit_fifo_size = CyMAX_CHAR_FIFO; 31028c2ecf20Sopenharmony_ci info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS; 31038c2ecf20Sopenharmony_ci info->cor2 = CyETC; 31048c2ecf20Sopenharmony_ci info->cor3 = 0x08; /* _very_ small rcv threshold */ 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci chip_number = channel / CyPORTS_PER_CHIP; 31078c2ecf20Sopenharmony_ci info->u.cyy.base_addr = cinfo->base_addr + 31088c2ecf20Sopenharmony_ci (cy_chip_offset[chip_number] << index); 31098c2ecf20Sopenharmony_ci info->chip_rev = cyy_readb(info, CyGFRCR); 31108c2ecf20Sopenharmony_ci 31118c2ecf20Sopenharmony_ci if (info->chip_rev >= CD1400_REV_J) { 31128c2ecf20Sopenharmony_ci /* It is a CD1400 rev. J or later */ 31138c2ecf20Sopenharmony_ci info->tbpr = baud_bpr_60[13]; /* Tx BPR */ 31148c2ecf20Sopenharmony_ci info->tco = baud_co_60[13]; /* Tx CO */ 31158c2ecf20Sopenharmony_ci info->rbpr = baud_bpr_60[13]; /* Rx BPR */ 31168c2ecf20Sopenharmony_ci info->rco = baud_co_60[13]; /* Rx CO */ 31178c2ecf20Sopenharmony_ci info->rtsdtr_inv = 1; 31188c2ecf20Sopenharmony_ci } else { 31198c2ecf20Sopenharmony_ci info->tbpr = baud_bpr_25[13]; /* Tx BPR */ 31208c2ecf20Sopenharmony_ci info->tco = baud_co_25[13]; /* Tx CO */ 31218c2ecf20Sopenharmony_ci info->rbpr = baud_bpr_25[13]; /* Rx BPR */ 31228c2ecf20Sopenharmony_ci info->rco = baud_co_25[13]; /* Rx CO */ 31238c2ecf20Sopenharmony_ci info->rtsdtr_inv = 0; 31248c2ecf20Sopenharmony_ci } 31258c2ecf20Sopenharmony_ci info->read_status_mask = CyTIMEOUT | CySPECHAR | 31268c2ecf20Sopenharmony_ci CyBREAK | CyPARITY | CyFRAME | CyOVERRUN; 31278c2ecf20Sopenharmony_ci } 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci } 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR 31328c2ecf20Sopenharmony_ci if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) { 31338c2ecf20Sopenharmony_ci mod_timer(&cyz_timerlist, jiffies + 1); 31348c2ecf20Sopenharmony_ci#ifdef CY_PCI_DEBUG 31358c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Cyclades-Z polling initialized\n"); 31368c2ecf20Sopenharmony_ci#endif 31378c2ecf20Sopenharmony_ci } 31388c2ecf20Sopenharmony_ci#endif 31398c2ecf20Sopenharmony_ci return 0; 31408c2ecf20Sopenharmony_ci} 31418c2ecf20Sopenharmony_ci 31428c2ecf20Sopenharmony_ci/* initialize chips on Cyclom-Y card -- return number of valid 31438c2ecf20Sopenharmony_ci chips (which is number of ports/4) */ 31448c2ecf20Sopenharmony_cistatic unsigned short cyy_init_card(void __iomem *true_base_addr, 31458c2ecf20Sopenharmony_ci int index) 31468c2ecf20Sopenharmony_ci{ 31478c2ecf20Sopenharmony_ci unsigned int chip_number; 31488c2ecf20Sopenharmony_ci void __iomem *base_addr; 31498c2ecf20Sopenharmony_ci 31508c2ecf20Sopenharmony_ci cy_writeb(true_base_addr + (Cy_HwReset << index), 0); 31518c2ecf20Sopenharmony_ci /* Cy_HwReset is 0x1400 */ 31528c2ecf20Sopenharmony_ci cy_writeb(true_base_addr + (Cy_ClrIntr << index), 0); 31538c2ecf20Sopenharmony_ci /* Cy_ClrIntr is 0x1800 */ 31548c2ecf20Sopenharmony_ci udelay(500L); 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_ci for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD; 31578c2ecf20Sopenharmony_ci chip_number++) { 31588c2ecf20Sopenharmony_ci base_addr = 31598c2ecf20Sopenharmony_ci true_base_addr + (cy_chip_offset[chip_number] << index); 31608c2ecf20Sopenharmony_ci mdelay(1); 31618c2ecf20Sopenharmony_ci if (readb(base_addr + (CyCCR << index)) != 0x00) { 31628c2ecf20Sopenharmony_ci /************* 31638c2ecf20Sopenharmony_ci printk(" chip #%d at %#6lx is never idle (CCR != 0)\n", 31648c2ecf20Sopenharmony_ci chip_number, (unsigned long)base_addr); 31658c2ecf20Sopenharmony_ci *************/ 31668c2ecf20Sopenharmony_ci return chip_number; 31678c2ecf20Sopenharmony_ci } 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci cy_writeb(base_addr + (CyGFRCR << index), 0); 31708c2ecf20Sopenharmony_ci udelay(10L); 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci /* The Cyclom-16Y does not decode address bit 9 and therefore 31738c2ecf20Sopenharmony_ci cannot distinguish between references to chip 0 and a non- 31748c2ecf20Sopenharmony_ci existent chip 4. If the preceding clearing of the supposed 31758c2ecf20Sopenharmony_ci chip 4 GFRCR register appears at chip 0, there is no chip 4 31768c2ecf20Sopenharmony_ci and this must be a Cyclom-16Y, not a Cyclom-32Ye. 31778c2ecf20Sopenharmony_ci */ 31788c2ecf20Sopenharmony_ci if (chip_number == 4 && readb(true_base_addr + 31798c2ecf20Sopenharmony_ci (cy_chip_offset[0] << index) + 31808c2ecf20Sopenharmony_ci (CyGFRCR << index)) == 0) { 31818c2ecf20Sopenharmony_ci return chip_number; 31828c2ecf20Sopenharmony_ci } 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci cy_writeb(base_addr + (CyCCR << index), CyCHIP_RESET); 31858c2ecf20Sopenharmony_ci mdelay(1); 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ci if (readb(base_addr + (CyGFRCR << index)) == 0x00) { 31888c2ecf20Sopenharmony_ci /* 31898c2ecf20Sopenharmony_ci printk(" chip #%d at %#6lx is not responding ", 31908c2ecf20Sopenharmony_ci chip_number, (unsigned long)base_addr); 31918c2ecf20Sopenharmony_ci printk("(GFRCR stayed 0)\n", 31928c2ecf20Sopenharmony_ci */ 31938c2ecf20Sopenharmony_ci return chip_number; 31948c2ecf20Sopenharmony_ci } 31958c2ecf20Sopenharmony_ci if ((0xf0 & (readb(base_addr + (CyGFRCR << index)))) != 31968c2ecf20Sopenharmony_ci 0x40) { 31978c2ecf20Sopenharmony_ci /* 31988c2ecf20Sopenharmony_ci printk(" chip #%d at %#6lx is not valid (GFRCR == " 31998c2ecf20Sopenharmony_ci "%#2x)\n", 32008c2ecf20Sopenharmony_ci chip_number, (unsigned long)base_addr, 32018c2ecf20Sopenharmony_ci base_addr[CyGFRCR<<index]); 32028c2ecf20Sopenharmony_ci */ 32038c2ecf20Sopenharmony_ci return chip_number; 32048c2ecf20Sopenharmony_ci } 32058c2ecf20Sopenharmony_ci cy_writeb(base_addr + (CyGCR << index), CyCH0_SERIAL); 32068c2ecf20Sopenharmony_ci if (readb(base_addr + (CyGFRCR << index)) >= CD1400_REV_J) { 32078c2ecf20Sopenharmony_ci /* It is a CD1400 rev. J or later */ 32088c2ecf20Sopenharmony_ci /* Impossible to reach 5ms with this chip. 32098c2ecf20Sopenharmony_ci Changed to 2ms instead (f = 500 Hz). */ 32108c2ecf20Sopenharmony_ci cy_writeb(base_addr + (CyPPR << index), CyCLOCK_60_2MS); 32118c2ecf20Sopenharmony_ci } else { 32128c2ecf20Sopenharmony_ci /* f = 200 Hz */ 32138c2ecf20Sopenharmony_ci cy_writeb(base_addr + (CyPPR << index), CyCLOCK_25_5MS); 32148c2ecf20Sopenharmony_ci } 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci /* 32178c2ecf20Sopenharmony_ci printk(" chip #%d at %#6lx is rev 0x%2x\n", 32188c2ecf20Sopenharmony_ci chip_number, (unsigned long)base_addr, 32198c2ecf20Sopenharmony_ci readb(base_addr+(CyGFRCR<<index))); 32208c2ecf20Sopenharmony_ci */ 32218c2ecf20Sopenharmony_ci } 32228c2ecf20Sopenharmony_ci return chip_number; 32238c2ecf20Sopenharmony_ci} /* cyy_init_card */ 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ci/* 32268c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- 32278c2ecf20Sopenharmony_ci * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. 32288c2ecf20Sopenharmony_ci * sets global variables and return the number of ISA boards found. 32298c2ecf20Sopenharmony_ci * --------------------------------------------------------------------- 32308c2ecf20Sopenharmony_ci */ 32318c2ecf20Sopenharmony_cistatic int __init cy_detect_isa(void) 32328c2ecf20Sopenharmony_ci{ 32338c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA 32348c2ecf20Sopenharmony_ci struct cyclades_card *card; 32358c2ecf20Sopenharmony_ci unsigned short cy_isa_irq, nboard; 32368c2ecf20Sopenharmony_ci void __iomem *cy_isa_address; 32378c2ecf20Sopenharmony_ci unsigned short i, j, k, cy_isa_nchan; 32388c2ecf20Sopenharmony_ci int isparam = 0; 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci nboard = 0; 32418c2ecf20Sopenharmony_ci 32428c2ecf20Sopenharmony_ci /* Check for module parameters */ 32438c2ecf20Sopenharmony_ci for (i = 0; i < NR_CARDS; i++) { 32448c2ecf20Sopenharmony_ci if (maddr[i] || i) { 32458c2ecf20Sopenharmony_ci isparam = 1; 32468c2ecf20Sopenharmony_ci cy_isa_addresses[i] = maddr[i]; 32478c2ecf20Sopenharmony_ci } 32488c2ecf20Sopenharmony_ci if (!maddr[i]) 32498c2ecf20Sopenharmony_ci break; 32508c2ecf20Sopenharmony_ci } 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_ci /* scan the address table probing for Cyclom-Y/ISA boards */ 32538c2ecf20Sopenharmony_ci for (i = 0; i < NR_ISA_ADDRS; i++) { 32548c2ecf20Sopenharmony_ci unsigned int isa_address = cy_isa_addresses[i]; 32558c2ecf20Sopenharmony_ci if (isa_address == 0x0000) 32568c2ecf20Sopenharmony_ci return nboard; 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci /* probe for CD1400... */ 32598c2ecf20Sopenharmony_ci cy_isa_address = ioremap(isa_address, CyISA_Ywin); 32608c2ecf20Sopenharmony_ci if (cy_isa_address == NULL) { 32618c2ecf20Sopenharmony_ci printk(KERN_ERR "Cyclom-Y/ISA: can't remap base " 32628c2ecf20Sopenharmony_ci "address\n"); 32638c2ecf20Sopenharmony_ci continue; 32648c2ecf20Sopenharmony_ci } 32658c2ecf20Sopenharmony_ci cy_isa_nchan = CyPORTS_PER_CHIP * 32668c2ecf20Sopenharmony_ci cyy_init_card(cy_isa_address, 0); 32678c2ecf20Sopenharmony_ci if (cy_isa_nchan == 0) { 32688c2ecf20Sopenharmony_ci iounmap(cy_isa_address); 32698c2ecf20Sopenharmony_ci continue; 32708c2ecf20Sopenharmony_ci } 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_ci if (isparam && i < NR_CARDS && irq[i]) 32738c2ecf20Sopenharmony_ci cy_isa_irq = irq[i]; 32748c2ecf20Sopenharmony_ci else 32758c2ecf20Sopenharmony_ci /* find out the board's irq by probing */ 32768c2ecf20Sopenharmony_ci cy_isa_irq = detect_isa_irq(cy_isa_address); 32778c2ecf20Sopenharmony_ci if (cy_isa_irq == 0) { 32788c2ecf20Sopenharmony_ci printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but the " 32798c2ecf20Sopenharmony_ci "IRQ could not be detected.\n", 32808c2ecf20Sopenharmony_ci (unsigned long)cy_isa_address); 32818c2ecf20Sopenharmony_ci iounmap(cy_isa_address); 32828c2ecf20Sopenharmony_ci continue; 32838c2ecf20Sopenharmony_ci } 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci if ((cy_next_channel + cy_isa_nchan) > NR_PORTS) { 32868c2ecf20Sopenharmony_ci printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " 32878c2ecf20Sopenharmony_ci "more channels are available. Change NR_PORTS " 32888c2ecf20Sopenharmony_ci "in cyclades.c and recompile kernel.\n", 32898c2ecf20Sopenharmony_ci (unsigned long)cy_isa_address); 32908c2ecf20Sopenharmony_ci iounmap(cy_isa_address); 32918c2ecf20Sopenharmony_ci return nboard; 32928c2ecf20Sopenharmony_ci } 32938c2ecf20Sopenharmony_ci /* fill the next cy_card structure available */ 32948c2ecf20Sopenharmony_ci for (j = 0; j < NR_CARDS; j++) { 32958c2ecf20Sopenharmony_ci card = &cy_card[j]; 32968c2ecf20Sopenharmony_ci if (card->base_addr == NULL) 32978c2ecf20Sopenharmony_ci break; 32988c2ecf20Sopenharmony_ci } 32998c2ecf20Sopenharmony_ci if (j == NR_CARDS) { /* no more cy_cards available */ 33008c2ecf20Sopenharmony_ci printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " 33018c2ecf20Sopenharmony_ci "more cards can be used. Change NR_CARDS in " 33028c2ecf20Sopenharmony_ci "cyclades.c and recompile kernel.\n", 33038c2ecf20Sopenharmony_ci (unsigned long)cy_isa_address); 33048c2ecf20Sopenharmony_ci iounmap(cy_isa_address); 33058c2ecf20Sopenharmony_ci return nboard; 33068c2ecf20Sopenharmony_ci } 33078c2ecf20Sopenharmony_ci 33088c2ecf20Sopenharmony_ci /* allocate IRQ */ 33098c2ecf20Sopenharmony_ci if (request_irq(cy_isa_irq, cyy_interrupt, 33108c2ecf20Sopenharmony_ci 0, "Cyclom-Y", card)) { 33118c2ecf20Sopenharmony_ci printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " 33128c2ecf20Sopenharmony_ci "could not allocate IRQ#%d.\n", 33138c2ecf20Sopenharmony_ci (unsigned long)cy_isa_address, cy_isa_irq); 33148c2ecf20Sopenharmony_ci iounmap(cy_isa_address); 33158c2ecf20Sopenharmony_ci return nboard; 33168c2ecf20Sopenharmony_ci } 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci /* set cy_card */ 33198c2ecf20Sopenharmony_ci card->base_addr = cy_isa_address; 33208c2ecf20Sopenharmony_ci card->ctl_addr.p9050 = NULL; 33218c2ecf20Sopenharmony_ci card->irq = (int)cy_isa_irq; 33228c2ecf20Sopenharmony_ci card->bus_index = 0; 33238c2ecf20Sopenharmony_ci card->first_line = cy_next_channel; 33248c2ecf20Sopenharmony_ci card->num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; 33258c2ecf20Sopenharmony_ci card->nports = cy_isa_nchan; 33268c2ecf20Sopenharmony_ci if (cy_init_card(card)) { 33278c2ecf20Sopenharmony_ci card->base_addr = NULL; 33288c2ecf20Sopenharmony_ci free_irq(cy_isa_irq, card); 33298c2ecf20Sopenharmony_ci iounmap(cy_isa_address); 33308c2ecf20Sopenharmony_ci continue; 33318c2ecf20Sopenharmony_ci } 33328c2ecf20Sopenharmony_ci nboard++; 33338c2ecf20Sopenharmony_ci 33348c2ecf20Sopenharmony_ci printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: " 33358c2ecf20Sopenharmony_ci "%d channels starting from port %d\n", 33368c2ecf20Sopenharmony_ci j + 1, (unsigned long)cy_isa_address, 33378c2ecf20Sopenharmony_ci (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), 33388c2ecf20Sopenharmony_ci cy_isa_irq, cy_isa_nchan, cy_next_channel); 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_ci for (k = 0, j = cy_next_channel; 33418c2ecf20Sopenharmony_ci j < cy_next_channel + cy_isa_nchan; j++, k++) 33428c2ecf20Sopenharmony_ci tty_port_register_device(&card->ports[k].port, 33438c2ecf20Sopenharmony_ci cy_serial_driver, j, NULL); 33448c2ecf20Sopenharmony_ci cy_next_channel += cy_isa_nchan; 33458c2ecf20Sopenharmony_ci } 33468c2ecf20Sopenharmony_ci return nboard; 33478c2ecf20Sopenharmony_ci#else 33488c2ecf20Sopenharmony_ci return 0; 33498c2ecf20Sopenharmony_ci#endif /* CONFIG_ISA */ 33508c2ecf20Sopenharmony_ci} /* cy_detect_isa */ 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 33538c2ecf20Sopenharmony_cistatic inline int cyc_isfwstr(const char *str, unsigned int size) 33548c2ecf20Sopenharmony_ci{ 33558c2ecf20Sopenharmony_ci unsigned int a; 33568c2ecf20Sopenharmony_ci 33578c2ecf20Sopenharmony_ci for (a = 0; a < size && *str; a++, str++) 33588c2ecf20Sopenharmony_ci if (*str & 0x80) 33598c2ecf20Sopenharmony_ci return -EINVAL; 33608c2ecf20Sopenharmony_ci 33618c2ecf20Sopenharmony_ci for (; a < size; a++, str++) 33628c2ecf20Sopenharmony_ci if (*str) 33638c2ecf20Sopenharmony_ci return -EINVAL; 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci return 0; 33668c2ecf20Sopenharmony_ci} 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_cistatic inline void cyz_fpga_copy(void __iomem *fpga, const u8 *data, 33698c2ecf20Sopenharmony_ci unsigned int size) 33708c2ecf20Sopenharmony_ci{ 33718c2ecf20Sopenharmony_ci for (; size > 0; size--) { 33728c2ecf20Sopenharmony_ci cy_writel(fpga, *data++); 33738c2ecf20Sopenharmony_ci udelay(10); 33748c2ecf20Sopenharmony_ci } 33758c2ecf20Sopenharmony_ci} 33768c2ecf20Sopenharmony_ci 33778c2ecf20Sopenharmony_cistatic void plx_init(struct pci_dev *pdev, int irq, 33788c2ecf20Sopenharmony_ci struct RUNTIME_9060 __iomem *addr) 33798c2ecf20Sopenharmony_ci{ 33808c2ecf20Sopenharmony_ci /* Reset PLX */ 33818c2ecf20Sopenharmony_ci cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000); 33828c2ecf20Sopenharmony_ci udelay(100L); 33838c2ecf20Sopenharmony_ci cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000); 33848c2ecf20Sopenharmony_ci 33858c2ecf20Sopenharmony_ci /* Reload Config. Registers from EEPROM */ 33868c2ecf20Sopenharmony_ci cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000); 33878c2ecf20Sopenharmony_ci udelay(100L); 33888c2ecf20Sopenharmony_ci cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000); 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_ci /* For some yet unknown reason, once the PLX9060 reloads the EEPROM, 33918c2ecf20Sopenharmony_ci * the IRQ is lost and, thus, we have to re-write it to the PCI config. 33928c2ecf20Sopenharmony_ci * registers. This will remain here until we find a permanent fix. 33938c2ecf20Sopenharmony_ci */ 33948c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); 33958c2ecf20Sopenharmony_ci} 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_cistatic int __cyz_load_fw(const struct firmware *fw, 33988c2ecf20Sopenharmony_ci const char *name, const u32 mailbox, void __iomem *base, 33998c2ecf20Sopenharmony_ci void __iomem *fpga) 34008c2ecf20Sopenharmony_ci{ 34018c2ecf20Sopenharmony_ci const void *ptr = fw->data; 34028c2ecf20Sopenharmony_ci const struct zfile_header *h = ptr; 34038c2ecf20Sopenharmony_ci const struct zfile_config *c, *cs; 34048c2ecf20Sopenharmony_ci const struct zfile_block *b, *bs; 34058c2ecf20Sopenharmony_ci unsigned int a, tmp, len = fw->size; 34068c2ecf20Sopenharmony_ci#define BAD_FW KERN_ERR "Bad firmware: " 34078c2ecf20Sopenharmony_ci if (len < sizeof(*h)) { 34088c2ecf20Sopenharmony_ci printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h)); 34098c2ecf20Sopenharmony_ci return -EINVAL; 34108c2ecf20Sopenharmony_ci } 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci cs = ptr + h->config_offset; 34138c2ecf20Sopenharmony_ci bs = ptr + h->block_offset; 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci if ((void *)(cs + h->n_config) > ptr + len || 34168c2ecf20Sopenharmony_ci (void *)(bs + h->n_blocks) > ptr + len) { 34178c2ecf20Sopenharmony_ci printk(BAD_FW "too short"); 34188c2ecf20Sopenharmony_ci return -EINVAL; 34198c2ecf20Sopenharmony_ci } 34208c2ecf20Sopenharmony_ci 34218c2ecf20Sopenharmony_ci if (cyc_isfwstr(h->name, sizeof(h->name)) || 34228c2ecf20Sopenharmony_ci cyc_isfwstr(h->date, sizeof(h->date))) { 34238c2ecf20Sopenharmony_ci printk(BAD_FW "bad formatted header string\n"); 34248c2ecf20Sopenharmony_ci return -EINVAL; 34258c2ecf20Sopenharmony_ci } 34268c2ecf20Sopenharmony_ci 34278c2ecf20Sopenharmony_ci if (strncmp(name, h->name, sizeof(h->name))) { 34288c2ecf20Sopenharmony_ci printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name); 34298c2ecf20Sopenharmony_ci return -EINVAL; 34308c2ecf20Sopenharmony_ci } 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci tmp = 0; 34338c2ecf20Sopenharmony_ci for (c = cs; c < cs + h->n_config; c++) { 34348c2ecf20Sopenharmony_ci for (a = 0; a < c->n_blocks; a++) 34358c2ecf20Sopenharmony_ci if (c->block_list[a] > h->n_blocks) { 34368c2ecf20Sopenharmony_ci printk(BAD_FW "bad block ref number in cfgs\n"); 34378c2ecf20Sopenharmony_ci return -EINVAL; 34388c2ecf20Sopenharmony_ci } 34398c2ecf20Sopenharmony_ci if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */ 34408c2ecf20Sopenharmony_ci tmp++; 34418c2ecf20Sopenharmony_ci } 34428c2ecf20Sopenharmony_ci if (!tmp) { 34438c2ecf20Sopenharmony_ci printk(BAD_FW "nothing appropriate\n"); 34448c2ecf20Sopenharmony_ci return -EINVAL; 34458c2ecf20Sopenharmony_ci } 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci for (b = bs; b < bs + h->n_blocks; b++) 34488c2ecf20Sopenharmony_ci if (b->file_offset + b->size > len) { 34498c2ecf20Sopenharmony_ci printk(BAD_FW "bad block data offset\n"); 34508c2ecf20Sopenharmony_ci return -EINVAL; 34518c2ecf20Sopenharmony_ci } 34528c2ecf20Sopenharmony_ci 34538c2ecf20Sopenharmony_ci /* everything is OK, let's seek'n'load it */ 34548c2ecf20Sopenharmony_ci for (c = cs; c < cs + h->n_config; c++) 34558c2ecf20Sopenharmony_ci if (c->mailbox == mailbox && c->function == 0) 34568c2ecf20Sopenharmony_ci break; 34578c2ecf20Sopenharmony_ci 34588c2ecf20Sopenharmony_ci for (a = 0; a < c->n_blocks; a++) { 34598c2ecf20Sopenharmony_ci b = &bs[c->block_list[a]]; 34608c2ecf20Sopenharmony_ci if (b->type == ZBLOCK_FPGA) { 34618c2ecf20Sopenharmony_ci if (fpga != NULL) 34628c2ecf20Sopenharmony_ci cyz_fpga_copy(fpga, ptr + b->file_offset, 34638c2ecf20Sopenharmony_ci b->size); 34648c2ecf20Sopenharmony_ci } else { 34658c2ecf20Sopenharmony_ci if (base != NULL) 34668c2ecf20Sopenharmony_ci memcpy_toio(base + b->ram_offset, 34678c2ecf20Sopenharmony_ci ptr + b->file_offset, b->size); 34688c2ecf20Sopenharmony_ci } 34698c2ecf20Sopenharmony_ci } 34708c2ecf20Sopenharmony_ci#undef BAD_FW 34718c2ecf20Sopenharmony_ci return 0; 34728c2ecf20Sopenharmony_ci} 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_cistatic int cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, 34758c2ecf20Sopenharmony_ci struct RUNTIME_9060 __iomem *ctl_addr, int irq) 34768c2ecf20Sopenharmony_ci{ 34778c2ecf20Sopenharmony_ci const struct firmware *fw; 34788c2ecf20Sopenharmony_ci struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS; 34798c2ecf20Sopenharmony_ci struct CUSTOM_REG __iomem *cust = base_addr; 34808c2ecf20Sopenharmony_ci struct ZFW_CTRL __iomem *pt_zfwctrl; 34818c2ecf20Sopenharmony_ci void __iomem *tmp; 34828c2ecf20Sopenharmony_ci u32 mailbox, status, nchan; 34838c2ecf20Sopenharmony_ci unsigned int i; 34848c2ecf20Sopenharmony_ci int retval; 34858c2ecf20Sopenharmony_ci 34868c2ecf20Sopenharmony_ci retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev); 34878c2ecf20Sopenharmony_ci if (retval) { 34888c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't get firmware\n"); 34898c2ecf20Sopenharmony_ci goto err; 34908c2ecf20Sopenharmony_ci } 34918c2ecf20Sopenharmony_ci 34928c2ecf20Sopenharmony_ci /* Check whether the firmware is already loaded and running. If 34938c2ecf20Sopenharmony_ci positive, skip this board */ 34948c2ecf20Sopenharmony_ci if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) { 34958c2ecf20Sopenharmony_ci u32 cntval = readl(base_addr + 0x190); 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ci udelay(100); 34988c2ecf20Sopenharmony_ci if (cntval != readl(base_addr + 0x190)) { 34998c2ecf20Sopenharmony_ci /* FW counter is working, FW is running */ 35008c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. " 35018c2ecf20Sopenharmony_ci "Skipping board.\n"); 35028c2ecf20Sopenharmony_ci retval = 0; 35038c2ecf20Sopenharmony_ci goto err_rel; 35048c2ecf20Sopenharmony_ci } 35058c2ecf20Sopenharmony_ci } 35068c2ecf20Sopenharmony_ci 35078c2ecf20Sopenharmony_ci /* start boot */ 35088c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) & 35098c2ecf20Sopenharmony_ci ~0x00030800UL); 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci mailbox = readl(&ctl_addr->mail_box_0); 35128c2ecf20Sopenharmony_ci 35138c2ecf20Sopenharmony_ci if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) { 35148c2ecf20Sopenharmony_ci /* stops CPU and set window to beginning of RAM */ 35158c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); 35168c2ecf20Sopenharmony_ci cy_writel(&cust->cpu_stop, 0); 35178c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); 35188c2ecf20Sopenharmony_ci udelay(100); 35198c2ecf20Sopenharmony_ci } 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci plx_init(pdev, irq, ctl_addr); 35228c2ecf20Sopenharmony_ci 35238c2ecf20Sopenharmony_ci if (mailbox != 0) { 35248c2ecf20Sopenharmony_ci /* load FPGA */ 35258c2ecf20Sopenharmony_ci retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL, 35268c2ecf20Sopenharmony_ci base_addr); 35278c2ecf20Sopenharmony_ci if (retval) 35288c2ecf20Sopenharmony_ci goto err_rel; 35298c2ecf20Sopenharmony_ci if (!__cyz_fpga_loaded(ctl_addr)) { 35308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "fw upload successful, but fw is " 35318c2ecf20Sopenharmony_ci "not loaded\n"); 35328c2ecf20Sopenharmony_ci goto err_rel; 35338c2ecf20Sopenharmony_ci } 35348c2ecf20Sopenharmony_ci } 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_ci /* stops CPU and set window to beginning of RAM */ 35378c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); 35388c2ecf20Sopenharmony_ci cy_writel(&cust->cpu_stop, 0); 35398c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); 35408c2ecf20Sopenharmony_ci udelay(100); 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci /* clear memory */ 35438c2ecf20Sopenharmony_ci for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) 35448c2ecf20Sopenharmony_ci cy_writeb(tmp, 255); 35458c2ecf20Sopenharmony_ci if (mailbox != 0) { 35468c2ecf20Sopenharmony_ci /* set window to last 512K of RAM */ 35478c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE); 35488c2ecf20Sopenharmony_ci for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) 35498c2ecf20Sopenharmony_ci cy_writeb(tmp, 255); 35508c2ecf20Sopenharmony_ci /* set window to beginning of RAM */ 35518c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); 35528c2ecf20Sopenharmony_ci } 35538c2ecf20Sopenharmony_ci 35548c2ecf20Sopenharmony_ci retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL); 35558c2ecf20Sopenharmony_ci release_firmware(fw); 35568c2ecf20Sopenharmony_ci if (retval) 35578c2ecf20Sopenharmony_ci goto err; 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci /* finish boot and start boards */ 35608c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); 35618c2ecf20Sopenharmony_ci cy_writel(&cust->cpu_start, 0); 35628c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); 35638c2ecf20Sopenharmony_ci i = 0; 35648c2ecf20Sopenharmony_ci while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40) 35658c2ecf20Sopenharmony_ci msleep(100); 35668c2ecf20Sopenharmony_ci if (status != ZFIRM_ID) { 35678c2ecf20Sopenharmony_ci if (status == ZFIRM_HLT) { 35688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "you need an external power supply " 35698c2ecf20Sopenharmony_ci "for this number of ports. Firmware halted and " 35708c2ecf20Sopenharmony_ci "board reset.\n"); 35718c2ecf20Sopenharmony_ci retval = -EIO; 35728c2ecf20Sopenharmony_ci goto err; 35738c2ecf20Sopenharmony_ci } 35748c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting " 35758c2ecf20Sopenharmony_ci "some more time\n", status); 35768c2ecf20Sopenharmony_ci while ((status = readl(&fid->signature)) != ZFIRM_ID && 35778c2ecf20Sopenharmony_ci i++ < 200) 35788c2ecf20Sopenharmony_ci msleep(100); 35798c2ecf20Sopenharmony_ci if (status != ZFIRM_ID) { 35808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Board not started in 20 seconds! " 35818c2ecf20Sopenharmony_ci "Giving up. (fid->signature = 0x%x)\n", 35828c2ecf20Sopenharmony_ci status); 35838c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "*** Warning ***: if you are " 35848c2ecf20Sopenharmony_ci "upgrading the FW, please power cycle the " 35858c2ecf20Sopenharmony_ci "system before loading the new FW to the " 35868c2ecf20Sopenharmony_ci "Cyclades-Z.\n"); 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci if (__cyz_fpga_loaded(ctl_addr)) 35898c2ecf20Sopenharmony_ci plx_init(pdev, irq, ctl_addr); 35908c2ecf20Sopenharmony_ci 35918c2ecf20Sopenharmony_ci retval = -EIO; 35928c2ecf20Sopenharmony_ci goto err; 35938c2ecf20Sopenharmony_ci } 35948c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n", 35958c2ecf20Sopenharmony_ci i / 10); 35968c2ecf20Sopenharmony_ci } 35978c2ecf20Sopenharmony_ci pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr); 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n", 36008c2ecf20Sopenharmony_ci base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr), 36018c2ecf20Sopenharmony_ci base_addr + readl(&fid->zfwctrl_addr)); 36028c2ecf20Sopenharmony_ci 36038c2ecf20Sopenharmony_ci nchan = readl(&pt_zfwctrl->board_ctrl.n_channel); 36048c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n", 36058c2ecf20Sopenharmony_ci readl(&pt_zfwctrl->board_ctrl.fw_version), nchan); 36068c2ecf20Sopenharmony_ci 36078c2ecf20Sopenharmony_ci if (nchan == 0) { 36088c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please " 36098c2ecf20Sopenharmony_ci "check the connection between the Z host card and the " 36108c2ecf20Sopenharmony_ci "serial expanders.\n"); 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci if (__cyz_fpga_loaded(ctl_addr)) 36138c2ecf20Sopenharmony_ci plx_init(pdev, irq, ctl_addr); 36148c2ecf20Sopenharmony_ci 36158c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Null number of ports detected. Board " 36168c2ecf20Sopenharmony_ci "reset.\n"); 36178c2ecf20Sopenharmony_ci retval = 0; 36188c2ecf20Sopenharmony_ci goto err; 36198c2ecf20Sopenharmony_ci } 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX); 36228c2ecf20Sopenharmony_ci cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION); 36238c2ecf20Sopenharmony_ci 36248c2ecf20Sopenharmony_ci /* 36258c2ecf20Sopenharmony_ci Early firmware failed to start looking for commands. 36268c2ecf20Sopenharmony_ci This enables firmware interrupts for those commands. 36278c2ecf20Sopenharmony_ci */ 36288c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | 36298c2ecf20Sopenharmony_ci (1 << 17)); 36308c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | 36318c2ecf20Sopenharmony_ci 0x00030800UL); 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_ci return nchan; 36348c2ecf20Sopenharmony_cierr_rel: 36358c2ecf20Sopenharmony_ci release_firmware(fw); 36368c2ecf20Sopenharmony_cierr: 36378c2ecf20Sopenharmony_ci return retval; 36388c2ecf20Sopenharmony_ci} 36398c2ecf20Sopenharmony_ci 36408c2ecf20Sopenharmony_cistatic int cy_pci_probe(struct pci_dev *pdev, 36418c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 36428c2ecf20Sopenharmony_ci{ 36438c2ecf20Sopenharmony_ci struct cyclades_card *card; 36448c2ecf20Sopenharmony_ci void __iomem *addr0 = NULL, *addr2 = NULL; 36458c2ecf20Sopenharmony_ci char *card_name = NULL; 36468c2ecf20Sopenharmony_ci u32 mailbox; 36478c2ecf20Sopenharmony_ci unsigned int device_id, nchan = 0, card_no, i, j; 36488c2ecf20Sopenharmony_ci unsigned char plx_ver; 36498c2ecf20Sopenharmony_ci int retval, irq; 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_ci retval = pci_enable_device(pdev); 36528c2ecf20Sopenharmony_ci if (retval) { 36538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot enable device\n"); 36548c2ecf20Sopenharmony_ci goto err; 36558c2ecf20Sopenharmony_ci } 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_ci /* read PCI configuration area */ 36588c2ecf20Sopenharmony_ci irq = pdev->irq; 36598c2ecf20Sopenharmony_ci device_id = pdev->device & ~PCI_DEVICE_ID_MASK; 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_ci#if defined(__alpha__) 36628c2ecf20Sopenharmony_ci if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ 36638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low " 36648c2ecf20Sopenharmony_ci "addresses on Alpha systems.\n"); 36658c2ecf20Sopenharmony_ci retval = -EIO; 36668c2ecf20Sopenharmony_ci goto err_dis; 36678c2ecf20Sopenharmony_ci } 36688c2ecf20Sopenharmony_ci#endif 36698c2ecf20Sopenharmony_ci if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) { 36708c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low " 36718c2ecf20Sopenharmony_ci "addresses\n"); 36728c2ecf20Sopenharmony_ci retval = -EIO; 36738c2ecf20Sopenharmony_ci goto err_dis; 36748c2ecf20Sopenharmony_ci } 36758c2ecf20Sopenharmony_ci 36768c2ecf20Sopenharmony_ci if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { 36778c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring " 36788c2ecf20Sopenharmony_ci "it...\n"); 36798c2ecf20Sopenharmony_ci pdev->resource[2].flags &= ~IORESOURCE_IO; 36808c2ecf20Sopenharmony_ci } 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_ci retval = pci_request_regions(pdev, "cyclades"); 36838c2ecf20Sopenharmony_ci if (retval) { 36848c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to reserve resources\n"); 36858c2ecf20Sopenharmony_ci goto err_dis; 36868c2ecf20Sopenharmony_ci } 36878c2ecf20Sopenharmony_ci 36888c2ecf20Sopenharmony_ci retval = -EIO; 36898c2ecf20Sopenharmony_ci if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || 36908c2ecf20Sopenharmony_ci device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { 36918c2ecf20Sopenharmony_ci card_name = "Cyclom-Y"; 36928c2ecf20Sopenharmony_ci 36938c2ecf20Sopenharmony_ci addr0 = ioremap(pci_resource_start(pdev, 0), 36948c2ecf20Sopenharmony_ci CyPCI_Yctl); 36958c2ecf20Sopenharmony_ci if (addr0 == NULL) { 36968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't remap ctl region\n"); 36978c2ecf20Sopenharmony_ci goto err_reg; 36988c2ecf20Sopenharmony_ci } 36998c2ecf20Sopenharmony_ci addr2 = ioremap(pci_resource_start(pdev, 2), 37008c2ecf20Sopenharmony_ci CyPCI_Ywin); 37018c2ecf20Sopenharmony_ci if (addr2 == NULL) { 37028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't remap base region\n"); 37038c2ecf20Sopenharmony_ci goto err_unmap; 37048c2ecf20Sopenharmony_ci } 37058c2ecf20Sopenharmony_ci 37068c2ecf20Sopenharmony_ci nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1); 37078c2ecf20Sopenharmony_ci if (nchan == 0) { 37088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cyclom-Y PCI host card with no " 37098c2ecf20Sopenharmony_ci "Serial-Modules\n"); 37108c2ecf20Sopenharmony_ci goto err_unmap; 37118c2ecf20Sopenharmony_ci } 37128c2ecf20Sopenharmony_ci } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { 37138c2ecf20Sopenharmony_ci struct RUNTIME_9060 __iomem *ctl_addr; 37148c2ecf20Sopenharmony_ci 37158c2ecf20Sopenharmony_ci ctl_addr = addr0 = ioremap(pci_resource_start(pdev, 0), 37168c2ecf20Sopenharmony_ci CyPCI_Zctl); 37178c2ecf20Sopenharmony_ci if (addr0 == NULL) { 37188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't remap ctl region\n"); 37198c2ecf20Sopenharmony_ci goto err_reg; 37208c2ecf20Sopenharmony_ci } 37218c2ecf20Sopenharmony_ci 37228c2ecf20Sopenharmony_ci /* Disable interrupts on the PLX before resetting it */ 37238c2ecf20Sopenharmony_ci cy_writew(&ctl_addr->intr_ctrl_stat, 37248c2ecf20Sopenharmony_ci readw(&ctl_addr->intr_ctrl_stat) & ~0x0900); 37258c2ecf20Sopenharmony_ci 37268c2ecf20Sopenharmony_ci plx_init(pdev, irq, addr0); 37278c2ecf20Sopenharmony_ci 37288c2ecf20Sopenharmony_ci mailbox = readl(&ctl_addr->mail_box_0); 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci addr2 = ioremap(pci_resource_start(pdev, 2), 37318c2ecf20Sopenharmony_ci mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin); 37328c2ecf20Sopenharmony_ci if (addr2 == NULL) { 37338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't remap base region\n"); 37348c2ecf20Sopenharmony_ci goto err_unmap; 37358c2ecf20Sopenharmony_ci } 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci if (mailbox == ZE_V1) { 37388c2ecf20Sopenharmony_ci card_name = "Cyclades-Ze"; 37398c2ecf20Sopenharmony_ci } else { 37408c2ecf20Sopenharmony_ci card_name = "Cyclades-8Zo"; 37418c2ecf20Sopenharmony_ci#ifdef CY_PCI_DEBUG 37428c2ecf20Sopenharmony_ci if (mailbox == ZO_V1) { 37438c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); 37448c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA " 37458c2ecf20Sopenharmony_ci "id %lx, ver %lx\n", (ulong)(0xff & 37468c2ecf20Sopenharmony_ci readl(&((struct CUSTOM_REG *)addr2)-> 37478c2ecf20Sopenharmony_ci fpga_id)), (ulong)(0xff & 37488c2ecf20Sopenharmony_ci readl(&((struct CUSTOM_REG *)addr2)-> 37498c2ecf20Sopenharmony_ci fpga_version))); 37508c2ecf20Sopenharmony_ci cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); 37518c2ecf20Sopenharmony_ci } else { 37528c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Cyclades-Z/PCI: New " 37538c2ecf20Sopenharmony_ci "Cyclades-Z board. FPGA not loaded\n"); 37548c2ecf20Sopenharmony_ci } 37558c2ecf20Sopenharmony_ci#endif 37568c2ecf20Sopenharmony_ci /* The following clears the firmware id word. This 37578c2ecf20Sopenharmony_ci ensures that the driver will not attempt to talk to 37588c2ecf20Sopenharmony_ci the board until it has been properly initialized. 37598c2ecf20Sopenharmony_ci */ 37608c2ecf20Sopenharmony_ci if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) 37618c2ecf20Sopenharmony_ci cy_writel(addr2 + ID_ADDRESS, 0L); 37628c2ecf20Sopenharmony_ci } 37638c2ecf20Sopenharmony_ci 37648c2ecf20Sopenharmony_ci retval = cyz_load_fw(pdev, addr2, addr0, irq); 37658c2ecf20Sopenharmony_ci if (retval <= 0) 37668c2ecf20Sopenharmony_ci goto err_unmap; 37678c2ecf20Sopenharmony_ci nchan = retval; 37688c2ecf20Sopenharmony_ci } 37698c2ecf20Sopenharmony_ci 37708c2ecf20Sopenharmony_ci if ((cy_next_channel + nchan) > NR_PORTS) { 37718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " 37728c2ecf20Sopenharmony_ci "channels are available. Change NR_PORTS in " 37738c2ecf20Sopenharmony_ci "cyclades.c and recompile kernel.\n"); 37748c2ecf20Sopenharmony_ci goto err_unmap; 37758c2ecf20Sopenharmony_ci } 37768c2ecf20Sopenharmony_ci /* fill the next cy_card structure available */ 37778c2ecf20Sopenharmony_ci for (card_no = 0; card_no < NR_CARDS; card_no++) { 37788c2ecf20Sopenharmony_ci card = &cy_card[card_no]; 37798c2ecf20Sopenharmony_ci if (card->base_addr == NULL) 37808c2ecf20Sopenharmony_ci break; 37818c2ecf20Sopenharmony_ci } 37828c2ecf20Sopenharmony_ci if (card_no == NR_CARDS) { /* no more cy_cards available */ 37838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " 37848c2ecf20Sopenharmony_ci "more cards can be used. Change NR_CARDS in " 37858c2ecf20Sopenharmony_ci "cyclades.c and recompile kernel.\n"); 37868c2ecf20Sopenharmony_ci goto err_unmap; 37878c2ecf20Sopenharmony_ci } 37888c2ecf20Sopenharmony_ci 37898c2ecf20Sopenharmony_ci if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || 37908c2ecf20Sopenharmony_ci device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { 37918c2ecf20Sopenharmony_ci /* allocate IRQ */ 37928c2ecf20Sopenharmony_ci retval = request_irq(irq, cyy_interrupt, 37938c2ecf20Sopenharmony_ci IRQF_SHARED, "Cyclom-Y", card); 37948c2ecf20Sopenharmony_ci if (retval) { 37958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not allocate IRQ\n"); 37968c2ecf20Sopenharmony_ci goto err_unmap; 37978c2ecf20Sopenharmony_ci } 37988c2ecf20Sopenharmony_ci card->num_chips = nchan / CyPORTS_PER_CHIP; 37998c2ecf20Sopenharmony_ci } else { 38008c2ecf20Sopenharmony_ci struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; 38018c2ecf20Sopenharmony_ci struct ZFW_CTRL __iomem *zfw_ctrl; 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_ci zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); 38048c2ecf20Sopenharmony_ci 38058c2ecf20Sopenharmony_ci card->hw_ver = mailbox; 38068c2ecf20Sopenharmony_ci card->num_chips = (unsigned int)-1; 38078c2ecf20Sopenharmony_ci card->board_ctrl = &zfw_ctrl->board_ctrl; 38088c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR 38098c2ecf20Sopenharmony_ci /* allocate IRQ only if board has an IRQ */ 38108c2ecf20Sopenharmony_ci if (irq != 0 && irq != 255) { 38118c2ecf20Sopenharmony_ci retval = request_irq(irq, cyz_interrupt, 38128c2ecf20Sopenharmony_ci IRQF_SHARED, "Cyclades-Z", card); 38138c2ecf20Sopenharmony_ci if (retval) { 38148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not allocate IRQ\n"); 38158c2ecf20Sopenharmony_ci goto err_unmap; 38168c2ecf20Sopenharmony_ci } 38178c2ecf20Sopenharmony_ci } 38188c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 38198c2ecf20Sopenharmony_ci } 38208c2ecf20Sopenharmony_ci 38218c2ecf20Sopenharmony_ci /* set cy_card */ 38228c2ecf20Sopenharmony_ci card->base_addr = addr2; 38238c2ecf20Sopenharmony_ci card->ctl_addr.p9050 = addr0; 38248c2ecf20Sopenharmony_ci card->irq = irq; 38258c2ecf20Sopenharmony_ci card->bus_index = 1; 38268c2ecf20Sopenharmony_ci card->first_line = cy_next_channel; 38278c2ecf20Sopenharmony_ci card->nports = nchan; 38288c2ecf20Sopenharmony_ci retval = cy_init_card(card); 38298c2ecf20Sopenharmony_ci if (retval) 38308c2ecf20Sopenharmony_ci goto err_null; 38318c2ecf20Sopenharmony_ci 38328c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, card); 38338c2ecf20Sopenharmony_ci 38348c2ecf20Sopenharmony_ci if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || 38358c2ecf20Sopenharmony_ci device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { 38368c2ecf20Sopenharmony_ci /* enable interrupts in the PCI interface */ 38378c2ecf20Sopenharmony_ci plx_ver = readb(addr2 + CyPLX_VER) & 0x0f; 38388c2ecf20Sopenharmony_ci switch (plx_ver) { 38398c2ecf20Sopenharmony_ci case PLX_9050: 38408c2ecf20Sopenharmony_ci cy_writeb(addr0 + 0x4c, 0x43); 38418c2ecf20Sopenharmony_ci break; 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_ci case PLX_9060: 38448c2ecf20Sopenharmony_ci case PLX_9080: 38458c2ecf20Sopenharmony_ci default: /* Old boards, use PLX_9060 */ 38468c2ecf20Sopenharmony_ci { 38478c2ecf20Sopenharmony_ci struct RUNTIME_9060 __iomem *ctl_addr = addr0; 38488c2ecf20Sopenharmony_ci plx_init(pdev, irq, ctl_addr); 38498c2ecf20Sopenharmony_ci cy_writew(&ctl_addr->intr_ctrl_stat, 38508c2ecf20Sopenharmony_ci readw(&ctl_addr->intr_ctrl_stat) | 0x0900); 38518c2ecf20Sopenharmony_ci break; 38528c2ecf20Sopenharmony_ci } 38538c2ecf20Sopenharmony_ci } 38548c2ecf20Sopenharmony_ci } 38558c2ecf20Sopenharmony_ci 38568c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from " 38578c2ecf20Sopenharmony_ci "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel); 38588c2ecf20Sopenharmony_ci for (j = 0, i = cy_next_channel; i < cy_next_channel + nchan; i++, j++) 38598c2ecf20Sopenharmony_ci tty_port_register_device(&card->ports[j].port, 38608c2ecf20Sopenharmony_ci cy_serial_driver, i, &pdev->dev); 38618c2ecf20Sopenharmony_ci cy_next_channel += nchan; 38628c2ecf20Sopenharmony_ci 38638c2ecf20Sopenharmony_ci return 0; 38648c2ecf20Sopenharmony_cierr_null: 38658c2ecf20Sopenharmony_ci card->base_addr = NULL; 38668c2ecf20Sopenharmony_ci free_irq(irq, card); 38678c2ecf20Sopenharmony_cierr_unmap: 38688c2ecf20Sopenharmony_ci iounmap(addr0); 38698c2ecf20Sopenharmony_ci if (addr2) 38708c2ecf20Sopenharmony_ci iounmap(addr2); 38718c2ecf20Sopenharmony_cierr_reg: 38728c2ecf20Sopenharmony_ci pci_release_regions(pdev); 38738c2ecf20Sopenharmony_cierr_dis: 38748c2ecf20Sopenharmony_ci pci_disable_device(pdev); 38758c2ecf20Sopenharmony_cierr: 38768c2ecf20Sopenharmony_ci return retval; 38778c2ecf20Sopenharmony_ci} 38788c2ecf20Sopenharmony_ci 38798c2ecf20Sopenharmony_cistatic void cy_pci_remove(struct pci_dev *pdev) 38808c2ecf20Sopenharmony_ci{ 38818c2ecf20Sopenharmony_ci struct cyclades_card *cinfo = pci_get_drvdata(pdev); 38828c2ecf20Sopenharmony_ci unsigned int i, channel; 38838c2ecf20Sopenharmony_ci 38848c2ecf20Sopenharmony_ci /* non-Z with old PLX */ 38858c2ecf20Sopenharmony_ci if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) == 38868c2ecf20Sopenharmony_ci PLX_9050) 38878c2ecf20Sopenharmony_ci cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0); 38888c2ecf20Sopenharmony_ci else 38898c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR 38908c2ecf20Sopenharmony_ci if (!cy_is_Z(cinfo)) 38918c2ecf20Sopenharmony_ci#endif 38928c2ecf20Sopenharmony_ci cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat, 38938c2ecf20Sopenharmony_ci readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) & 38948c2ecf20Sopenharmony_ci ~0x0900); 38958c2ecf20Sopenharmony_ci 38968c2ecf20Sopenharmony_ci iounmap(cinfo->base_addr); 38978c2ecf20Sopenharmony_ci if (cinfo->ctl_addr.p9050) 38988c2ecf20Sopenharmony_ci iounmap(cinfo->ctl_addr.p9050); 38998c2ecf20Sopenharmony_ci if (cinfo->irq 39008c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR 39018c2ecf20Sopenharmony_ci && !cy_is_Z(cinfo) 39028c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 39038c2ecf20Sopenharmony_ci ) 39048c2ecf20Sopenharmony_ci free_irq(cinfo->irq, cinfo); 39058c2ecf20Sopenharmony_ci pci_release_regions(pdev); 39068c2ecf20Sopenharmony_ci 39078c2ecf20Sopenharmony_ci cinfo->base_addr = NULL; 39088c2ecf20Sopenharmony_ci for (channel = 0, i = cinfo->first_line; i < cinfo->first_line + 39098c2ecf20Sopenharmony_ci cinfo->nports; i++, channel++) { 39108c2ecf20Sopenharmony_ci tty_unregister_device(cy_serial_driver, i); 39118c2ecf20Sopenharmony_ci tty_port_destroy(&cinfo->ports[channel].port); 39128c2ecf20Sopenharmony_ci } 39138c2ecf20Sopenharmony_ci cinfo->nports = 0; 39148c2ecf20Sopenharmony_ci kfree(cinfo->ports); 39158c2ecf20Sopenharmony_ci} 39168c2ecf20Sopenharmony_ci 39178c2ecf20Sopenharmony_cistatic struct pci_driver cy_pci_driver = { 39188c2ecf20Sopenharmony_ci .name = "cyclades", 39198c2ecf20Sopenharmony_ci .id_table = cy_pci_dev_id, 39208c2ecf20Sopenharmony_ci .probe = cy_pci_probe, 39218c2ecf20Sopenharmony_ci .remove = cy_pci_remove 39228c2ecf20Sopenharmony_ci}; 39238c2ecf20Sopenharmony_ci#endif 39248c2ecf20Sopenharmony_ci 39258c2ecf20Sopenharmony_cistatic int cyclades_proc_show(struct seq_file *m, void *v) 39268c2ecf20Sopenharmony_ci{ 39278c2ecf20Sopenharmony_ci struct cyclades_port *info; 39288c2ecf20Sopenharmony_ci unsigned int i, j; 39298c2ecf20Sopenharmony_ci __u32 cur_jifs = jiffies; 39308c2ecf20Sopenharmony_ci 39318c2ecf20Sopenharmony_ci seq_puts(m, "Dev TimeOpen BytesOut IdleOut BytesIn " 39328c2ecf20Sopenharmony_ci "IdleIn Overruns Ldisc\n"); 39338c2ecf20Sopenharmony_ci 39348c2ecf20Sopenharmony_ci /* Output one line for each known port */ 39358c2ecf20Sopenharmony_ci for (i = 0; i < NR_CARDS; i++) 39368c2ecf20Sopenharmony_ci for (j = 0; j < cy_card[i].nports; j++) { 39378c2ecf20Sopenharmony_ci info = &cy_card[i].ports[j]; 39388c2ecf20Sopenharmony_ci 39398c2ecf20Sopenharmony_ci if (info->port.count) { 39408c2ecf20Sopenharmony_ci /* XXX is the ldisc num worth this? */ 39418c2ecf20Sopenharmony_ci struct tty_struct *tty; 39428c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 39438c2ecf20Sopenharmony_ci int num = 0; 39448c2ecf20Sopenharmony_ci tty = tty_port_tty_get(&info->port); 39458c2ecf20Sopenharmony_ci if (tty) { 39468c2ecf20Sopenharmony_ci ld = tty_ldisc_ref(tty); 39478c2ecf20Sopenharmony_ci if (ld) { 39488c2ecf20Sopenharmony_ci num = ld->ops->num; 39498c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 39508c2ecf20Sopenharmony_ci } 39518c2ecf20Sopenharmony_ci tty_kref_put(tty); 39528c2ecf20Sopenharmony_ci } 39538c2ecf20Sopenharmony_ci seq_printf(m, "%3d %8lu %10lu %8lu " 39548c2ecf20Sopenharmony_ci "%10lu %8lu %9lu %6d\n", info->line, 39558c2ecf20Sopenharmony_ci (cur_jifs - info->idle_stats.in_use) / 39568c2ecf20Sopenharmony_ci HZ, info->idle_stats.xmit_bytes, 39578c2ecf20Sopenharmony_ci (cur_jifs - info->idle_stats.xmit_idle)/ 39588c2ecf20Sopenharmony_ci HZ, info->idle_stats.recv_bytes, 39598c2ecf20Sopenharmony_ci (cur_jifs - info->idle_stats.recv_idle)/ 39608c2ecf20Sopenharmony_ci HZ, info->idle_stats.overruns, 39618c2ecf20Sopenharmony_ci num); 39628c2ecf20Sopenharmony_ci } else 39638c2ecf20Sopenharmony_ci seq_printf(m, "%3d %8lu %10lu %8lu " 39648c2ecf20Sopenharmony_ci "%10lu %8lu %9lu %6ld\n", 39658c2ecf20Sopenharmony_ci info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); 39668c2ecf20Sopenharmony_ci } 39678c2ecf20Sopenharmony_ci return 0; 39688c2ecf20Sopenharmony_ci} 39698c2ecf20Sopenharmony_ci 39708c2ecf20Sopenharmony_ci/* The serial driver boot-time initialization code! 39718c2ecf20Sopenharmony_ci Hardware I/O ports are mapped to character special devices on a 39728c2ecf20Sopenharmony_ci first found, first allocated manner. That is, this code searches 39738c2ecf20Sopenharmony_ci for Cyclom cards in the system. As each is found, it is probed 39748c2ecf20Sopenharmony_ci to discover how many chips (and thus how many ports) are present. 39758c2ecf20Sopenharmony_ci These ports are mapped to the tty ports 32 and upward in monotonic 39768c2ecf20Sopenharmony_ci fashion. If an 8-port card is replaced with a 16-port card, the 39778c2ecf20Sopenharmony_ci port mapping on a following card will shift. 39788c2ecf20Sopenharmony_ci 39798c2ecf20Sopenharmony_ci This approach is different from what is used in the other serial 39808c2ecf20Sopenharmony_ci device driver because the Cyclom is more properly a multiplexer, 39818c2ecf20Sopenharmony_ci not just an aggregation of serial ports on one card. 39828c2ecf20Sopenharmony_ci 39838c2ecf20Sopenharmony_ci If there are more cards with more ports than have been 39848c2ecf20Sopenharmony_ci statically allocated above, a warning is printed and the 39858c2ecf20Sopenharmony_ci extra ports are ignored. 39868c2ecf20Sopenharmony_ci */ 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_cistatic const struct tty_operations cy_ops = { 39898c2ecf20Sopenharmony_ci .open = cy_open, 39908c2ecf20Sopenharmony_ci .close = cy_close, 39918c2ecf20Sopenharmony_ci .write = cy_write, 39928c2ecf20Sopenharmony_ci .put_char = cy_put_char, 39938c2ecf20Sopenharmony_ci .flush_chars = cy_flush_chars, 39948c2ecf20Sopenharmony_ci .write_room = cy_write_room, 39958c2ecf20Sopenharmony_ci .chars_in_buffer = cy_chars_in_buffer, 39968c2ecf20Sopenharmony_ci .flush_buffer = cy_flush_buffer, 39978c2ecf20Sopenharmony_ci .ioctl = cy_ioctl, 39988c2ecf20Sopenharmony_ci .throttle = cy_throttle, 39998c2ecf20Sopenharmony_ci .unthrottle = cy_unthrottle, 40008c2ecf20Sopenharmony_ci .set_termios = cy_set_termios, 40018c2ecf20Sopenharmony_ci .stop = cy_stop, 40028c2ecf20Sopenharmony_ci .start = cy_start, 40038c2ecf20Sopenharmony_ci .hangup = cy_hangup, 40048c2ecf20Sopenharmony_ci .break_ctl = cy_break, 40058c2ecf20Sopenharmony_ci .wait_until_sent = cy_wait_until_sent, 40068c2ecf20Sopenharmony_ci .tiocmget = cy_tiocmget, 40078c2ecf20Sopenharmony_ci .tiocmset = cy_tiocmset, 40088c2ecf20Sopenharmony_ci .get_icount = cy_get_icount, 40098c2ecf20Sopenharmony_ci .set_serial = cy_set_serial_info, 40108c2ecf20Sopenharmony_ci .get_serial = cy_get_serial_info, 40118c2ecf20Sopenharmony_ci .proc_show = cyclades_proc_show, 40128c2ecf20Sopenharmony_ci}; 40138c2ecf20Sopenharmony_ci 40148c2ecf20Sopenharmony_cistatic int __init cy_init(void) 40158c2ecf20Sopenharmony_ci{ 40168c2ecf20Sopenharmony_ci unsigned int nboards; 40178c2ecf20Sopenharmony_ci int retval = -ENOMEM; 40188c2ecf20Sopenharmony_ci 40198c2ecf20Sopenharmony_ci cy_serial_driver = alloc_tty_driver(NR_PORTS); 40208c2ecf20Sopenharmony_ci if (!cy_serial_driver) 40218c2ecf20Sopenharmony_ci goto err; 40228c2ecf20Sopenharmony_ci 40238c2ecf20Sopenharmony_ci printk(KERN_INFO "Cyclades driver " CY_VERSION "\n"); 40248c2ecf20Sopenharmony_ci 40258c2ecf20Sopenharmony_ci /* Initialize the tty_driver structure */ 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_ci cy_serial_driver->driver_name = "cyclades"; 40288c2ecf20Sopenharmony_ci cy_serial_driver->name = "ttyC"; 40298c2ecf20Sopenharmony_ci cy_serial_driver->major = CYCLADES_MAJOR; 40308c2ecf20Sopenharmony_ci cy_serial_driver->minor_start = 0; 40318c2ecf20Sopenharmony_ci cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; 40328c2ecf20Sopenharmony_ci cy_serial_driver->subtype = SERIAL_TYPE_NORMAL; 40338c2ecf20Sopenharmony_ci cy_serial_driver->init_termios = tty_std_termios; 40348c2ecf20Sopenharmony_ci cy_serial_driver->init_termios.c_cflag = 40358c2ecf20Sopenharmony_ci B9600 | CS8 | CREAD | HUPCL | CLOCAL; 40368c2ecf20Sopenharmony_ci cy_serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 40378c2ecf20Sopenharmony_ci tty_set_operations(cy_serial_driver, &cy_ops); 40388c2ecf20Sopenharmony_ci 40398c2ecf20Sopenharmony_ci retval = tty_register_driver(cy_serial_driver); 40408c2ecf20Sopenharmony_ci if (retval) { 40418c2ecf20Sopenharmony_ci printk(KERN_ERR "Couldn't register Cyclades serial driver\n"); 40428c2ecf20Sopenharmony_ci goto err_frtty; 40438c2ecf20Sopenharmony_ci } 40448c2ecf20Sopenharmony_ci 40458c2ecf20Sopenharmony_ci /* the code below is responsible to find the boards. Each different 40468c2ecf20Sopenharmony_ci type of board has its own detection routine. If a board is found, 40478c2ecf20Sopenharmony_ci the next cy_card structure available is set by the detection 40488c2ecf20Sopenharmony_ci routine. These functions are responsible for checking the 40498c2ecf20Sopenharmony_ci availability of cy_card and cy_port data structures and updating 40508c2ecf20Sopenharmony_ci the cy_next_channel. */ 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_ci /* look for isa boards */ 40538c2ecf20Sopenharmony_ci nboards = cy_detect_isa(); 40548c2ecf20Sopenharmony_ci 40558c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 40568c2ecf20Sopenharmony_ci /* look for pci boards */ 40578c2ecf20Sopenharmony_ci retval = pci_register_driver(&cy_pci_driver); 40588c2ecf20Sopenharmony_ci if (retval && !nboards) { 40598c2ecf20Sopenharmony_ci tty_unregister_driver(cy_serial_driver); 40608c2ecf20Sopenharmony_ci goto err_frtty; 40618c2ecf20Sopenharmony_ci } 40628c2ecf20Sopenharmony_ci#endif 40638c2ecf20Sopenharmony_ci 40648c2ecf20Sopenharmony_ci return 0; 40658c2ecf20Sopenharmony_cierr_frtty: 40668c2ecf20Sopenharmony_ci put_tty_driver(cy_serial_driver); 40678c2ecf20Sopenharmony_cierr: 40688c2ecf20Sopenharmony_ci return retval; 40698c2ecf20Sopenharmony_ci} /* cy_init */ 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_cistatic void __exit cy_cleanup_module(void) 40728c2ecf20Sopenharmony_ci{ 40738c2ecf20Sopenharmony_ci struct cyclades_card *card; 40748c2ecf20Sopenharmony_ci unsigned int i, e1; 40758c2ecf20Sopenharmony_ci 40768c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR 40778c2ecf20Sopenharmony_ci del_timer_sync(&cyz_timerlist); 40788c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 40798c2ecf20Sopenharmony_ci 40808c2ecf20Sopenharmony_ci e1 = tty_unregister_driver(cy_serial_driver); 40818c2ecf20Sopenharmony_ci if (e1) 40828c2ecf20Sopenharmony_ci printk(KERN_ERR "failed to unregister Cyclades serial " 40838c2ecf20Sopenharmony_ci "driver(%d)\n", e1); 40848c2ecf20Sopenharmony_ci 40858c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 40868c2ecf20Sopenharmony_ci pci_unregister_driver(&cy_pci_driver); 40878c2ecf20Sopenharmony_ci#endif 40888c2ecf20Sopenharmony_ci 40898c2ecf20Sopenharmony_ci for (i = 0; i < NR_CARDS; i++) { 40908c2ecf20Sopenharmony_ci card = &cy_card[i]; 40918c2ecf20Sopenharmony_ci if (card->base_addr) { 40928c2ecf20Sopenharmony_ci /* clear interrupt */ 40938c2ecf20Sopenharmony_ci cy_writeb(card->base_addr + Cy_ClrIntr, 0); 40948c2ecf20Sopenharmony_ci iounmap(card->base_addr); 40958c2ecf20Sopenharmony_ci if (card->ctl_addr.p9050) 40968c2ecf20Sopenharmony_ci iounmap(card->ctl_addr.p9050); 40978c2ecf20Sopenharmony_ci if (card->irq 40988c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR 40998c2ecf20Sopenharmony_ci && !cy_is_Z(card) 41008c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */ 41018c2ecf20Sopenharmony_ci ) 41028c2ecf20Sopenharmony_ci free_irq(card->irq, card); 41038c2ecf20Sopenharmony_ci for (e1 = card->first_line; e1 < card->first_line + 41048c2ecf20Sopenharmony_ci card->nports; e1++) 41058c2ecf20Sopenharmony_ci tty_unregister_device(cy_serial_driver, e1); 41068c2ecf20Sopenharmony_ci kfree(card->ports); 41078c2ecf20Sopenharmony_ci } 41088c2ecf20Sopenharmony_ci } 41098c2ecf20Sopenharmony_ci 41108c2ecf20Sopenharmony_ci put_tty_driver(cy_serial_driver); 41118c2ecf20Sopenharmony_ci} /* cy_cleanup_module */ 41128c2ecf20Sopenharmony_ci 41138c2ecf20Sopenharmony_cimodule_init(cy_init); 41148c2ecf20Sopenharmony_cimodule_exit(cy_cleanup_module); 41158c2ecf20Sopenharmony_ci 41168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 41178c2ecf20Sopenharmony_ciMODULE_VERSION(CY_VERSION); 41188c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR); 41198c2ecf20Sopenharmony_ciMODULE_FIRMWARE("cyzfirm.bin"); 4120