18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Device driver for Microgate SyncLink GT serial adapters. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * written by Paul Fulghum for Microgate Corporation 68c2ecf20Sopenharmony_ci * paulkf@microgate.com 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Microgate and SyncLink are trademarks of Microgate Corporation 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 118c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 128c2ecf20Sopenharmony_ci * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 138c2ecf20Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 148c2ecf20Sopenharmony_ci * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 158c2ecf20Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 168c2ecf20Sopenharmony_ci * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 178c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 188c2ecf20Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 198c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 208c2ecf20Sopenharmony_ci * OF THE POSSIBILITY OF SUCH DAMAGE. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * DEBUG OUTPUT DEFINITIONS 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * uncomment lines below to enable specific types of debug output 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * DBGINFO information - most verbose output 298c2ecf20Sopenharmony_ci * DBGERR serious errors 308c2ecf20Sopenharmony_ci * DBGBH bottom half service routine debugging 318c2ecf20Sopenharmony_ci * DBGISR interrupt service routine debugging 328c2ecf20Sopenharmony_ci * DBGDATA output receive and transmit data 338c2ecf20Sopenharmony_ci * DBGTBUF output transmit DMA buffers and registers 348c2ecf20Sopenharmony_ci * DBGRBUF output receive DMA buffers and registers 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt 388c2ecf20Sopenharmony_ci#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt 398c2ecf20Sopenharmony_ci#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt 408c2ecf20Sopenharmony_ci#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt 418c2ecf20Sopenharmony_ci#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label)) 428c2ecf20Sopenharmony_ci/*#define DBGTBUF(info) dump_tbufs(info)*/ 438c2ecf20Sopenharmony_ci/*#define DBGRBUF(info) dump_rbufs(info)*/ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include <linux/module.h> 478c2ecf20Sopenharmony_ci#include <linux/errno.h> 488c2ecf20Sopenharmony_ci#include <linux/signal.h> 498c2ecf20Sopenharmony_ci#include <linux/sched.h> 508c2ecf20Sopenharmony_ci#include <linux/timer.h> 518c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 528c2ecf20Sopenharmony_ci#include <linux/pci.h> 538c2ecf20Sopenharmony_ci#include <linux/tty.h> 548c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 558c2ecf20Sopenharmony_ci#include <linux/serial.h> 568c2ecf20Sopenharmony_ci#include <linux/major.h> 578c2ecf20Sopenharmony_ci#include <linux/string.h> 588c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 598c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 608c2ecf20Sopenharmony_ci#include <linux/ioport.h> 618c2ecf20Sopenharmony_ci#include <linux/mm.h> 628c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 638c2ecf20Sopenharmony_ci#include <linux/slab.h> 648c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 658c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 668c2ecf20Sopenharmony_ci#include <linux/init.h> 678c2ecf20Sopenharmony_ci#include <linux/delay.h> 688c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 698c2ecf20Sopenharmony_ci#include <linux/termios.h> 708c2ecf20Sopenharmony_ci#include <linux/bitops.h> 718c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 728c2ecf20Sopenharmony_ci#include <linux/hdlc.h> 738c2ecf20Sopenharmony_ci#include <linux/synclink.h> 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#include <asm/io.h> 768c2ecf20Sopenharmony_ci#include <asm/irq.h> 778c2ecf20Sopenharmony_ci#include <asm/dma.h> 788c2ecf20Sopenharmony_ci#include <asm/types.h> 798c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE)) 828c2ecf20Sopenharmony_ci#define SYNCLINK_GENERIC_HDLC 1 838c2ecf20Sopenharmony_ci#else 848c2ecf20Sopenharmony_ci#define SYNCLINK_GENERIC_HDLC 0 858c2ecf20Sopenharmony_ci#endif 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * module identification 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistatic char *driver_name = "SyncLink GT"; 918c2ecf20Sopenharmony_cistatic char *slgt_driver_name = "synclink_gt"; 928c2ecf20Sopenharmony_cistatic char *tty_dev_prefix = "ttySLG"; 938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 948c2ecf20Sopenharmony_ci#define MGSL_MAGIC 0x5401 958c2ecf20Sopenharmony_ci#define MAX_DEVICES 32 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic const struct pci_device_id pci_table[] = { 988c2ecf20Sopenharmony_ci {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, 998c2ecf20Sopenharmony_ci {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT2_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, 1008c2ecf20Sopenharmony_ci {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT4_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, 1018c2ecf20Sopenharmony_ci {PCI_VENDOR_ID_MICROGATE, SYNCLINK_AC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, 1028c2ecf20Sopenharmony_ci {0,}, /* terminate list */ 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_table); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int init_one(struct pci_dev *dev,const struct pci_device_id *ent); 1078c2ecf20Sopenharmony_cistatic void remove_one(struct pci_dev *dev); 1088c2ecf20Sopenharmony_cistatic struct pci_driver pci_driver = { 1098c2ecf20Sopenharmony_ci .name = "synclink_gt", 1108c2ecf20Sopenharmony_ci .id_table = pci_table, 1118c2ecf20Sopenharmony_ci .probe = init_one, 1128c2ecf20Sopenharmony_ci .remove = remove_one, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic bool pci_registered; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * module configuration and status 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic struct slgt_info *slgt_device_list; 1218c2ecf20Sopenharmony_cistatic int slgt_device_count; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int ttymajor; 1248c2ecf20Sopenharmony_cistatic int debug_level; 1258c2ecf20Sopenharmony_cistatic int maxframe[MAX_DEVICES]; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cimodule_param(ttymajor, int, 0); 1288c2ecf20Sopenharmony_cimodule_param(debug_level, int, 0); 1298c2ecf20Sopenharmony_cimodule_param_array(maxframe, int, NULL, 0); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned"); 1328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail"); 1338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)"); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * tty support and callbacks 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic struct tty_driver *serial_driver; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void wait_until_sent(struct tty_struct *tty, int timeout); 1418c2ecf20Sopenharmony_cistatic void flush_buffer(struct tty_struct *tty); 1428c2ecf20Sopenharmony_cistatic void tx_release(struct tty_struct *tty); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * generic HDLC support 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci#define dev_to_port(D) (dev_to_hdlc(D)->priv) 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/* 1518c2ecf20Sopenharmony_ci * device specific structures, macros and functions 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define SLGT_MAX_PORTS 4 1558c2ecf20Sopenharmony_ci#define SLGT_REG_SIZE 256 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * conditional wait facility 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_cistruct cond_wait { 1618c2ecf20Sopenharmony_ci struct cond_wait *next; 1628c2ecf20Sopenharmony_ci wait_queue_head_t q; 1638c2ecf20Sopenharmony_ci wait_queue_entry_t wait; 1648c2ecf20Sopenharmony_ci unsigned int data; 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_cistatic void flush_cond_wait(struct cond_wait **head); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * DMA buffer descriptor and access macros 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_cistruct slgt_desc 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci __le16 count; 1748c2ecf20Sopenharmony_ci __le16 status; 1758c2ecf20Sopenharmony_ci __le32 pbuf; /* physical address of data buffer */ 1768c2ecf20Sopenharmony_ci __le32 next; /* physical address of next descriptor */ 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* driver book keeping */ 1798c2ecf20Sopenharmony_ci char *buf; /* virtual address of data buffer */ 1808c2ecf20Sopenharmony_ci unsigned int pdesc; /* physical address of this descriptor */ 1818c2ecf20Sopenharmony_ci dma_addr_t buf_dma_addr; 1828c2ecf20Sopenharmony_ci unsigned short buf_count; 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b)) 1868c2ecf20Sopenharmony_ci#define set_desc_next(a,b) (a).next = cpu_to_le32((unsigned int)(b)) 1878c2ecf20Sopenharmony_ci#define set_desc_count(a,b)(a).count = cpu_to_le16((unsigned short)(b)) 1888c2ecf20Sopenharmony_ci#define set_desc_eof(a,b) (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0)) 1898c2ecf20Sopenharmony_ci#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b)) 1908c2ecf20Sopenharmony_ci#define desc_count(a) (le16_to_cpu((a).count)) 1918c2ecf20Sopenharmony_ci#define desc_status(a) (le16_to_cpu((a).status)) 1928c2ecf20Sopenharmony_ci#define desc_complete(a) (le16_to_cpu((a).status) & BIT15) 1938c2ecf20Sopenharmony_ci#define desc_eof(a) (le16_to_cpu((a).status) & BIT2) 1948c2ecf20Sopenharmony_ci#define desc_crc_error(a) (le16_to_cpu((a).status) & BIT1) 1958c2ecf20Sopenharmony_ci#define desc_abort(a) (le16_to_cpu((a).status) & BIT0) 1968c2ecf20Sopenharmony_ci#define desc_residue(a) ((le16_to_cpu((a).status) & 0x38) >> 3) 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistruct _input_signal_events { 1998c2ecf20Sopenharmony_ci int ri_up; 2008c2ecf20Sopenharmony_ci int ri_down; 2018c2ecf20Sopenharmony_ci int dsr_up; 2028c2ecf20Sopenharmony_ci int dsr_down; 2038c2ecf20Sopenharmony_ci int dcd_up; 2048c2ecf20Sopenharmony_ci int dcd_down; 2058c2ecf20Sopenharmony_ci int cts_up; 2068c2ecf20Sopenharmony_ci int cts_down; 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * device instance data structure 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistruct slgt_info { 2138c2ecf20Sopenharmony_ci void *if_ptr; /* General purpose pointer (used by SPPP) */ 2148c2ecf20Sopenharmony_ci struct tty_port port; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci struct slgt_info *next_device; /* device list link */ 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci int magic; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci char device_name[25]; 2218c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci int port_count; /* count of ports on adapter */ 2248c2ecf20Sopenharmony_ci int adapter_num; /* adapter instance number */ 2258c2ecf20Sopenharmony_ci int port_num; /* port instance number */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* array of pointers to port contexts on this adapter */ 2288c2ecf20Sopenharmony_ci struct slgt_info *port_array[SLGT_MAX_PORTS]; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci int line; /* tty line instance number */ 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci struct mgsl_icount icount; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci int timeout; 2358c2ecf20Sopenharmony_ci int x_char; /* xon/xoff character */ 2368c2ecf20Sopenharmony_ci unsigned int read_status_mask; 2378c2ecf20Sopenharmony_ci unsigned int ignore_status_mask; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci wait_queue_head_t status_event_wait_q; 2408c2ecf20Sopenharmony_ci wait_queue_head_t event_wait_q; 2418c2ecf20Sopenharmony_ci struct timer_list tx_timer; 2428c2ecf20Sopenharmony_ci struct timer_list rx_timer; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci unsigned int gpio_present; 2458c2ecf20Sopenharmony_ci struct cond_wait *gpio_wait_q; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci spinlock_t lock; /* spinlock for synchronizing with ISR */ 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci struct work_struct task; 2508c2ecf20Sopenharmony_ci u32 pending_bh; 2518c2ecf20Sopenharmony_ci bool bh_requested; 2528c2ecf20Sopenharmony_ci bool bh_running; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci int isr_overflow; 2558c2ecf20Sopenharmony_ci bool irq_requested; /* true if IRQ requested */ 2568c2ecf20Sopenharmony_ci bool irq_occurred; /* for diagnostics use */ 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* device configuration */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci unsigned int bus_type; 2618c2ecf20Sopenharmony_ci unsigned int irq_level; 2628c2ecf20Sopenharmony_ci unsigned long irq_flags; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci unsigned char __iomem * reg_addr; /* memory mapped registers address */ 2658c2ecf20Sopenharmony_ci u32 phys_reg_addr; 2668c2ecf20Sopenharmony_ci bool reg_addr_requested; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci MGSL_PARAMS params; /* communications parameters */ 2698c2ecf20Sopenharmony_ci u32 idle_mode; 2708c2ecf20Sopenharmony_ci u32 max_frame_size; /* as set by device config */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci unsigned int rbuf_fill_level; 2738c2ecf20Sopenharmony_ci unsigned int rx_pio; 2748c2ecf20Sopenharmony_ci unsigned int if_mode; 2758c2ecf20Sopenharmony_ci unsigned int base_clock; 2768c2ecf20Sopenharmony_ci unsigned int xsync; 2778c2ecf20Sopenharmony_ci unsigned int xctrl; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* device status */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci bool rx_enabled; 2828c2ecf20Sopenharmony_ci bool rx_restart; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci bool tx_enabled; 2858c2ecf20Sopenharmony_ci bool tx_active; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci unsigned char signals; /* serial signal states */ 2888c2ecf20Sopenharmony_ci int init_error; /* initialization error */ 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci unsigned char *tx_buf; 2918c2ecf20Sopenharmony_ci int tx_count; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci char *flag_buf; 2948c2ecf20Sopenharmony_ci bool drop_rts_on_tx_done; 2958c2ecf20Sopenharmony_ci struct _input_signal_events input_signal_events; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci int dcd_chkcount; /* check counts to prevent */ 2988c2ecf20Sopenharmony_ci int cts_chkcount; /* too many IRQs if a signal */ 2998c2ecf20Sopenharmony_ci int dsr_chkcount; /* is floating */ 3008c2ecf20Sopenharmony_ci int ri_chkcount; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci char *bufs; /* virtual address of DMA buffer lists */ 3038c2ecf20Sopenharmony_ci dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */ 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci unsigned int rbuf_count; 3068c2ecf20Sopenharmony_ci struct slgt_desc *rbufs; 3078c2ecf20Sopenharmony_ci unsigned int rbuf_current; 3088c2ecf20Sopenharmony_ci unsigned int rbuf_index; 3098c2ecf20Sopenharmony_ci unsigned int rbuf_fill_index; 3108c2ecf20Sopenharmony_ci unsigned short rbuf_fill_count; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci unsigned int tbuf_count; 3138c2ecf20Sopenharmony_ci struct slgt_desc *tbufs; 3148c2ecf20Sopenharmony_ci unsigned int tbuf_current; 3158c2ecf20Sopenharmony_ci unsigned int tbuf_start; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci unsigned char *tmp_rbuf; 3188c2ecf20Sopenharmony_ci unsigned int tmp_rbuf_count; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* SPPP/Cisco HDLC device parts */ 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci int netcount; 3238c2ecf20Sopenharmony_ci spinlock_t netlock; 3248c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 3258c2ecf20Sopenharmony_ci struct net_device *netdev; 3268c2ecf20Sopenharmony_ci#endif 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci}; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic MGSL_PARAMS default_params = { 3318c2ecf20Sopenharmony_ci .mode = MGSL_MODE_HDLC, 3328c2ecf20Sopenharmony_ci .loopback = 0, 3338c2ecf20Sopenharmony_ci .flags = HDLC_FLAG_UNDERRUN_ABORT15, 3348c2ecf20Sopenharmony_ci .encoding = HDLC_ENCODING_NRZI_SPACE, 3358c2ecf20Sopenharmony_ci .clock_speed = 0, 3368c2ecf20Sopenharmony_ci .addr_filter = 0xff, 3378c2ecf20Sopenharmony_ci .crc_type = HDLC_CRC_16_CCITT, 3388c2ecf20Sopenharmony_ci .preamble_length = HDLC_PREAMBLE_LENGTH_8BITS, 3398c2ecf20Sopenharmony_ci .preamble = HDLC_PREAMBLE_PATTERN_NONE, 3408c2ecf20Sopenharmony_ci .data_rate = 9600, 3418c2ecf20Sopenharmony_ci .data_bits = 8, 3428c2ecf20Sopenharmony_ci .stop_bits = 1, 3438c2ecf20Sopenharmony_ci .parity = ASYNC_PARITY_NONE 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci#define BH_RECEIVE 1 3488c2ecf20Sopenharmony_ci#define BH_TRANSMIT 2 3498c2ecf20Sopenharmony_ci#define BH_STATUS 4 3508c2ecf20Sopenharmony_ci#define IO_PIN_SHUTDOWN_LIMIT 100 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci#define DMABUFSIZE 256 3538c2ecf20Sopenharmony_ci#define DESC_LIST_SIZE 4096 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci#define MASK_PARITY BIT1 3568c2ecf20Sopenharmony_ci#define MASK_FRAMING BIT0 3578c2ecf20Sopenharmony_ci#define MASK_BREAK BIT14 3588c2ecf20Sopenharmony_ci#define MASK_OVERRUN BIT4 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci#define GSR 0x00 /* global status */ 3618c2ecf20Sopenharmony_ci#define JCR 0x04 /* JTAG control */ 3628c2ecf20Sopenharmony_ci#define IODR 0x08 /* GPIO direction */ 3638c2ecf20Sopenharmony_ci#define IOER 0x0c /* GPIO interrupt enable */ 3648c2ecf20Sopenharmony_ci#define IOVR 0x10 /* GPIO value */ 3658c2ecf20Sopenharmony_ci#define IOSR 0x14 /* GPIO interrupt status */ 3668c2ecf20Sopenharmony_ci#define TDR 0x80 /* tx data */ 3678c2ecf20Sopenharmony_ci#define RDR 0x80 /* rx data */ 3688c2ecf20Sopenharmony_ci#define TCR 0x82 /* tx control */ 3698c2ecf20Sopenharmony_ci#define TIR 0x84 /* tx idle */ 3708c2ecf20Sopenharmony_ci#define TPR 0x85 /* tx preamble */ 3718c2ecf20Sopenharmony_ci#define RCR 0x86 /* rx control */ 3728c2ecf20Sopenharmony_ci#define VCR 0x88 /* V.24 control */ 3738c2ecf20Sopenharmony_ci#define CCR 0x89 /* clock control */ 3748c2ecf20Sopenharmony_ci#define BDR 0x8a /* baud divisor */ 3758c2ecf20Sopenharmony_ci#define SCR 0x8c /* serial control */ 3768c2ecf20Sopenharmony_ci#define SSR 0x8e /* serial status */ 3778c2ecf20Sopenharmony_ci#define RDCSR 0x90 /* rx DMA control/status */ 3788c2ecf20Sopenharmony_ci#define TDCSR 0x94 /* tx DMA control/status */ 3798c2ecf20Sopenharmony_ci#define RDDAR 0x98 /* rx DMA descriptor address */ 3808c2ecf20Sopenharmony_ci#define TDDAR 0x9c /* tx DMA descriptor address */ 3818c2ecf20Sopenharmony_ci#define XSR 0x40 /* extended sync pattern */ 3828c2ecf20Sopenharmony_ci#define XCR 0x44 /* extended control */ 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci#define RXIDLE BIT14 3858c2ecf20Sopenharmony_ci#define RXBREAK BIT14 3868c2ecf20Sopenharmony_ci#define IRQ_TXDATA BIT13 3878c2ecf20Sopenharmony_ci#define IRQ_TXIDLE BIT12 3888c2ecf20Sopenharmony_ci#define IRQ_TXUNDER BIT11 /* HDLC */ 3898c2ecf20Sopenharmony_ci#define IRQ_RXDATA BIT10 3908c2ecf20Sopenharmony_ci#define IRQ_RXIDLE BIT9 /* HDLC */ 3918c2ecf20Sopenharmony_ci#define IRQ_RXBREAK BIT9 /* async */ 3928c2ecf20Sopenharmony_ci#define IRQ_RXOVER BIT8 3938c2ecf20Sopenharmony_ci#define IRQ_DSR BIT7 3948c2ecf20Sopenharmony_ci#define IRQ_CTS BIT6 3958c2ecf20Sopenharmony_ci#define IRQ_DCD BIT5 3968c2ecf20Sopenharmony_ci#define IRQ_RI BIT4 3978c2ecf20Sopenharmony_ci#define IRQ_ALL 0x3ff0 3988c2ecf20Sopenharmony_ci#define IRQ_MASTER BIT0 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci#define slgt_irq_on(info, mask) \ 4018c2ecf20Sopenharmony_ci wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask))) 4028c2ecf20Sopenharmony_ci#define slgt_irq_off(info, mask) \ 4038c2ecf20Sopenharmony_ci wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask))) 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic __u8 rd_reg8(struct slgt_info *info, unsigned int addr); 4068c2ecf20Sopenharmony_cistatic void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value); 4078c2ecf20Sopenharmony_cistatic __u16 rd_reg16(struct slgt_info *info, unsigned int addr); 4088c2ecf20Sopenharmony_cistatic void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value); 4098c2ecf20Sopenharmony_cistatic __u32 rd_reg32(struct slgt_info *info, unsigned int addr); 4108c2ecf20Sopenharmony_cistatic void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void msc_set_vcr(struct slgt_info *info); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int startup(struct slgt_info *info); 4158c2ecf20Sopenharmony_cistatic int block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info); 4168c2ecf20Sopenharmony_cistatic void shutdown(struct slgt_info *info); 4178c2ecf20Sopenharmony_cistatic void program_hw(struct slgt_info *info); 4188c2ecf20Sopenharmony_cistatic void change_params(struct slgt_info *info); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int adapter_test(struct slgt_info *info); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void reset_port(struct slgt_info *info); 4238c2ecf20Sopenharmony_cistatic void async_mode(struct slgt_info *info); 4248c2ecf20Sopenharmony_cistatic void sync_mode(struct slgt_info *info); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void rx_stop(struct slgt_info *info); 4278c2ecf20Sopenharmony_cistatic void rx_start(struct slgt_info *info); 4288c2ecf20Sopenharmony_cistatic void reset_rbufs(struct slgt_info *info); 4298c2ecf20Sopenharmony_cistatic void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last); 4308c2ecf20Sopenharmony_cistatic bool rx_get_frame(struct slgt_info *info); 4318c2ecf20Sopenharmony_cistatic bool rx_get_buf(struct slgt_info *info); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void tx_start(struct slgt_info *info); 4348c2ecf20Sopenharmony_cistatic void tx_stop(struct slgt_info *info); 4358c2ecf20Sopenharmony_cistatic void tx_set_idle(struct slgt_info *info); 4368c2ecf20Sopenharmony_cistatic unsigned int tbuf_bytes(struct slgt_info *info); 4378c2ecf20Sopenharmony_cistatic void reset_tbufs(struct slgt_info *info); 4388c2ecf20Sopenharmony_cistatic void tdma_reset(struct slgt_info *info); 4398c2ecf20Sopenharmony_cistatic bool tx_load(struct slgt_info *info, const char *buf, unsigned int count); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic void get_gtsignals(struct slgt_info *info); 4428c2ecf20Sopenharmony_cistatic void set_gtsignals(struct slgt_info *info); 4438c2ecf20Sopenharmony_cistatic void set_rate(struct slgt_info *info, u32 data_rate); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic void bh_transmit(struct slgt_info *info); 4468c2ecf20Sopenharmony_cistatic void isr_txeom(struct slgt_info *info, unsigned short status); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void tx_timeout(struct timer_list *t); 4498c2ecf20Sopenharmony_cistatic void rx_timeout(struct timer_list *t); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/* 4528c2ecf20Sopenharmony_ci * ioctl handlers 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount); 4558c2ecf20Sopenharmony_cistatic int get_params(struct slgt_info *info, MGSL_PARAMS __user *params); 4568c2ecf20Sopenharmony_cistatic int set_params(struct slgt_info *info, MGSL_PARAMS __user *params); 4578c2ecf20Sopenharmony_cistatic int get_txidle(struct slgt_info *info, int __user *idle_mode); 4588c2ecf20Sopenharmony_cistatic int set_txidle(struct slgt_info *info, int idle_mode); 4598c2ecf20Sopenharmony_cistatic int tx_enable(struct slgt_info *info, int enable); 4608c2ecf20Sopenharmony_cistatic int tx_abort(struct slgt_info *info); 4618c2ecf20Sopenharmony_cistatic int rx_enable(struct slgt_info *info, int enable); 4628c2ecf20Sopenharmony_cistatic int modem_input_wait(struct slgt_info *info,int arg); 4638c2ecf20Sopenharmony_cistatic int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); 4648c2ecf20Sopenharmony_cistatic int get_interface(struct slgt_info *info, int __user *if_mode); 4658c2ecf20Sopenharmony_cistatic int set_interface(struct slgt_info *info, int if_mode); 4668c2ecf20Sopenharmony_cistatic int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); 4678c2ecf20Sopenharmony_cistatic int get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); 4688c2ecf20Sopenharmony_cistatic int wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); 4698c2ecf20Sopenharmony_cistatic int get_xsync(struct slgt_info *info, int __user *if_mode); 4708c2ecf20Sopenharmony_cistatic int set_xsync(struct slgt_info *info, int if_mode); 4718c2ecf20Sopenharmony_cistatic int get_xctrl(struct slgt_info *info, int __user *if_mode); 4728c2ecf20Sopenharmony_cistatic int set_xctrl(struct slgt_info *info, int if_mode); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/* 4758c2ecf20Sopenharmony_ci * driver functions 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_cistatic void release_resources(struct slgt_info *info); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* 4808c2ecf20Sopenharmony_ci * DEBUG OUTPUT CODE 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ci#ifndef DBGINFO 4838c2ecf20Sopenharmony_ci#define DBGINFO(fmt) 4848c2ecf20Sopenharmony_ci#endif 4858c2ecf20Sopenharmony_ci#ifndef DBGERR 4868c2ecf20Sopenharmony_ci#define DBGERR(fmt) 4878c2ecf20Sopenharmony_ci#endif 4888c2ecf20Sopenharmony_ci#ifndef DBGBH 4898c2ecf20Sopenharmony_ci#define DBGBH(fmt) 4908c2ecf20Sopenharmony_ci#endif 4918c2ecf20Sopenharmony_ci#ifndef DBGISR 4928c2ecf20Sopenharmony_ci#define DBGISR(fmt) 4938c2ecf20Sopenharmony_ci#endif 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci#ifdef DBGDATA 4968c2ecf20Sopenharmony_cistatic void trace_block(struct slgt_info *info, const char *data, int count, const char *label) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci int i; 4998c2ecf20Sopenharmony_ci int linecount; 5008c2ecf20Sopenharmony_ci printk("%s %s data:\n",info->device_name, label); 5018c2ecf20Sopenharmony_ci while(count) { 5028c2ecf20Sopenharmony_ci linecount = (count > 16) ? 16 : count; 5038c2ecf20Sopenharmony_ci for(i=0; i < linecount; i++) 5048c2ecf20Sopenharmony_ci printk("%02X ",(unsigned char)data[i]); 5058c2ecf20Sopenharmony_ci for(;i<17;i++) 5068c2ecf20Sopenharmony_ci printk(" "); 5078c2ecf20Sopenharmony_ci for(i=0;i<linecount;i++) { 5088c2ecf20Sopenharmony_ci if (data[i]>=040 && data[i]<=0176) 5098c2ecf20Sopenharmony_ci printk("%c",data[i]); 5108c2ecf20Sopenharmony_ci else 5118c2ecf20Sopenharmony_ci printk("."); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci printk("\n"); 5148c2ecf20Sopenharmony_ci data += linecount; 5158c2ecf20Sopenharmony_ci count -= linecount; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci#else 5198c2ecf20Sopenharmony_ci#define DBGDATA(info, buf, size, label) 5208c2ecf20Sopenharmony_ci#endif 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci#ifdef DBGTBUF 5238c2ecf20Sopenharmony_cistatic void dump_tbufs(struct slgt_info *info) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci int i; 5268c2ecf20Sopenharmony_ci printk("tbuf_current=%d\n", info->tbuf_current); 5278c2ecf20Sopenharmony_ci for (i=0 ; i < info->tbuf_count ; i++) { 5288c2ecf20Sopenharmony_ci printk("%d: count=%04X status=%04X\n", 5298c2ecf20Sopenharmony_ci i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status)); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci#else 5338c2ecf20Sopenharmony_ci#define DBGTBUF(info) 5348c2ecf20Sopenharmony_ci#endif 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci#ifdef DBGRBUF 5378c2ecf20Sopenharmony_cistatic void dump_rbufs(struct slgt_info *info) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci int i; 5408c2ecf20Sopenharmony_ci printk("rbuf_current=%d\n", info->rbuf_current); 5418c2ecf20Sopenharmony_ci for (i=0 ; i < info->rbuf_count ; i++) { 5428c2ecf20Sopenharmony_ci printk("%d: count=%04X status=%04X\n", 5438c2ecf20Sopenharmony_ci i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status)); 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci#else 5478c2ecf20Sopenharmony_ci#define DBGRBUF(info) 5488c2ecf20Sopenharmony_ci#endif 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic inline int sanity_check(struct slgt_info *info, char *devname, const char *name) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci#ifdef SANITY_CHECK 5538c2ecf20Sopenharmony_ci if (!info) { 5548c2ecf20Sopenharmony_ci printk("null struct slgt_info for (%s) in %s\n", devname, name); 5558c2ecf20Sopenharmony_ci return 1; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci if (info->magic != MGSL_MAGIC) { 5588c2ecf20Sopenharmony_ci printk("bad magic number struct slgt_info (%s) in %s\n", devname, name); 5598c2ecf20Sopenharmony_ci return 1; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci#else 5628c2ecf20Sopenharmony_ci if (!info) 5638c2ecf20Sopenharmony_ci return 1; 5648c2ecf20Sopenharmony_ci#endif 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci/** 5698c2ecf20Sopenharmony_ci * line discipline callback wrappers 5708c2ecf20Sopenharmony_ci * 5718c2ecf20Sopenharmony_ci * The wrappers maintain line discipline references 5728c2ecf20Sopenharmony_ci * while calling into the line discipline. 5738c2ecf20Sopenharmony_ci * 5748c2ecf20Sopenharmony_ci * ldisc_receive_buf - pass receive data to line discipline 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_cistatic void ldisc_receive_buf(struct tty_struct *tty, 5778c2ecf20Sopenharmony_ci const __u8 *data, char *flags, int count) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 5808c2ecf20Sopenharmony_ci if (!tty) 5818c2ecf20Sopenharmony_ci return; 5828c2ecf20Sopenharmony_ci ld = tty_ldisc_ref(tty); 5838c2ecf20Sopenharmony_ci if (ld) { 5848c2ecf20Sopenharmony_ci if (ld->ops->receive_buf) 5858c2ecf20Sopenharmony_ci ld->ops->receive_buf(tty, data, flags, count); 5868c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* tty callbacks */ 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic int open(struct tty_struct *tty, struct file *filp) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct slgt_info *info; 5958c2ecf20Sopenharmony_ci int retval, line; 5968c2ecf20Sopenharmony_ci unsigned long flags; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci line = tty->index; 5998c2ecf20Sopenharmony_ci if (line >= slgt_device_count) { 6008c2ecf20Sopenharmony_ci DBGERR(("%s: open with invalid line #%d.\n", driver_name, line)); 6018c2ecf20Sopenharmony_ci return -ENODEV; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci info = slgt_device_list; 6058c2ecf20Sopenharmony_ci while(info && info->line != line) 6068c2ecf20Sopenharmony_ci info = info->next_device; 6078c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "open")) 6088c2ecf20Sopenharmony_ci return -ENODEV; 6098c2ecf20Sopenharmony_ci if (info->init_error) { 6108c2ecf20Sopenharmony_ci DBGERR(("%s init error=%d\n", info->device_name, info->init_error)); 6118c2ecf20Sopenharmony_ci return -ENODEV; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci tty->driver_data = info; 6158c2ecf20Sopenharmony_ci info->port.tty = tty; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count)); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci mutex_lock(&info->port.mutex); 6208c2ecf20Sopenharmony_ci info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 6238c2ecf20Sopenharmony_ci if (info->netcount) { 6248c2ecf20Sopenharmony_ci retval = -EBUSY; 6258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 6268c2ecf20Sopenharmony_ci mutex_unlock(&info->port.mutex); 6278c2ecf20Sopenharmony_ci goto cleanup; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci info->port.count++; 6308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (info->port.count == 1) { 6338c2ecf20Sopenharmony_ci /* 1st open on this device, init hardware */ 6348c2ecf20Sopenharmony_ci retval = startup(info); 6358c2ecf20Sopenharmony_ci if (retval < 0) { 6368c2ecf20Sopenharmony_ci mutex_unlock(&info->port.mutex); 6378c2ecf20Sopenharmony_ci goto cleanup; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci mutex_unlock(&info->port.mutex); 6418c2ecf20Sopenharmony_ci retval = block_til_ready(tty, filp, info); 6428c2ecf20Sopenharmony_ci if (retval) { 6438c2ecf20Sopenharmony_ci DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval)); 6448c2ecf20Sopenharmony_ci goto cleanup; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci retval = 0; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cicleanup: 6508c2ecf20Sopenharmony_ci if (retval) { 6518c2ecf20Sopenharmony_ci if (tty->count == 1) 6528c2ecf20Sopenharmony_ci info->port.tty = NULL; /* tty layer will release tty struct */ 6538c2ecf20Sopenharmony_ci if(info->port.count) 6548c2ecf20Sopenharmony_ci info->port.count--; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci DBGINFO(("%s open rc=%d\n", info->device_name, retval)); 6588c2ecf20Sopenharmony_ci return retval; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic void close(struct tty_struct *tty, struct file *filp) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "close")) 6668c2ecf20Sopenharmony_ci return; 6678c2ecf20Sopenharmony_ci DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count)); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (tty_port_close_start(&info->port, tty, filp) == 0) 6708c2ecf20Sopenharmony_ci goto cleanup; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci mutex_lock(&info->port.mutex); 6738c2ecf20Sopenharmony_ci if (tty_port_initialized(&info->port)) 6748c2ecf20Sopenharmony_ci wait_until_sent(tty, info->timeout); 6758c2ecf20Sopenharmony_ci flush_buffer(tty); 6768c2ecf20Sopenharmony_ci tty_ldisc_flush(tty); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci shutdown(info); 6798c2ecf20Sopenharmony_ci mutex_unlock(&info->port.mutex); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci tty_port_close_end(&info->port, tty); 6828c2ecf20Sopenharmony_ci info->port.tty = NULL; 6838c2ecf20Sopenharmony_cicleanup: 6848c2ecf20Sopenharmony_ci DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count)); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic void hangup(struct tty_struct *tty) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 6908c2ecf20Sopenharmony_ci unsigned long flags; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "hangup")) 6938c2ecf20Sopenharmony_ci return; 6948c2ecf20Sopenharmony_ci DBGINFO(("%s hangup\n", info->device_name)); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci flush_buffer(tty); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci mutex_lock(&info->port.mutex); 6998c2ecf20Sopenharmony_ci shutdown(info); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->port.lock, flags); 7028c2ecf20Sopenharmony_ci info->port.count = 0; 7038c2ecf20Sopenharmony_ci info->port.tty = NULL; 7048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->port.lock, flags); 7058c2ecf20Sopenharmony_ci tty_port_set_active(&info->port, 0); 7068c2ecf20Sopenharmony_ci mutex_unlock(&info->port.mutex); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci wake_up_interruptible(&info->port.open_wait); 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic void set_termios(struct tty_struct *tty, struct ktermios *old_termios) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 7148c2ecf20Sopenharmony_ci unsigned long flags; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci DBGINFO(("%s set_termios\n", tty->driver->name)); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci change_params(info); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* Handle transition to B0 status */ 7218c2ecf20Sopenharmony_ci if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { 7228c2ecf20Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 7238c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 7248c2ecf20Sopenharmony_ci set_gtsignals(info); 7258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* Handle transition away from B0 status */ 7298c2ecf20Sopenharmony_ci if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { 7308c2ecf20Sopenharmony_ci info->signals |= SerialSignal_DTR; 7318c2ecf20Sopenharmony_ci if (!C_CRTSCTS(tty) || !tty_throttled(tty)) 7328c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RTS; 7338c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 7348c2ecf20Sopenharmony_ci set_gtsignals(info); 7358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* Handle turning off CRTSCTS */ 7398c2ecf20Sopenharmony_ci if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { 7408c2ecf20Sopenharmony_ci tty->hw_stopped = 0; 7418c2ecf20Sopenharmony_ci tx_release(tty); 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic void update_tx_timer(struct slgt_info *info) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci /* 7488c2ecf20Sopenharmony_ci * use worst case speed of 1200bps to calculate transmit timeout 7498c2ecf20Sopenharmony_ci * based on data in buffers (tbuf_bytes) and FIFO (128 bytes) 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_ci if (info->params.mode == MGSL_MODE_HDLC) { 7528c2ecf20Sopenharmony_ci int timeout = (tbuf_bytes(info) * 7) + 1000; 7538c2ecf20Sopenharmony_ci mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout)); 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic int write(struct tty_struct *tty, 7588c2ecf20Sopenharmony_ci const unsigned char *buf, int count) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci int ret = 0; 7618c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 7628c2ecf20Sopenharmony_ci unsigned long flags; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "write")) 7658c2ecf20Sopenharmony_ci return -EIO; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci DBGINFO(("%s write count=%d\n", info->device_name, count)); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (!info->tx_buf || (count > info->max_frame_size)) 7708c2ecf20Sopenharmony_ci return -EIO; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (!count || tty->stopped || tty->hw_stopped) 7738c2ecf20Sopenharmony_ci return 0; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (info->tx_count) { 7788c2ecf20Sopenharmony_ci /* send accumulated data from send_char() */ 7798c2ecf20Sopenharmony_ci if (!tx_load(info, info->tx_buf, info->tx_count)) 7808c2ecf20Sopenharmony_ci goto cleanup; 7818c2ecf20Sopenharmony_ci info->tx_count = 0; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (tx_load(info, buf, count)) 7858c2ecf20Sopenharmony_ci ret = count; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cicleanup: 7888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 7898c2ecf20Sopenharmony_ci DBGINFO(("%s write rc=%d\n", info->device_name, ret)); 7908c2ecf20Sopenharmony_ci return ret; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic int put_char(struct tty_struct *tty, unsigned char ch) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 7968c2ecf20Sopenharmony_ci unsigned long flags; 7978c2ecf20Sopenharmony_ci int ret = 0; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "put_char")) 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci DBGINFO(("%s put_char(%d)\n", info->device_name, ch)); 8028c2ecf20Sopenharmony_ci if (!info->tx_buf) 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 8058c2ecf20Sopenharmony_ci if (info->tx_count < info->max_frame_size) { 8068c2ecf20Sopenharmony_ci info->tx_buf[info->tx_count++] = ch; 8078c2ecf20Sopenharmony_ci ret = 1; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 8108c2ecf20Sopenharmony_ci return ret; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic void send_xchar(struct tty_struct *tty, char ch) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 8168c2ecf20Sopenharmony_ci unsigned long flags; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "send_xchar")) 8198c2ecf20Sopenharmony_ci return; 8208c2ecf20Sopenharmony_ci DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch)); 8218c2ecf20Sopenharmony_ci info->x_char = ch; 8228c2ecf20Sopenharmony_ci if (ch) { 8238c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 8248c2ecf20Sopenharmony_ci if (!info->tx_enabled) 8258c2ecf20Sopenharmony_ci tx_start(info); 8268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic void wait_until_sent(struct tty_struct *tty, int timeout) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 8338c2ecf20Sopenharmony_ci unsigned long orig_jiffies, char_time; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (!info ) 8368c2ecf20Sopenharmony_ci return; 8378c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "wait_until_sent")) 8388c2ecf20Sopenharmony_ci return; 8398c2ecf20Sopenharmony_ci DBGINFO(("%s wait_until_sent entry\n", info->device_name)); 8408c2ecf20Sopenharmony_ci if (!tty_port_initialized(&info->port)) 8418c2ecf20Sopenharmony_ci goto exit; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci orig_jiffies = jiffies; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* Set check interval to 1/5 of estimated time to 8468c2ecf20Sopenharmony_ci * send a character, and make it at least 1. The check 8478c2ecf20Sopenharmony_ci * interval should also be less than the timeout. 8488c2ecf20Sopenharmony_ci * Note: use tight timings here to satisfy the NIST-PCTS. 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (info->params.data_rate) { 8528c2ecf20Sopenharmony_ci char_time = info->timeout/(32 * 5); 8538c2ecf20Sopenharmony_ci if (!char_time) 8548c2ecf20Sopenharmony_ci char_time++; 8558c2ecf20Sopenharmony_ci } else 8568c2ecf20Sopenharmony_ci char_time = 1; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (timeout) 8598c2ecf20Sopenharmony_ci char_time = min_t(unsigned long, char_time, timeout); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci while (info->tx_active) { 8628c2ecf20Sopenharmony_ci msleep_interruptible(jiffies_to_msecs(char_time)); 8638c2ecf20Sopenharmony_ci if (signal_pending(current)) 8648c2ecf20Sopenharmony_ci break; 8658c2ecf20Sopenharmony_ci if (timeout && time_after(jiffies, orig_jiffies + timeout)) 8668c2ecf20Sopenharmony_ci break; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ciexit: 8698c2ecf20Sopenharmony_ci DBGINFO(("%s wait_until_sent exit\n", info->device_name)); 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic int write_room(struct tty_struct *tty) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 8758c2ecf20Sopenharmony_ci int ret; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "write_room")) 8788c2ecf20Sopenharmony_ci return 0; 8798c2ecf20Sopenharmony_ci ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE; 8808c2ecf20Sopenharmony_ci DBGINFO(("%s write_room=%d\n", info->device_name, ret)); 8818c2ecf20Sopenharmony_ci return ret; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic void flush_chars(struct tty_struct *tty) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 8878c2ecf20Sopenharmony_ci unsigned long flags; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "flush_chars")) 8908c2ecf20Sopenharmony_ci return; 8918c2ecf20Sopenharmony_ci DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count)); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (info->tx_count <= 0 || tty->stopped || 8948c2ecf20Sopenharmony_ci tty->hw_stopped || !info->tx_buf) 8958c2ecf20Sopenharmony_ci return; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci DBGINFO(("%s flush_chars start transmit\n", info->device_name)); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 9008c2ecf20Sopenharmony_ci if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) 9018c2ecf20Sopenharmony_ci info->tx_count = 0; 9028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic void flush_buffer(struct tty_struct *tty) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 9088c2ecf20Sopenharmony_ci unsigned long flags; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "flush_buffer")) 9118c2ecf20Sopenharmony_ci return; 9128c2ecf20Sopenharmony_ci DBGINFO(("%s flush_buffer\n", info->device_name)); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 9158c2ecf20Sopenharmony_ci info->tx_count = 0; 9168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci tty_wakeup(tty); 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci/* 9228c2ecf20Sopenharmony_ci * throttle (stop) transmitter 9238c2ecf20Sopenharmony_ci */ 9248c2ecf20Sopenharmony_cistatic void tx_hold(struct tty_struct *tty) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 9278c2ecf20Sopenharmony_ci unsigned long flags; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "tx_hold")) 9308c2ecf20Sopenharmony_ci return; 9318c2ecf20Sopenharmony_ci DBGINFO(("%s tx_hold\n", info->device_name)); 9328c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 9338c2ecf20Sopenharmony_ci if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC) 9348c2ecf20Sopenharmony_ci tx_stop(info); 9358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci/* 9398c2ecf20Sopenharmony_ci * release (start) transmitter 9408c2ecf20Sopenharmony_ci */ 9418c2ecf20Sopenharmony_cistatic void tx_release(struct tty_struct *tty) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 9448c2ecf20Sopenharmony_ci unsigned long flags; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "tx_release")) 9478c2ecf20Sopenharmony_ci return; 9488c2ecf20Sopenharmony_ci DBGINFO(("%s tx_release\n", info->device_name)); 9498c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 9508c2ecf20Sopenharmony_ci if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) 9518c2ecf20Sopenharmony_ci info->tx_count = 0; 9528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci/* 9568c2ecf20Sopenharmony_ci * Service an IOCTL request 9578c2ecf20Sopenharmony_ci * 9588c2ecf20Sopenharmony_ci * Arguments 9598c2ecf20Sopenharmony_ci * 9608c2ecf20Sopenharmony_ci * tty pointer to tty instance data 9618c2ecf20Sopenharmony_ci * cmd IOCTL command code 9628c2ecf20Sopenharmony_ci * arg command argument/context 9638c2ecf20Sopenharmony_ci * 9648c2ecf20Sopenharmony_ci * Return 0 if success, otherwise error code 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_cistatic int ioctl(struct tty_struct *tty, 9678c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 9708c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 9718c2ecf20Sopenharmony_ci int ret; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "ioctl")) 9748c2ecf20Sopenharmony_ci return -ENODEV; 9758c2ecf20Sopenharmony_ci DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd)); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (cmd != TIOCMIWAIT) { 9788c2ecf20Sopenharmony_ci if (tty_io_error(tty)) 9798c2ecf20Sopenharmony_ci return -EIO; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci switch (cmd) { 9838c2ecf20Sopenharmony_ci case MGSL_IOCWAITEVENT: 9848c2ecf20Sopenharmony_ci return wait_mgsl_event(info, argp); 9858c2ecf20Sopenharmony_ci case TIOCMIWAIT: 9868c2ecf20Sopenharmony_ci return modem_input_wait(info,(int)arg); 9878c2ecf20Sopenharmony_ci case MGSL_IOCSGPIO: 9888c2ecf20Sopenharmony_ci return set_gpio(info, argp); 9898c2ecf20Sopenharmony_ci case MGSL_IOCGGPIO: 9908c2ecf20Sopenharmony_ci return get_gpio(info, argp); 9918c2ecf20Sopenharmony_ci case MGSL_IOCWAITGPIO: 9928c2ecf20Sopenharmony_ci return wait_gpio(info, argp); 9938c2ecf20Sopenharmony_ci case MGSL_IOCGXSYNC: 9948c2ecf20Sopenharmony_ci return get_xsync(info, argp); 9958c2ecf20Sopenharmony_ci case MGSL_IOCSXSYNC: 9968c2ecf20Sopenharmony_ci return set_xsync(info, (int)arg); 9978c2ecf20Sopenharmony_ci case MGSL_IOCGXCTRL: 9988c2ecf20Sopenharmony_ci return get_xctrl(info, argp); 9998c2ecf20Sopenharmony_ci case MGSL_IOCSXCTRL: 10008c2ecf20Sopenharmony_ci return set_xctrl(info, (int)arg); 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci mutex_lock(&info->port.mutex); 10038c2ecf20Sopenharmony_ci switch (cmd) { 10048c2ecf20Sopenharmony_ci case MGSL_IOCGPARAMS: 10058c2ecf20Sopenharmony_ci ret = get_params(info, argp); 10068c2ecf20Sopenharmony_ci break; 10078c2ecf20Sopenharmony_ci case MGSL_IOCSPARAMS: 10088c2ecf20Sopenharmony_ci ret = set_params(info, argp); 10098c2ecf20Sopenharmony_ci break; 10108c2ecf20Sopenharmony_ci case MGSL_IOCGTXIDLE: 10118c2ecf20Sopenharmony_ci ret = get_txidle(info, argp); 10128c2ecf20Sopenharmony_ci break; 10138c2ecf20Sopenharmony_ci case MGSL_IOCSTXIDLE: 10148c2ecf20Sopenharmony_ci ret = set_txidle(info, (int)arg); 10158c2ecf20Sopenharmony_ci break; 10168c2ecf20Sopenharmony_ci case MGSL_IOCTXENABLE: 10178c2ecf20Sopenharmony_ci ret = tx_enable(info, (int)arg); 10188c2ecf20Sopenharmony_ci break; 10198c2ecf20Sopenharmony_ci case MGSL_IOCRXENABLE: 10208c2ecf20Sopenharmony_ci ret = rx_enable(info, (int)arg); 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci case MGSL_IOCTXABORT: 10238c2ecf20Sopenharmony_ci ret = tx_abort(info); 10248c2ecf20Sopenharmony_ci break; 10258c2ecf20Sopenharmony_ci case MGSL_IOCGSTATS: 10268c2ecf20Sopenharmony_ci ret = get_stats(info, argp); 10278c2ecf20Sopenharmony_ci break; 10288c2ecf20Sopenharmony_ci case MGSL_IOCGIF: 10298c2ecf20Sopenharmony_ci ret = get_interface(info, argp); 10308c2ecf20Sopenharmony_ci break; 10318c2ecf20Sopenharmony_ci case MGSL_IOCSIF: 10328c2ecf20Sopenharmony_ci ret = set_interface(info,(int)arg); 10338c2ecf20Sopenharmony_ci break; 10348c2ecf20Sopenharmony_ci default: 10358c2ecf20Sopenharmony_ci ret = -ENOIOCTLCMD; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci mutex_unlock(&info->port.mutex); 10388c2ecf20Sopenharmony_ci return ret; 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic int get_icount(struct tty_struct *tty, 10428c2ecf20Sopenharmony_ci struct serial_icounter_struct *icount) 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci{ 10458c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 10468c2ecf20Sopenharmony_ci struct mgsl_icount cnow; /* kernel counter temps */ 10478c2ecf20Sopenharmony_ci unsigned long flags; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 10508c2ecf20Sopenharmony_ci cnow = info->icount; 10518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci icount->cts = cnow.cts; 10548c2ecf20Sopenharmony_ci icount->dsr = cnow.dsr; 10558c2ecf20Sopenharmony_ci icount->rng = cnow.rng; 10568c2ecf20Sopenharmony_ci icount->dcd = cnow.dcd; 10578c2ecf20Sopenharmony_ci icount->rx = cnow.rx; 10588c2ecf20Sopenharmony_ci icount->tx = cnow.tx; 10598c2ecf20Sopenharmony_ci icount->frame = cnow.frame; 10608c2ecf20Sopenharmony_ci icount->overrun = cnow.overrun; 10618c2ecf20Sopenharmony_ci icount->parity = cnow.parity; 10628c2ecf20Sopenharmony_ci icount->brk = cnow.brk; 10638c2ecf20Sopenharmony_ci icount->buf_overrun = cnow.buf_overrun; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci return 0; 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci/* 10698c2ecf20Sopenharmony_ci * support for 32 bit ioctl calls on 64 bit systems 10708c2ecf20Sopenharmony_ci */ 10718c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 10728c2ecf20Sopenharmony_cistatic long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci struct MGSL_PARAMS32 tmp_params; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci DBGINFO(("%s get_params32\n", info->device_name)); 10778c2ecf20Sopenharmony_ci memset(&tmp_params, 0, sizeof(tmp_params)); 10788c2ecf20Sopenharmony_ci tmp_params.mode = (compat_ulong_t)info->params.mode; 10798c2ecf20Sopenharmony_ci tmp_params.loopback = info->params.loopback; 10808c2ecf20Sopenharmony_ci tmp_params.flags = info->params.flags; 10818c2ecf20Sopenharmony_ci tmp_params.encoding = info->params.encoding; 10828c2ecf20Sopenharmony_ci tmp_params.clock_speed = (compat_ulong_t)info->params.clock_speed; 10838c2ecf20Sopenharmony_ci tmp_params.addr_filter = info->params.addr_filter; 10848c2ecf20Sopenharmony_ci tmp_params.crc_type = info->params.crc_type; 10858c2ecf20Sopenharmony_ci tmp_params.preamble_length = info->params.preamble_length; 10868c2ecf20Sopenharmony_ci tmp_params.preamble = info->params.preamble; 10878c2ecf20Sopenharmony_ci tmp_params.data_rate = (compat_ulong_t)info->params.data_rate; 10888c2ecf20Sopenharmony_ci tmp_params.data_bits = info->params.data_bits; 10898c2ecf20Sopenharmony_ci tmp_params.stop_bits = info->params.stop_bits; 10908c2ecf20Sopenharmony_ci tmp_params.parity = info->params.parity; 10918c2ecf20Sopenharmony_ci if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32))) 10928c2ecf20Sopenharmony_ci return -EFAULT; 10938c2ecf20Sopenharmony_ci return 0; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct MGSL_PARAMS32 tmp_params; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci DBGINFO(("%s set_params32\n", info->device_name)); 11018c2ecf20Sopenharmony_ci if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32))) 11028c2ecf20Sopenharmony_ci return -EFAULT; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci spin_lock(&info->lock); 11058c2ecf20Sopenharmony_ci if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) { 11068c2ecf20Sopenharmony_ci info->base_clock = tmp_params.clock_speed; 11078c2ecf20Sopenharmony_ci } else { 11088c2ecf20Sopenharmony_ci info->params.mode = tmp_params.mode; 11098c2ecf20Sopenharmony_ci info->params.loopback = tmp_params.loopback; 11108c2ecf20Sopenharmony_ci info->params.flags = tmp_params.flags; 11118c2ecf20Sopenharmony_ci info->params.encoding = tmp_params.encoding; 11128c2ecf20Sopenharmony_ci info->params.clock_speed = tmp_params.clock_speed; 11138c2ecf20Sopenharmony_ci info->params.addr_filter = tmp_params.addr_filter; 11148c2ecf20Sopenharmony_ci info->params.crc_type = tmp_params.crc_type; 11158c2ecf20Sopenharmony_ci info->params.preamble_length = tmp_params.preamble_length; 11168c2ecf20Sopenharmony_ci info->params.preamble = tmp_params.preamble; 11178c2ecf20Sopenharmony_ci info->params.data_rate = tmp_params.data_rate; 11188c2ecf20Sopenharmony_ci info->params.data_bits = tmp_params.data_bits; 11198c2ecf20Sopenharmony_ci info->params.stop_bits = tmp_params.stop_bits; 11208c2ecf20Sopenharmony_ci info->params.parity = tmp_params.parity; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci spin_unlock(&info->lock); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci program_hw(info); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci return 0; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic long slgt_compat_ioctl(struct tty_struct *tty, 11308c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 11338c2ecf20Sopenharmony_ci int rc; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "compat_ioctl")) 11368c2ecf20Sopenharmony_ci return -ENODEV; 11378c2ecf20Sopenharmony_ci DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd)); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci switch (cmd) { 11408c2ecf20Sopenharmony_ci case MGSL_IOCSPARAMS32: 11418c2ecf20Sopenharmony_ci rc = set_params32(info, compat_ptr(arg)); 11428c2ecf20Sopenharmony_ci break; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci case MGSL_IOCGPARAMS32: 11458c2ecf20Sopenharmony_ci rc = get_params32(info, compat_ptr(arg)); 11468c2ecf20Sopenharmony_ci break; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci case MGSL_IOCGPARAMS: 11498c2ecf20Sopenharmony_ci case MGSL_IOCSPARAMS: 11508c2ecf20Sopenharmony_ci case MGSL_IOCGTXIDLE: 11518c2ecf20Sopenharmony_ci case MGSL_IOCGSTATS: 11528c2ecf20Sopenharmony_ci case MGSL_IOCWAITEVENT: 11538c2ecf20Sopenharmony_ci case MGSL_IOCGIF: 11548c2ecf20Sopenharmony_ci case MGSL_IOCSGPIO: 11558c2ecf20Sopenharmony_ci case MGSL_IOCGGPIO: 11568c2ecf20Sopenharmony_ci case MGSL_IOCWAITGPIO: 11578c2ecf20Sopenharmony_ci case MGSL_IOCGXSYNC: 11588c2ecf20Sopenharmony_ci case MGSL_IOCGXCTRL: 11598c2ecf20Sopenharmony_ci rc = ioctl(tty, cmd, (unsigned long)compat_ptr(arg)); 11608c2ecf20Sopenharmony_ci break; 11618c2ecf20Sopenharmony_ci default: 11628c2ecf20Sopenharmony_ci rc = ioctl(tty, cmd, arg); 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc)); 11658c2ecf20Sopenharmony_ci return rc; 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci#else 11688c2ecf20Sopenharmony_ci#define slgt_compat_ioctl NULL 11698c2ecf20Sopenharmony_ci#endif /* ifdef CONFIG_COMPAT */ 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci/* 11728c2ecf20Sopenharmony_ci * proc fs support 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_cistatic inline void line_info(struct seq_file *m, struct slgt_info *info) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci char stat_buf[30]; 11778c2ecf20Sopenharmony_ci unsigned long flags; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n", 11808c2ecf20Sopenharmony_ci info->device_name, info->phys_reg_addr, 11818c2ecf20Sopenharmony_ci info->irq_level, info->max_frame_size); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* output current serial signal states */ 11848c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 11858c2ecf20Sopenharmony_ci get_gtsignals(info); 11868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci stat_buf[0] = 0; 11898c2ecf20Sopenharmony_ci stat_buf[1] = 0; 11908c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_RTS) 11918c2ecf20Sopenharmony_ci strcat(stat_buf, "|RTS"); 11928c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_CTS) 11938c2ecf20Sopenharmony_ci strcat(stat_buf, "|CTS"); 11948c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_DTR) 11958c2ecf20Sopenharmony_ci strcat(stat_buf, "|DTR"); 11968c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_DSR) 11978c2ecf20Sopenharmony_ci strcat(stat_buf, "|DSR"); 11988c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_DCD) 11998c2ecf20Sopenharmony_ci strcat(stat_buf, "|CD"); 12008c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_RI) 12018c2ecf20Sopenharmony_ci strcat(stat_buf, "|RI"); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 12048c2ecf20Sopenharmony_ci seq_printf(m, "\tHDLC txok:%d rxok:%d", 12058c2ecf20Sopenharmony_ci info->icount.txok, info->icount.rxok); 12068c2ecf20Sopenharmony_ci if (info->icount.txunder) 12078c2ecf20Sopenharmony_ci seq_printf(m, " txunder:%d", info->icount.txunder); 12088c2ecf20Sopenharmony_ci if (info->icount.txabort) 12098c2ecf20Sopenharmony_ci seq_printf(m, " txabort:%d", info->icount.txabort); 12108c2ecf20Sopenharmony_ci if (info->icount.rxshort) 12118c2ecf20Sopenharmony_ci seq_printf(m, " rxshort:%d", info->icount.rxshort); 12128c2ecf20Sopenharmony_ci if (info->icount.rxlong) 12138c2ecf20Sopenharmony_ci seq_printf(m, " rxlong:%d", info->icount.rxlong); 12148c2ecf20Sopenharmony_ci if (info->icount.rxover) 12158c2ecf20Sopenharmony_ci seq_printf(m, " rxover:%d", info->icount.rxover); 12168c2ecf20Sopenharmony_ci if (info->icount.rxcrc) 12178c2ecf20Sopenharmony_ci seq_printf(m, " rxcrc:%d", info->icount.rxcrc); 12188c2ecf20Sopenharmony_ci } else { 12198c2ecf20Sopenharmony_ci seq_printf(m, "\tASYNC tx:%d rx:%d", 12208c2ecf20Sopenharmony_ci info->icount.tx, info->icount.rx); 12218c2ecf20Sopenharmony_ci if (info->icount.frame) 12228c2ecf20Sopenharmony_ci seq_printf(m, " fe:%d", info->icount.frame); 12238c2ecf20Sopenharmony_ci if (info->icount.parity) 12248c2ecf20Sopenharmony_ci seq_printf(m, " pe:%d", info->icount.parity); 12258c2ecf20Sopenharmony_ci if (info->icount.brk) 12268c2ecf20Sopenharmony_ci seq_printf(m, " brk:%d", info->icount.brk); 12278c2ecf20Sopenharmony_ci if (info->icount.overrun) 12288c2ecf20Sopenharmony_ci seq_printf(m, " oe:%d", info->icount.overrun); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci /* Append serial signal status to end */ 12328c2ecf20Sopenharmony_ci seq_printf(m, " %s\n", stat_buf+1); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", 12358c2ecf20Sopenharmony_ci info->tx_active,info->bh_requested,info->bh_running, 12368c2ecf20Sopenharmony_ci info->pending_bh); 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci/* Called to print information about devices 12408c2ecf20Sopenharmony_ci */ 12418c2ecf20Sopenharmony_cistatic int synclink_gt_proc_show(struct seq_file *m, void *v) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci struct slgt_info *info; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci seq_puts(m, "synclink_gt driver\n"); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci info = slgt_device_list; 12488c2ecf20Sopenharmony_ci while( info ) { 12498c2ecf20Sopenharmony_ci line_info(m, info); 12508c2ecf20Sopenharmony_ci info = info->next_device; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci return 0; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci/* 12568c2ecf20Sopenharmony_ci * return count of bytes in transmit buffer 12578c2ecf20Sopenharmony_ci */ 12588c2ecf20Sopenharmony_cistatic int chars_in_buffer(struct tty_struct *tty) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 12618c2ecf20Sopenharmony_ci int count; 12628c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "chars_in_buffer")) 12638c2ecf20Sopenharmony_ci return 0; 12648c2ecf20Sopenharmony_ci count = tbuf_bytes(info); 12658c2ecf20Sopenharmony_ci DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count)); 12668c2ecf20Sopenharmony_ci return count; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci/* 12708c2ecf20Sopenharmony_ci * signal remote device to throttle send data (our receive data) 12718c2ecf20Sopenharmony_ci */ 12728c2ecf20Sopenharmony_cistatic void throttle(struct tty_struct * tty) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 12758c2ecf20Sopenharmony_ci unsigned long flags; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "throttle")) 12788c2ecf20Sopenharmony_ci return; 12798c2ecf20Sopenharmony_ci DBGINFO(("%s throttle\n", info->device_name)); 12808c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) 12818c2ecf20Sopenharmony_ci send_xchar(tty, STOP_CHAR(tty)); 12828c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) { 12838c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 12848c2ecf20Sopenharmony_ci info->signals &= ~SerialSignal_RTS; 12858c2ecf20Sopenharmony_ci set_gtsignals(info); 12868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci/* 12918c2ecf20Sopenharmony_ci * signal remote device to stop throttling send data (our receive data) 12928c2ecf20Sopenharmony_ci */ 12938c2ecf20Sopenharmony_cistatic void unthrottle(struct tty_struct * tty) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 12968c2ecf20Sopenharmony_ci unsigned long flags; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "unthrottle")) 12998c2ecf20Sopenharmony_ci return; 13008c2ecf20Sopenharmony_ci DBGINFO(("%s unthrottle\n", info->device_name)); 13018c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) { 13028c2ecf20Sopenharmony_ci if (info->x_char) 13038c2ecf20Sopenharmony_ci info->x_char = 0; 13048c2ecf20Sopenharmony_ci else 13058c2ecf20Sopenharmony_ci send_xchar(tty, START_CHAR(tty)); 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) { 13088c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 13098c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RTS; 13108c2ecf20Sopenharmony_ci set_gtsignals(info); 13118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci} 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci/* 13168c2ecf20Sopenharmony_ci * set or clear transmit break condition 13178c2ecf20Sopenharmony_ci * break_state -1=set break condition, 0=clear 13188c2ecf20Sopenharmony_ci */ 13198c2ecf20Sopenharmony_cistatic int set_break(struct tty_struct *tty, int break_state) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 13228c2ecf20Sopenharmony_ci unsigned short value; 13238c2ecf20Sopenharmony_ci unsigned long flags; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (sanity_check(info, tty->name, "set_break")) 13268c2ecf20Sopenharmony_ci return -EINVAL; 13278c2ecf20Sopenharmony_ci DBGINFO(("%s set_break(%d)\n", info->device_name, break_state)); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 13308c2ecf20Sopenharmony_ci value = rd_reg16(info, TCR); 13318c2ecf20Sopenharmony_ci if (break_state == -1) 13328c2ecf20Sopenharmony_ci value |= BIT6; 13338c2ecf20Sopenharmony_ci else 13348c2ecf20Sopenharmony_ci value &= ~BIT6; 13358c2ecf20Sopenharmony_ci wr_reg16(info, TCR, value); 13368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 13378c2ecf20Sopenharmony_ci return 0; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci/** 13438c2ecf20Sopenharmony_ci * hdlcdev_attach - called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) 13448c2ecf20Sopenharmony_ci * @dev: pointer to network device structure 13458c2ecf20Sopenharmony_ci * @encoding: serial encoding setting 13468c2ecf20Sopenharmony_ci * @parity: FCS setting 13478c2ecf20Sopenharmony_ci * 13488c2ecf20Sopenharmony_ci * Set encoding and frame check sequence (FCS) options. 13498c2ecf20Sopenharmony_ci * 13508c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_cistatic int hdlcdev_attach(struct net_device *dev, unsigned short encoding, 13538c2ecf20Sopenharmony_ci unsigned short parity) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 13568c2ecf20Sopenharmony_ci unsigned char new_encoding; 13578c2ecf20Sopenharmony_ci unsigned short new_crctype; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci /* return error if TTY interface open */ 13608c2ecf20Sopenharmony_ci if (info->port.count) 13618c2ecf20Sopenharmony_ci return -EBUSY; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci DBGINFO(("%s hdlcdev_attach\n", info->device_name)); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci switch (encoding) 13668c2ecf20Sopenharmony_ci { 13678c2ecf20Sopenharmony_ci case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; 13688c2ecf20Sopenharmony_ci case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; 13698c2ecf20Sopenharmony_ci case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; 13708c2ecf20Sopenharmony_ci case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; 13718c2ecf20Sopenharmony_ci case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; 13728c2ecf20Sopenharmony_ci default: return -EINVAL; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci switch (parity) 13768c2ecf20Sopenharmony_ci { 13778c2ecf20Sopenharmony_ci case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; 13788c2ecf20Sopenharmony_ci case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; 13798c2ecf20Sopenharmony_ci case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; 13808c2ecf20Sopenharmony_ci default: return -EINVAL; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci info->params.encoding = new_encoding; 13848c2ecf20Sopenharmony_ci info->params.crc_type = new_crctype; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci /* if network interface up, reprogram hardware */ 13878c2ecf20Sopenharmony_ci if (info->netcount) 13888c2ecf20Sopenharmony_ci program_hw(info); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci return 0; 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci/** 13948c2ecf20Sopenharmony_ci * hdlcdev_xmit - called by generic HDLC layer to send a frame 13958c2ecf20Sopenharmony_ci * @skb: socket buffer containing HDLC frame 13968c2ecf20Sopenharmony_ci * @dev: pointer to network device structure 13978c2ecf20Sopenharmony_ci */ 13988c2ecf20Sopenharmony_cistatic netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, 13998c2ecf20Sopenharmony_ci struct net_device *dev) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 14028c2ecf20Sopenharmony_ci unsigned long flags; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci DBGINFO(("%s hdlc_xmit\n", dev->name)); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci if (!skb->len) 14078c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci /* stop sending until this frame completes */ 14108c2ecf20Sopenharmony_ci netif_stop_queue(dev); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci /* update network statistics */ 14138c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 14148c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci /* save start time for transmit timeout detection */ 14178c2ecf20Sopenharmony_ci netif_trans_update(dev); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 14208c2ecf20Sopenharmony_ci tx_load(info, skb->data, skb->len); 14218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* done with socket buffer, so free it */ 14248c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci/** 14308c2ecf20Sopenharmony_ci * hdlcdev_open - called by network layer when interface enabled 14318c2ecf20Sopenharmony_ci * @dev: pointer to network device structure 14328c2ecf20Sopenharmony_ci * 14338c2ecf20Sopenharmony_ci * Claim resources and initialize hardware. 14348c2ecf20Sopenharmony_ci * 14358c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code 14368c2ecf20Sopenharmony_ci */ 14378c2ecf20Sopenharmony_cistatic int hdlcdev_open(struct net_device *dev) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 14408c2ecf20Sopenharmony_ci int rc; 14418c2ecf20Sopenharmony_ci unsigned long flags; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 14448c2ecf20Sopenharmony_ci return -EBUSY; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci DBGINFO(("%s hdlcdev_open\n", dev->name)); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* generic HDLC layer open processing */ 14498c2ecf20Sopenharmony_ci rc = hdlc_open(dev); 14508c2ecf20Sopenharmony_ci if (rc) 14518c2ecf20Sopenharmony_ci return rc; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci /* arbitrate between network and tty opens */ 14548c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 14558c2ecf20Sopenharmony_ci if (info->port.count != 0 || info->netcount != 0) { 14568c2ecf20Sopenharmony_ci DBGINFO(("%s hdlc_open busy\n", dev->name)); 14578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 14588c2ecf20Sopenharmony_ci return -EBUSY; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci info->netcount=1; 14618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* claim resources and init adapter */ 14648c2ecf20Sopenharmony_ci if ((rc = startup(info)) != 0) { 14658c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 14668c2ecf20Sopenharmony_ci info->netcount=0; 14678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 14688c2ecf20Sopenharmony_ci return rc; 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci /* assert RTS and DTR, apply hardware settings */ 14728c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RTS | SerialSignal_DTR; 14738c2ecf20Sopenharmony_ci program_hw(info); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci /* enable network layer transmit */ 14768c2ecf20Sopenharmony_ci netif_trans_update(dev); 14778c2ecf20Sopenharmony_ci netif_start_queue(dev); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci /* inform generic HDLC layer of current DCD status */ 14808c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 14818c2ecf20Sopenharmony_ci get_gtsignals(info); 14828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 14838c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_DCD) 14848c2ecf20Sopenharmony_ci netif_carrier_on(dev); 14858c2ecf20Sopenharmony_ci else 14868c2ecf20Sopenharmony_ci netif_carrier_off(dev); 14878c2ecf20Sopenharmony_ci return 0; 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci/** 14918c2ecf20Sopenharmony_ci * hdlcdev_close - called by network layer when interface is disabled 14928c2ecf20Sopenharmony_ci * @dev: pointer to network device structure 14938c2ecf20Sopenharmony_ci * 14948c2ecf20Sopenharmony_ci * Shutdown hardware and release resources. 14958c2ecf20Sopenharmony_ci * 14968c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code 14978c2ecf20Sopenharmony_ci */ 14988c2ecf20Sopenharmony_cistatic int hdlcdev_close(struct net_device *dev) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 15018c2ecf20Sopenharmony_ci unsigned long flags; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci DBGINFO(("%s hdlcdev_close\n", dev->name)); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci netif_stop_queue(dev); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci /* shutdown adapter and release resources */ 15088c2ecf20Sopenharmony_ci shutdown(info); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci hdlc_close(dev); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 15138c2ecf20Sopenharmony_ci info->netcount=0; 15148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 15178c2ecf20Sopenharmony_ci return 0; 15188c2ecf20Sopenharmony_ci} 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci/** 15218c2ecf20Sopenharmony_ci * hdlcdev_ioctl - called by network layer to process IOCTL call to network device 15228c2ecf20Sopenharmony_ci * @dev: pointer to network device structure 15238c2ecf20Sopenharmony_ci * @ifr: pointer to network interface request structure 15248c2ecf20Sopenharmony_ci * @cmd: IOCTL command code 15258c2ecf20Sopenharmony_ci * 15268c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code 15278c2ecf20Sopenharmony_ci */ 15288c2ecf20Sopenharmony_cistatic int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 15298c2ecf20Sopenharmony_ci{ 15308c2ecf20Sopenharmony_ci const size_t size = sizeof(sync_serial_settings); 15318c2ecf20Sopenharmony_ci sync_serial_settings new_line; 15328c2ecf20Sopenharmony_ci sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync; 15338c2ecf20Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 15348c2ecf20Sopenharmony_ci unsigned int flags; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci DBGINFO(("%s hdlcdev_ioctl\n", dev->name)); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* return error if TTY interface open */ 15398c2ecf20Sopenharmony_ci if (info->port.count) 15408c2ecf20Sopenharmony_ci return -EBUSY; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci if (cmd != SIOCWANDEV) 15438c2ecf20Sopenharmony_ci return hdlc_ioctl(dev, ifr, cmd); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci memset(&new_line, 0, sizeof(new_line)); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci switch(ifr->ifr_settings.type) { 15488c2ecf20Sopenharmony_ci case IF_GET_IFACE: /* return current sync_serial_settings */ 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; 15518c2ecf20Sopenharmony_ci if (ifr->ifr_settings.size < size) { 15528c2ecf20Sopenharmony_ci ifr->ifr_settings.size = size; /* data size wanted */ 15538c2ecf20Sopenharmony_ci return -ENOBUFS; 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | 15578c2ecf20Sopenharmony_ci HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | 15588c2ecf20Sopenharmony_ci HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | 15598c2ecf20Sopenharmony_ci HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci switch (flags){ 15628c2ecf20Sopenharmony_ci case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; 15638c2ecf20Sopenharmony_ci case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; 15648c2ecf20Sopenharmony_ci case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; 15658c2ecf20Sopenharmony_ci case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; 15668c2ecf20Sopenharmony_ci default: new_line.clock_type = CLOCK_DEFAULT; 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci new_line.clock_rate = info->params.clock_speed; 15708c2ecf20Sopenharmony_ci new_line.loopback = info->params.loopback ? 1:0; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci if (copy_to_user(line, &new_line, size)) 15738c2ecf20Sopenharmony_ci return -EFAULT; 15748c2ecf20Sopenharmony_ci return 0; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci if(!capable(CAP_NET_ADMIN)) 15798c2ecf20Sopenharmony_ci return -EPERM; 15808c2ecf20Sopenharmony_ci if (copy_from_user(&new_line, line, size)) 15818c2ecf20Sopenharmony_ci return -EFAULT; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci switch (new_line.clock_type) 15848c2ecf20Sopenharmony_ci { 15858c2ecf20Sopenharmony_ci case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; 15868c2ecf20Sopenharmony_ci case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; 15878c2ecf20Sopenharmony_ci case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; 15888c2ecf20Sopenharmony_ci case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; 15898c2ecf20Sopenharmony_ci case CLOCK_DEFAULT: flags = info->params.flags & 15908c2ecf20Sopenharmony_ci (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | 15918c2ecf20Sopenharmony_ci HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | 15928c2ecf20Sopenharmony_ci HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | 15938c2ecf20Sopenharmony_ci HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; 15948c2ecf20Sopenharmony_ci default: return -EINVAL; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci if (new_line.loopback != 0 && new_line.loopback != 1) 15988c2ecf20Sopenharmony_ci return -EINVAL; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | 16018c2ecf20Sopenharmony_ci HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | 16028c2ecf20Sopenharmony_ci HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | 16038c2ecf20Sopenharmony_ci HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); 16048c2ecf20Sopenharmony_ci info->params.flags |= flags; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci info->params.loopback = new_line.loopback; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) 16098c2ecf20Sopenharmony_ci info->params.clock_speed = new_line.clock_rate; 16108c2ecf20Sopenharmony_ci else 16118c2ecf20Sopenharmony_ci info->params.clock_speed = 0; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci /* if network interface up, reprogram hardware */ 16148c2ecf20Sopenharmony_ci if (info->netcount) 16158c2ecf20Sopenharmony_ci program_hw(info); 16168c2ecf20Sopenharmony_ci return 0; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci default: 16198c2ecf20Sopenharmony_ci return hdlc_ioctl(dev, ifr, cmd); 16208c2ecf20Sopenharmony_ci } 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci/** 16248c2ecf20Sopenharmony_ci * hdlcdev_tx_timeout - called by network layer when transmit timeout is detected 16258c2ecf20Sopenharmony_ci * @dev: pointer to network device structure 16268c2ecf20Sopenharmony_ci */ 16278c2ecf20Sopenharmony_cistatic void hdlcdev_tx_timeout(struct net_device *dev, unsigned int txqueue) 16288c2ecf20Sopenharmony_ci{ 16298c2ecf20Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 16308c2ecf20Sopenharmony_ci unsigned long flags; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name)); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 16358c2ecf20Sopenharmony_ci dev->stats.tx_aborted_errors++; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 16388c2ecf20Sopenharmony_ci tx_stop(info); 16398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci netif_wake_queue(dev); 16428c2ecf20Sopenharmony_ci} 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci/** 16458c2ecf20Sopenharmony_ci * hdlcdev_tx_done - called by device driver when transmit completes 16468c2ecf20Sopenharmony_ci * @info: pointer to device instance information 16478c2ecf20Sopenharmony_ci * 16488c2ecf20Sopenharmony_ci * Reenable network layer transmit if stopped. 16498c2ecf20Sopenharmony_ci */ 16508c2ecf20Sopenharmony_cistatic void hdlcdev_tx_done(struct slgt_info *info) 16518c2ecf20Sopenharmony_ci{ 16528c2ecf20Sopenharmony_ci if (netif_queue_stopped(info->netdev)) 16538c2ecf20Sopenharmony_ci netif_wake_queue(info->netdev); 16548c2ecf20Sopenharmony_ci} 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci/** 16578c2ecf20Sopenharmony_ci * hdlcdev_rx - called by device driver when frame received 16588c2ecf20Sopenharmony_ci * @info: pointer to device instance information 16598c2ecf20Sopenharmony_ci * @buf: pointer to buffer contianing frame data 16608c2ecf20Sopenharmony_ci * @size: count of data bytes in buf 16618c2ecf20Sopenharmony_ci * 16628c2ecf20Sopenharmony_ci * Pass frame to network layer. 16638c2ecf20Sopenharmony_ci */ 16648c2ecf20Sopenharmony_cistatic void hdlcdev_rx(struct slgt_info *info, char *buf, int size) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct sk_buff *skb = dev_alloc_skb(size); 16678c2ecf20Sopenharmony_ci struct net_device *dev = info->netdev; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci DBGINFO(("%s hdlcdev_rx\n", dev->name)); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci if (skb == NULL) { 16728c2ecf20Sopenharmony_ci DBGERR(("%s: can't alloc skb, drop packet\n", dev->name)); 16738c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 16748c2ecf20Sopenharmony_ci return; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci skb_put_data(skb, buf, size); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci skb->protocol = hdlc_type_trans(skb, dev); 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 16828c2ecf20Sopenharmony_ci dev->stats.rx_bytes += size; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci netif_rx(skb); 16858c2ecf20Sopenharmony_ci} 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_cistatic const struct net_device_ops hdlcdev_ops = { 16888c2ecf20Sopenharmony_ci .ndo_open = hdlcdev_open, 16898c2ecf20Sopenharmony_ci .ndo_stop = hdlcdev_close, 16908c2ecf20Sopenharmony_ci .ndo_start_xmit = hdlc_start_xmit, 16918c2ecf20Sopenharmony_ci .ndo_do_ioctl = hdlcdev_ioctl, 16928c2ecf20Sopenharmony_ci .ndo_tx_timeout = hdlcdev_tx_timeout, 16938c2ecf20Sopenharmony_ci}; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci/** 16968c2ecf20Sopenharmony_ci * hdlcdev_init - called by device driver when adding device instance 16978c2ecf20Sopenharmony_ci * @info: pointer to device instance information 16988c2ecf20Sopenharmony_ci * 16998c2ecf20Sopenharmony_ci * Do generic HDLC initialization. 17008c2ecf20Sopenharmony_ci * 17018c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code 17028c2ecf20Sopenharmony_ci */ 17038c2ecf20Sopenharmony_cistatic int hdlcdev_init(struct slgt_info *info) 17048c2ecf20Sopenharmony_ci{ 17058c2ecf20Sopenharmony_ci int rc; 17068c2ecf20Sopenharmony_ci struct net_device *dev; 17078c2ecf20Sopenharmony_ci hdlc_device *hdlc; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci /* allocate and initialize network and HDLC layer objects */ 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci dev = alloc_hdlcdev(info); 17128c2ecf20Sopenharmony_ci if (!dev) { 17138c2ecf20Sopenharmony_ci printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name); 17148c2ecf20Sopenharmony_ci return -ENOMEM; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci /* for network layer reporting purposes only */ 17188c2ecf20Sopenharmony_ci dev->mem_start = info->phys_reg_addr; 17198c2ecf20Sopenharmony_ci dev->mem_end = info->phys_reg_addr + SLGT_REG_SIZE - 1; 17208c2ecf20Sopenharmony_ci dev->irq = info->irq_level; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci /* network layer callbacks and settings */ 17238c2ecf20Sopenharmony_ci dev->netdev_ops = &hdlcdev_ops; 17248c2ecf20Sopenharmony_ci dev->watchdog_timeo = 10 * HZ; 17258c2ecf20Sopenharmony_ci dev->tx_queue_len = 50; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci /* generic HDLC layer callbacks and settings */ 17288c2ecf20Sopenharmony_ci hdlc = dev_to_hdlc(dev); 17298c2ecf20Sopenharmony_ci hdlc->attach = hdlcdev_attach; 17308c2ecf20Sopenharmony_ci hdlc->xmit = hdlcdev_xmit; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci /* register objects with HDLC layer */ 17338c2ecf20Sopenharmony_ci rc = register_hdlc_device(dev); 17348c2ecf20Sopenharmony_ci if (rc) { 17358c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); 17368c2ecf20Sopenharmony_ci free_netdev(dev); 17378c2ecf20Sopenharmony_ci return rc; 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci info->netdev = dev; 17418c2ecf20Sopenharmony_ci return 0; 17428c2ecf20Sopenharmony_ci} 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci/** 17458c2ecf20Sopenharmony_ci * hdlcdev_exit - called by device driver when removing device instance 17468c2ecf20Sopenharmony_ci * @info: pointer to device instance information 17478c2ecf20Sopenharmony_ci * 17488c2ecf20Sopenharmony_ci * Do generic HDLC cleanup. 17498c2ecf20Sopenharmony_ci */ 17508c2ecf20Sopenharmony_cistatic void hdlcdev_exit(struct slgt_info *info) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci if (!info->netdev) 17538c2ecf20Sopenharmony_ci return; 17548c2ecf20Sopenharmony_ci unregister_hdlc_device(info->netdev); 17558c2ecf20Sopenharmony_ci free_netdev(info->netdev); 17568c2ecf20Sopenharmony_ci info->netdev = NULL; 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci#endif /* ifdef CONFIG_HDLC */ 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci/* 17628c2ecf20Sopenharmony_ci * get async data from rx DMA buffers 17638c2ecf20Sopenharmony_ci */ 17648c2ecf20Sopenharmony_cistatic void rx_async(struct slgt_info *info) 17658c2ecf20Sopenharmony_ci{ 17668c2ecf20Sopenharmony_ci struct mgsl_icount *icount = &info->icount; 17678c2ecf20Sopenharmony_ci unsigned int start, end; 17688c2ecf20Sopenharmony_ci unsigned char *p; 17698c2ecf20Sopenharmony_ci unsigned char status; 17708c2ecf20Sopenharmony_ci struct slgt_desc *bufs = info->rbufs; 17718c2ecf20Sopenharmony_ci int i, count; 17728c2ecf20Sopenharmony_ci int chars = 0; 17738c2ecf20Sopenharmony_ci int stat; 17748c2ecf20Sopenharmony_ci unsigned char ch; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci start = end = info->rbuf_current; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci while(desc_complete(bufs[end])) { 17798c2ecf20Sopenharmony_ci count = desc_count(bufs[end]) - info->rbuf_index; 17808c2ecf20Sopenharmony_ci p = bufs[end].buf + info->rbuf_index; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci DBGISR(("%s rx_async count=%d\n", info->device_name, count)); 17838c2ecf20Sopenharmony_ci DBGDATA(info, p, count, "rx"); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci for(i=0 ; i < count; i+=2, p+=2) { 17868c2ecf20Sopenharmony_ci ch = *p; 17878c2ecf20Sopenharmony_ci icount->rx++; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci stat = 0; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci status = *(p + 1) & (BIT1 + BIT0); 17928c2ecf20Sopenharmony_ci if (status) { 17938c2ecf20Sopenharmony_ci if (status & BIT1) 17948c2ecf20Sopenharmony_ci icount->parity++; 17958c2ecf20Sopenharmony_ci else if (status & BIT0) 17968c2ecf20Sopenharmony_ci icount->frame++; 17978c2ecf20Sopenharmony_ci /* discard char if tty control flags say so */ 17988c2ecf20Sopenharmony_ci if (status & info->ignore_status_mask) 17998c2ecf20Sopenharmony_ci continue; 18008c2ecf20Sopenharmony_ci if (status & BIT1) 18018c2ecf20Sopenharmony_ci stat = TTY_PARITY; 18028c2ecf20Sopenharmony_ci else if (status & BIT0) 18038c2ecf20Sopenharmony_ci stat = TTY_FRAME; 18048c2ecf20Sopenharmony_ci } 18058c2ecf20Sopenharmony_ci tty_insert_flip_char(&info->port, ch, stat); 18068c2ecf20Sopenharmony_ci chars++; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci if (i < count) { 18108c2ecf20Sopenharmony_ci /* receive buffer not completed */ 18118c2ecf20Sopenharmony_ci info->rbuf_index += i; 18128c2ecf20Sopenharmony_ci mod_timer(&info->rx_timer, jiffies + 1); 18138c2ecf20Sopenharmony_ci break; 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci info->rbuf_index = 0; 18178c2ecf20Sopenharmony_ci free_rbufs(info, end, end); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci if (++end == info->rbuf_count) 18208c2ecf20Sopenharmony_ci end = 0; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* if entire list searched then no frame available */ 18238c2ecf20Sopenharmony_ci if (end == start) 18248c2ecf20Sopenharmony_ci break; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (chars) 18288c2ecf20Sopenharmony_ci tty_flip_buffer_push(&info->port); 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci/* 18328c2ecf20Sopenharmony_ci * return next bottom half action to perform 18338c2ecf20Sopenharmony_ci */ 18348c2ecf20Sopenharmony_cistatic int bh_action(struct slgt_info *info) 18358c2ecf20Sopenharmony_ci{ 18368c2ecf20Sopenharmony_ci unsigned long flags; 18378c2ecf20Sopenharmony_ci int rc; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci if (info->pending_bh & BH_RECEIVE) { 18428c2ecf20Sopenharmony_ci info->pending_bh &= ~BH_RECEIVE; 18438c2ecf20Sopenharmony_ci rc = BH_RECEIVE; 18448c2ecf20Sopenharmony_ci } else if (info->pending_bh & BH_TRANSMIT) { 18458c2ecf20Sopenharmony_ci info->pending_bh &= ~BH_TRANSMIT; 18468c2ecf20Sopenharmony_ci rc = BH_TRANSMIT; 18478c2ecf20Sopenharmony_ci } else if (info->pending_bh & BH_STATUS) { 18488c2ecf20Sopenharmony_ci info->pending_bh &= ~BH_STATUS; 18498c2ecf20Sopenharmony_ci rc = BH_STATUS; 18508c2ecf20Sopenharmony_ci } else { 18518c2ecf20Sopenharmony_ci /* Mark BH routine as complete */ 18528c2ecf20Sopenharmony_ci info->bh_running = false; 18538c2ecf20Sopenharmony_ci info->bh_requested = false; 18548c2ecf20Sopenharmony_ci rc = 0; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci return rc; 18608c2ecf20Sopenharmony_ci} 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci/* 18638c2ecf20Sopenharmony_ci * perform bottom half processing 18648c2ecf20Sopenharmony_ci */ 18658c2ecf20Sopenharmony_cistatic void bh_handler(struct work_struct *work) 18668c2ecf20Sopenharmony_ci{ 18678c2ecf20Sopenharmony_ci struct slgt_info *info = container_of(work, struct slgt_info, task); 18688c2ecf20Sopenharmony_ci int action; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci info->bh_running = true; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci while((action = bh_action(info))) { 18738c2ecf20Sopenharmony_ci switch (action) { 18748c2ecf20Sopenharmony_ci case BH_RECEIVE: 18758c2ecf20Sopenharmony_ci DBGBH(("%s bh receive\n", info->device_name)); 18768c2ecf20Sopenharmony_ci switch(info->params.mode) { 18778c2ecf20Sopenharmony_ci case MGSL_MODE_ASYNC: 18788c2ecf20Sopenharmony_ci rx_async(info); 18798c2ecf20Sopenharmony_ci break; 18808c2ecf20Sopenharmony_ci case MGSL_MODE_HDLC: 18818c2ecf20Sopenharmony_ci while(rx_get_frame(info)); 18828c2ecf20Sopenharmony_ci break; 18838c2ecf20Sopenharmony_ci case MGSL_MODE_RAW: 18848c2ecf20Sopenharmony_ci case MGSL_MODE_MONOSYNC: 18858c2ecf20Sopenharmony_ci case MGSL_MODE_BISYNC: 18868c2ecf20Sopenharmony_ci case MGSL_MODE_XSYNC: 18878c2ecf20Sopenharmony_ci while(rx_get_buf(info)); 18888c2ecf20Sopenharmony_ci break; 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci /* restart receiver if rx DMA buffers exhausted */ 18918c2ecf20Sopenharmony_ci if (info->rx_restart) 18928c2ecf20Sopenharmony_ci rx_start(info); 18938c2ecf20Sopenharmony_ci break; 18948c2ecf20Sopenharmony_ci case BH_TRANSMIT: 18958c2ecf20Sopenharmony_ci bh_transmit(info); 18968c2ecf20Sopenharmony_ci break; 18978c2ecf20Sopenharmony_ci case BH_STATUS: 18988c2ecf20Sopenharmony_ci DBGBH(("%s bh status\n", info->device_name)); 18998c2ecf20Sopenharmony_ci info->ri_chkcount = 0; 19008c2ecf20Sopenharmony_ci info->dsr_chkcount = 0; 19018c2ecf20Sopenharmony_ci info->dcd_chkcount = 0; 19028c2ecf20Sopenharmony_ci info->cts_chkcount = 0; 19038c2ecf20Sopenharmony_ci break; 19048c2ecf20Sopenharmony_ci default: 19058c2ecf20Sopenharmony_ci DBGBH(("%s unknown action\n", info->device_name)); 19068c2ecf20Sopenharmony_ci break; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci DBGBH(("%s bh_handler exit\n", info->device_name)); 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_cistatic void bh_transmit(struct slgt_info *info) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci struct tty_struct *tty = info->port.tty; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci DBGBH(("%s bh_transmit\n", info->device_name)); 19178c2ecf20Sopenharmony_ci if (tty) 19188c2ecf20Sopenharmony_ci tty_wakeup(tty); 19198c2ecf20Sopenharmony_ci} 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_cistatic void dsr_change(struct slgt_info *info, unsigned short status) 19228c2ecf20Sopenharmony_ci{ 19238c2ecf20Sopenharmony_ci if (status & BIT3) { 19248c2ecf20Sopenharmony_ci info->signals |= SerialSignal_DSR; 19258c2ecf20Sopenharmony_ci info->input_signal_events.dsr_up++; 19268c2ecf20Sopenharmony_ci } else { 19278c2ecf20Sopenharmony_ci info->signals &= ~SerialSignal_DSR; 19288c2ecf20Sopenharmony_ci info->input_signal_events.dsr_down++; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals)); 19318c2ecf20Sopenharmony_ci if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { 19328c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_DSR); 19338c2ecf20Sopenharmony_ci return; 19348c2ecf20Sopenharmony_ci } 19358c2ecf20Sopenharmony_ci info->icount.dsr++; 19368c2ecf20Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 19378c2ecf20Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 19388c2ecf20Sopenharmony_ci info->pending_bh |= BH_STATUS; 19398c2ecf20Sopenharmony_ci} 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_cistatic void cts_change(struct slgt_info *info, unsigned short status) 19428c2ecf20Sopenharmony_ci{ 19438c2ecf20Sopenharmony_ci if (status & BIT2) { 19448c2ecf20Sopenharmony_ci info->signals |= SerialSignal_CTS; 19458c2ecf20Sopenharmony_ci info->input_signal_events.cts_up++; 19468c2ecf20Sopenharmony_ci } else { 19478c2ecf20Sopenharmony_ci info->signals &= ~SerialSignal_CTS; 19488c2ecf20Sopenharmony_ci info->input_signal_events.cts_down++; 19498c2ecf20Sopenharmony_ci } 19508c2ecf20Sopenharmony_ci DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals)); 19518c2ecf20Sopenharmony_ci if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { 19528c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_CTS); 19538c2ecf20Sopenharmony_ci return; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci info->icount.cts++; 19568c2ecf20Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 19578c2ecf20Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 19588c2ecf20Sopenharmony_ci info->pending_bh |= BH_STATUS; 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci if (tty_port_cts_enabled(&info->port)) { 19618c2ecf20Sopenharmony_ci if (info->port.tty) { 19628c2ecf20Sopenharmony_ci if (info->port.tty->hw_stopped) { 19638c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_CTS) { 19648c2ecf20Sopenharmony_ci info->port.tty->hw_stopped = 0; 19658c2ecf20Sopenharmony_ci info->pending_bh |= BH_TRANSMIT; 19668c2ecf20Sopenharmony_ci return; 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci } else { 19698c2ecf20Sopenharmony_ci if (!(info->signals & SerialSignal_CTS)) 19708c2ecf20Sopenharmony_ci info->port.tty->hw_stopped = 1; 19718c2ecf20Sopenharmony_ci } 19728c2ecf20Sopenharmony_ci } 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci} 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_cistatic void dcd_change(struct slgt_info *info, unsigned short status) 19778c2ecf20Sopenharmony_ci{ 19788c2ecf20Sopenharmony_ci if (status & BIT1) { 19798c2ecf20Sopenharmony_ci info->signals |= SerialSignal_DCD; 19808c2ecf20Sopenharmony_ci info->input_signal_events.dcd_up++; 19818c2ecf20Sopenharmony_ci } else { 19828c2ecf20Sopenharmony_ci info->signals &= ~SerialSignal_DCD; 19838c2ecf20Sopenharmony_ci info->input_signal_events.dcd_down++; 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals)); 19868c2ecf20Sopenharmony_ci if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { 19878c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_DCD); 19888c2ecf20Sopenharmony_ci return; 19898c2ecf20Sopenharmony_ci } 19908c2ecf20Sopenharmony_ci info->icount.dcd++; 19918c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 19928c2ecf20Sopenharmony_ci if (info->netcount) { 19938c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_DCD) 19948c2ecf20Sopenharmony_ci netif_carrier_on(info->netdev); 19958c2ecf20Sopenharmony_ci else 19968c2ecf20Sopenharmony_ci netif_carrier_off(info->netdev); 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci#endif 19998c2ecf20Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 20008c2ecf20Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 20018c2ecf20Sopenharmony_ci info->pending_bh |= BH_STATUS; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci if (tty_port_check_carrier(&info->port)) { 20048c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_DCD) 20058c2ecf20Sopenharmony_ci wake_up_interruptible(&info->port.open_wait); 20068c2ecf20Sopenharmony_ci else { 20078c2ecf20Sopenharmony_ci if (info->port.tty) 20088c2ecf20Sopenharmony_ci tty_hangup(info->port.tty); 20098c2ecf20Sopenharmony_ci } 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci} 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_cistatic void ri_change(struct slgt_info *info, unsigned short status) 20148c2ecf20Sopenharmony_ci{ 20158c2ecf20Sopenharmony_ci if (status & BIT0) { 20168c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RI; 20178c2ecf20Sopenharmony_ci info->input_signal_events.ri_up++; 20188c2ecf20Sopenharmony_ci } else { 20198c2ecf20Sopenharmony_ci info->signals &= ~SerialSignal_RI; 20208c2ecf20Sopenharmony_ci info->input_signal_events.ri_down++; 20218c2ecf20Sopenharmony_ci } 20228c2ecf20Sopenharmony_ci DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals)); 20238c2ecf20Sopenharmony_ci if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { 20248c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_RI); 20258c2ecf20Sopenharmony_ci return; 20268c2ecf20Sopenharmony_ci } 20278c2ecf20Sopenharmony_ci info->icount.rng++; 20288c2ecf20Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 20298c2ecf20Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 20308c2ecf20Sopenharmony_ci info->pending_bh |= BH_STATUS; 20318c2ecf20Sopenharmony_ci} 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_cistatic void isr_rxdata(struct slgt_info *info) 20348c2ecf20Sopenharmony_ci{ 20358c2ecf20Sopenharmony_ci unsigned int count = info->rbuf_fill_count; 20368c2ecf20Sopenharmony_ci unsigned int i = info->rbuf_fill_index; 20378c2ecf20Sopenharmony_ci unsigned short reg; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci while (rd_reg16(info, SSR) & IRQ_RXDATA) { 20408c2ecf20Sopenharmony_ci reg = rd_reg16(info, RDR); 20418c2ecf20Sopenharmony_ci DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg)); 20428c2ecf20Sopenharmony_ci if (desc_complete(info->rbufs[i])) { 20438c2ecf20Sopenharmony_ci /* all buffers full */ 20448c2ecf20Sopenharmony_ci rx_stop(info); 20458c2ecf20Sopenharmony_ci info->rx_restart = true; 20468c2ecf20Sopenharmony_ci continue; 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci info->rbufs[i].buf[count++] = (unsigned char)reg; 20498c2ecf20Sopenharmony_ci /* async mode saves status byte to buffer for each data byte */ 20508c2ecf20Sopenharmony_ci if (info->params.mode == MGSL_MODE_ASYNC) 20518c2ecf20Sopenharmony_ci info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8); 20528c2ecf20Sopenharmony_ci if (count == info->rbuf_fill_level || (reg & BIT10)) { 20538c2ecf20Sopenharmony_ci /* buffer full or end of frame */ 20548c2ecf20Sopenharmony_ci set_desc_count(info->rbufs[i], count); 20558c2ecf20Sopenharmony_ci set_desc_status(info->rbufs[i], BIT15 | (reg >> 8)); 20568c2ecf20Sopenharmony_ci info->rbuf_fill_count = count = 0; 20578c2ecf20Sopenharmony_ci if (++i == info->rbuf_count) 20588c2ecf20Sopenharmony_ci i = 0; 20598c2ecf20Sopenharmony_ci info->pending_bh |= BH_RECEIVE; 20608c2ecf20Sopenharmony_ci } 20618c2ecf20Sopenharmony_ci } 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci info->rbuf_fill_index = i; 20648c2ecf20Sopenharmony_ci info->rbuf_fill_count = count; 20658c2ecf20Sopenharmony_ci} 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_cistatic void isr_serial(struct slgt_info *info) 20688c2ecf20Sopenharmony_ci{ 20698c2ecf20Sopenharmony_ci unsigned short status = rd_reg16(info, SSR); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci DBGISR(("%s isr_serial status=%04X\n", info->device_name, status)); 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci wr_reg16(info, SSR, status); /* clear pending */ 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci info->irq_occurred = true; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci if (info->params.mode == MGSL_MODE_ASYNC) { 20788c2ecf20Sopenharmony_ci if (status & IRQ_TXIDLE) { 20798c2ecf20Sopenharmony_ci if (info->tx_active) 20808c2ecf20Sopenharmony_ci isr_txeom(info, status); 20818c2ecf20Sopenharmony_ci } 20828c2ecf20Sopenharmony_ci if (info->rx_pio && (status & IRQ_RXDATA)) 20838c2ecf20Sopenharmony_ci isr_rxdata(info); 20848c2ecf20Sopenharmony_ci if ((status & IRQ_RXBREAK) && (status & RXBREAK)) { 20858c2ecf20Sopenharmony_ci info->icount.brk++; 20868c2ecf20Sopenharmony_ci /* process break detection if tty control allows */ 20878c2ecf20Sopenharmony_ci if (info->port.tty) { 20888c2ecf20Sopenharmony_ci if (!(status & info->ignore_status_mask)) { 20898c2ecf20Sopenharmony_ci if (info->read_status_mask & MASK_BREAK) { 20908c2ecf20Sopenharmony_ci tty_insert_flip_char(&info->port, 0, TTY_BREAK); 20918c2ecf20Sopenharmony_ci if (info->port.flags & ASYNC_SAK) 20928c2ecf20Sopenharmony_ci do_SAK(info->port.tty); 20938c2ecf20Sopenharmony_ci } 20948c2ecf20Sopenharmony_ci } 20958c2ecf20Sopenharmony_ci } 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci } else { 20988c2ecf20Sopenharmony_ci if (status & (IRQ_TXIDLE + IRQ_TXUNDER)) 20998c2ecf20Sopenharmony_ci isr_txeom(info, status); 21008c2ecf20Sopenharmony_ci if (info->rx_pio && (status & IRQ_RXDATA)) 21018c2ecf20Sopenharmony_ci isr_rxdata(info); 21028c2ecf20Sopenharmony_ci if (status & IRQ_RXIDLE) { 21038c2ecf20Sopenharmony_ci if (status & RXIDLE) 21048c2ecf20Sopenharmony_ci info->icount.rxidle++; 21058c2ecf20Sopenharmony_ci else 21068c2ecf20Sopenharmony_ci info->icount.exithunt++; 21078c2ecf20Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 21088c2ecf20Sopenharmony_ci } 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci if (status & IRQ_RXOVER) 21118c2ecf20Sopenharmony_ci rx_start(info); 21128c2ecf20Sopenharmony_ci } 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci if (status & IRQ_DSR) 21158c2ecf20Sopenharmony_ci dsr_change(info, status); 21168c2ecf20Sopenharmony_ci if (status & IRQ_CTS) 21178c2ecf20Sopenharmony_ci cts_change(info, status); 21188c2ecf20Sopenharmony_ci if (status & IRQ_DCD) 21198c2ecf20Sopenharmony_ci dcd_change(info, status); 21208c2ecf20Sopenharmony_ci if (status & IRQ_RI) 21218c2ecf20Sopenharmony_ci ri_change(info, status); 21228c2ecf20Sopenharmony_ci} 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_cistatic void isr_rdma(struct slgt_info *info) 21258c2ecf20Sopenharmony_ci{ 21268c2ecf20Sopenharmony_ci unsigned int status = rd_reg32(info, RDCSR); 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status)); 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci /* RDCSR (rx DMA control/status) 21318c2ecf20Sopenharmony_ci * 21328c2ecf20Sopenharmony_ci * 31..07 reserved 21338c2ecf20Sopenharmony_ci * 06 save status byte to DMA buffer 21348c2ecf20Sopenharmony_ci * 05 error 21358c2ecf20Sopenharmony_ci * 04 eol (end of list) 21368c2ecf20Sopenharmony_ci * 03 eob (end of buffer) 21378c2ecf20Sopenharmony_ci * 02 IRQ enable 21388c2ecf20Sopenharmony_ci * 01 reset 21398c2ecf20Sopenharmony_ci * 00 enable 21408c2ecf20Sopenharmony_ci */ 21418c2ecf20Sopenharmony_ci wr_reg32(info, RDCSR, status); /* clear pending */ 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci if (status & (BIT5 + BIT4)) { 21448c2ecf20Sopenharmony_ci DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name)); 21458c2ecf20Sopenharmony_ci info->rx_restart = true; 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci info->pending_bh |= BH_RECEIVE; 21488c2ecf20Sopenharmony_ci} 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_cistatic void isr_tdma(struct slgt_info *info) 21518c2ecf20Sopenharmony_ci{ 21528c2ecf20Sopenharmony_ci unsigned int status = rd_reg32(info, TDCSR); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status)); 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci /* TDCSR (tx DMA control/status) 21578c2ecf20Sopenharmony_ci * 21588c2ecf20Sopenharmony_ci * 31..06 reserved 21598c2ecf20Sopenharmony_ci * 05 error 21608c2ecf20Sopenharmony_ci * 04 eol (end of list) 21618c2ecf20Sopenharmony_ci * 03 eob (end of buffer) 21628c2ecf20Sopenharmony_ci * 02 IRQ enable 21638c2ecf20Sopenharmony_ci * 01 reset 21648c2ecf20Sopenharmony_ci * 00 enable 21658c2ecf20Sopenharmony_ci */ 21668c2ecf20Sopenharmony_ci wr_reg32(info, TDCSR, status); /* clear pending */ 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci if (status & (BIT5 + BIT4 + BIT3)) { 21698c2ecf20Sopenharmony_ci // another transmit buffer has completed 21708c2ecf20Sopenharmony_ci // run bottom half to get more send data from user 21718c2ecf20Sopenharmony_ci info->pending_bh |= BH_TRANSMIT; 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci} 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci/* 21768c2ecf20Sopenharmony_ci * return true if there are unsent tx DMA buffers, otherwise false 21778c2ecf20Sopenharmony_ci * 21788c2ecf20Sopenharmony_ci * if there are unsent buffers then info->tbuf_start 21798c2ecf20Sopenharmony_ci * is set to index of first unsent buffer 21808c2ecf20Sopenharmony_ci */ 21818c2ecf20Sopenharmony_cistatic bool unsent_tbufs(struct slgt_info *info) 21828c2ecf20Sopenharmony_ci{ 21838c2ecf20Sopenharmony_ci unsigned int i = info->tbuf_current; 21848c2ecf20Sopenharmony_ci bool rc = false; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci /* 21878c2ecf20Sopenharmony_ci * search backwards from last loaded buffer (precedes tbuf_current) 21888c2ecf20Sopenharmony_ci * for first unsent buffer (desc_count > 0) 21898c2ecf20Sopenharmony_ci */ 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci do { 21928c2ecf20Sopenharmony_ci if (i) 21938c2ecf20Sopenharmony_ci i--; 21948c2ecf20Sopenharmony_ci else 21958c2ecf20Sopenharmony_ci i = info->tbuf_count - 1; 21968c2ecf20Sopenharmony_ci if (!desc_count(info->tbufs[i])) 21978c2ecf20Sopenharmony_ci break; 21988c2ecf20Sopenharmony_ci info->tbuf_start = i; 21998c2ecf20Sopenharmony_ci rc = true; 22008c2ecf20Sopenharmony_ci } while (i != info->tbuf_current); 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci return rc; 22038c2ecf20Sopenharmony_ci} 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_cistatic void isr_txeom(struct slgt_info *info, unsigned short status) 22068c2ecf20Sopenharmony_ci{ 22078c2ecf20Sopenharmony_ci DBGISR(("%s txeom status=%04x\n", info->device_name, status)); 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); 22108c2ecf20Sopenharmony_ci tdma_reset(info); 22118c2ecf20Sopenharmony_ci if (status & IRQ_TXUNDER) { 22128c2ecf20Sopenharmony_ci unsigned short val = rd_reg16(info, TCR); 22138c2ecf20Sopenharmony_ci wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ 22148c2ecf20Sopenharmony_ci wr_reg16(info, TCR, val); /* clear reset bit */ 22158c2ecf20Sopenharmony_ci } 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if (info->tx_active) { 22188c2ecf20Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 22198c2ecf20Sopenharmony_ci if (status & IRQ_TXUNDER) 22208c2ecf20Sopenharmony_ci info->icount.txunder++; 22218c2ecf20Sopenharmony_ci else if (status & IRQ_TXIDLE) 22228c2ecf20Sopenharmony_ci info->icount.txok++; 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci if (unsent_tbufs(info)) { 22268c2ecf20Sopenharmony_ci tx_start(info); 22278c2ecf20Sopenharmony_ci update_tx_timer(info); 22288c2ecf20Sopenharmony_ci return; 22298c2ecf20Sopenharmony_ci } 22308c2ecf20Sopenharmony_ci info->tx_active = false; 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci del_timer(&info->tx_timer); 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) { 22358c2ecf20Sopenharmony_ci info->signals &= ~SerialSignal_RTS; 22368c2ecf20Sopenharmony_ci info->drop_rts_on_tx_done = false; 22378c2ecf20Sopenharmony_ci set_gtsignals(info); 22388c2ecf20Sopenharmony_ci } 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 22418c2ecf20Sopenharmony_ci if (info->netcount) 22428c2ecf20Sopenharmony_ci hdlcdev_tx_done(info); 22438c2ecf20Sopenharmony_ci else 22448c2ecf20Sopenharmony_ci#endif 22458c2ecf20Sopenharmony_ci { 22468c2ecf20Sopenharmony_ci if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) { 22478c2ecf20Sopenharmony_ci tx_stop(info); 22488c2ecf20Sopenharmony_ci return; 22498c2ecf20Sopenharmony_ci } 22508c2ecf20Sopenharmony_ci info->pending_bh |= BH_TRANSMIT; 22518c2ecf20Sopenharmony_ci } 22528c2ecf20Sopenharmony_ci } 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_cistatic void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state) 22568c2ecf20Sopenharmony_ci{ 22578c2ecf20Sopenharmony_ci struct cond_wait *w, *prev; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci /* wake processes waiting for specific transitions */ 22608c2ecf20Sopenharmony_ci for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) { 22618c2ecf20Sopenharmony_ci if (w->data & changed) { 22628c2ecf20Sopenharmony_ci w->data = state; 22638c2ecf20Sopenharmony_ci wake_up_interruptible(&w->q); 22648c2ecf20Sopenharmony_ci if (prev != NULL) 22658c2ecf20Sopenharmony_ci prev->next = w->next; 22668c2ecf20Sopenharmony_ci else 22678c2ecf20Sopenharmony_ci info->gpio_wait_q = w->next; 22688c2ecf20Sopenharmony_ci } else 22698c2ecf20Sopenharmony_ci prev = w; 22708c2ecf20Sopenharmony_ci } 22718c2ecf20Sopenharmony_ci} 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci/* interrupt service routine 22748c2ecf20Sopenharmony_ci * 22758c2ecf20Sopenharmony_ci * irq interrupt number 22768c2ecf20Sopenharmony_ci * dev_id device ID supplied during interrupt registration 22778c2ecf20Sopenharmony_ci */ 22788c2ecf20Sopenharmony_cistatic irqreturn_t slgt_interrupt(int dummy, void *dev_id) 22798c2ecf20Sopenharmony_ci{ 22808c2ecf20Sopenharmony_ci struct slgt_info *info = dev_id; 22818c2ecf20Sopenharmony_ci unsigned int gsr; 22828c2ecf20Sopenharmony_ci unsigned int i; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level)); 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci while((gsr = rd_reg32(info, GSR) & 0xffffff00)) { 22878c2ecf20Sopenharmony_ci DBGISR(("%s gsr=%08x\n", info->device_name, gsr)); 22888c2ecf20Sopenharmony_ci info->irq_occurred = true; 22898c2ecf20Sopenharmony_ci for(i=0; i < info->port_count ; i++) { 22908c2ecf20Sopenharmony_ci if (info->port_array[i] == NULL) 22918c2ecf20Sopenharmony_ci continue; 22928c2ecf20Sopenharmony_ci spin_lock(&info->port_array[i]->lock); 22938c2ecf20Sopenharmony_ci if (gsr & (BIT8 << i)) 22948c2ecf20Sopenharmony_ci isr_serial(info->port_array[i]); 22958c2ecf20Sopenharmony_ci if (gsr & (BIT16 << (i*2))) 22968c2ecf20Sopenharmony_ci isr_rdma(info->port_array[i]); 22978c2ecf20Sopenharmony_ci if (gsr & (BIT17 << (i*2))) 22988c2ecf20Sopenharmony_ci isr_tdma(info->port_array[i]); 22998c2ecf20Sopenharmony_ci spin_unlock(&info->port_array[i]->lock); 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ci } 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci if (info->gpio_present) { 23048c2ecf20Sopenharmony_ci unsigned int state; 23058c2ecf20Sopenharmony_ci unsigned int changed; 23068c2ecf20Sopenharmony_ci spin_lock(&info->lock); 23078c2ecf20Sopenharmony_ci while ((changed = rd_reg32(info, IOSR)) != 0) { 23088c2ecf20Sopenharmony_ci DBGISR(("%s iosr=%08x\n", info->device_name, changed)); 23098c2ecf20Sopenharmony_ci /* read latched state of GPIO signals */ 23108c2ecf20Sopenharmony_ci state = rd_reg32(info, IOVR); 23118c2ecf20Sopenharmony_ci /* clear pending GPIO interrupt bits */ 23128c2ecf20Sopenharmony_ci wr_reg32(info, IOSR, changed); 23138c2ecf20Sopenharmony_ci for (i=0 ; i < info->port_count ; i++) { 23148c2ecf20Sopenharmony_ci if (info->port_array[i] != NULL) 23158c2ecf20Sopenharmony_ci isr_gpio(info->port_array[i], changed, state); 23168c2ecf20Sopenharmony_ci } 23178c2ecf20Sopenharmony_ci } 23188c2ecf20Sopenharmony_ci spin_unlock(&info->lock); 23198c2ecf20Sopenharmony_ci } 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci for(i=0; i < info->port_count ; i++) { 23228c2ecf20Sopenharmony_ci struct slgt_info *port = info->port_array[i]; 23238c2ecf20Sopenharmony_ci if (port == NULL) 23248c2ecf20Sopenharmony_ci continue; 23258c2ecf20Sopenharmony_ci spin_lock(&port->lock); 23268c2ecf20Sopenharmony_ci if ((port->port.count || port->netcount) && 23278c2ecf20Sopenharmony_ci port->pending_bh && !port->bh_running && 23288c2ecf20Sopenharmony_ci !port->bh_requested) { 23298c2ecf20Sopenharmony_ci DBGISR(("%s bh queued\n", port->device_name)); 23308c2ecf20Sopenharmony_ci schedule_work(&port->task); 23318c2ecf20Sopenharmony_ci port->bh_requested = true; 23328c2ecf20Sopenharmony_ci } 23338c2ecf20Sopenharmony_ci spin_unlock(&port->lock); 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level)); 23378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 23388c2ecf20Sopenharmony_ci} 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_cistatic int startup(struct slgt_info *info) 23418c2ecf20Sopenharmony_ci{ 23428c2ecf20Sopenharmony_ci DBGINFO(("%s startup\n", info->device_name)); 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci if (tty_port_initialized(&info->port)) 23458c2ecf20Sopenharmony_ci return 0; 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci if (!info->tx_buf) { 23488c2ecf20Sopenharmony_ci info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); 23498c2ecf20Sopenharmony_ci if (!info->tx_buf) { 23508c2ecf20Sopenharmony_ci DBGERR(("%s can't allocate tx buffer\n", info->device_name)); 23518c2ecf20Sopenharmony_ci return -ENOMEM; 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci } 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci info->pending_bh = 0; 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci memset(&info->icount, 0, sizeof(info->icount)); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci /* program hardware for current parameters */ 23608c2ecf20Sopenharmony_ci change_params(info); 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci if (info->port.tty) 23638c2ecf20Sopenharmony_ci clear_bit(TTY_IO_ERROR, &info->port.tty->flags); 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci tty_port_set_initialized(&info->port, 1); 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci return 0; 23688c2ecf20Sopenharmony_ci} 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci/* 23718c2ecf20Sopenharmony_ci * called by close() and hangup() to shutdown hardware 23728c2ecf20Sopenharmony_ci */ 23738c2ecf20Sopenharmony_cistatic void shutdown(struct slgt_info *info) 23748c2ecf20Sopenharmony_ci{ 23758c2ecf20Sopenharmony_ci unsigned long flags; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci if (!tty_port_initialized(&info->port)) 23788c2ecf20Sopenharmony_ci return; 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci DBGINFO(("%s shutdown\n", info->device_name)); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci /* clear status wait queue because status changes */ 23838c2ecf20Sopenharmony_ci /* can't happen after shutting down the hardware */ 23848c2ecf20Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 23858c2ecf20Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci del_timer_sync(&info->tx_timer); 23888c2ecf20Sopenharmony_ci del_timer_sync(&info->rx_timer); 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci kfree(info->tx_buf); 23918c2ecf20Sopenharmony_ci info->tx_buf = NULL; 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci tx_stop(info); 23968c2ecf20Sopenharmony_ci rx_stop(info); 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { 24018c2ecf20Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 24028c2ecf20Sopenharmony_ci set_gtsignals(info); 24038c2ecf20Sopenharmony_ci } 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci flush_cond_wait(&info->gpio_wait_q); 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci if (info->port.tty) 24108c2ecf20Sopenharmony_ci set_bit(TTY_IO_ERROR, &info->port.tty->flags); 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci tty_port_set_initialized(&info->port, 0); 24138c2ecf20Sopenharmony_ci} 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_cistatic void program_hw(struct slgt_info *info) 24168c2ecf20Sopenharmony_ci{ 24178c2ecf20Sopenharmony_ci unsigned long flags; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci rx_stop(info); 24228c2ecf20Sopenharmony_ci tx_stop(info); 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC || 24258c2ecf20Sopenharmony_ci info->netcount) 24268c2ecf20Sopenharmony_ci sync_mode(info); 24278c2ecf20Sopenharmony_ci else 24288c2ecf20Sopenharmony_ci async_mode(info); 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_ci set_gtsignals(info); 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci info->dcd_chkcount = 0; 24338c2ecf20Sopenharmony_ci info->cts_chkcount = 0; 24348c2ecf20Sopenharmony_ci info->ri_chkcount = 0; 24358c2ecf20Sopenharmony_ci info->dsr_chkcount = 0; 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI); 24388c2ecf20Sopenharmony_ci get_gtsignals(info); 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci if (info->netcount || 24418c2ecf20Sopenharmony_ci (info->port.tty && info->port.tty->termios.c_cflag & CREAD)) 24428c2ecf20Sopenharmony_ci rx_start(info); 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 24458c2ecf20Sopenharmony_ci} 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci/* 24488c2ecf20Sopenharmony_ci * reconfigure adapter based on new parameters 24498c2ecf20Sopenharmony_ci */ 24508c2ecf20Sopenharmony_cistatic void change_params(struct slgt_info *info) 24518c2ecf20Sopenharmony_ci{ 24528c2ecf20Sopenharmony_ci unsigned cflag; 24538c2ecf20Sopenharmony_ci int bits_per_char; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci if (!info->port.tty) 24568c2ecf20Sopenharmony_ci return; 24578c2ecf20Sopenharmony_ci DBGINFO(("%s change_params\n", info->device_name)); 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci cflag = info->port.tty->termios.c_cflag; 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci /* if B0 rate (hangup) specified then negate RTS and DTR */ 24628c2ecf20Sopenharmony_ci /* otherwise assert RTS and DTR */ 24638c2ecf20Sopenharmony_ci if (cflag & CBAUD) 24648c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RTS | SerialSignal_DTR; 24658c2ecf20Sopenharmony_ci else 24668c2ecf20Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci /* byte size and parity */ 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 24718c2ecf20Sopenharmony_ci case CS5: info->params.data_bits = 5; break; 24728c2ecf20Sopenharmony_ci case CS6: info->params.data_bits = 6; break; 24738c2ecf20Sopenharmony_ci case CS7: info->params.data_bits = 7; break; 24748c2ecf20Sopenharmony_ci case CS8: info->params.data_bits = 8; break; 24758c2ecf20Sopenharmony_ci default: info->params.data_bits = 7; break; 24768c2ecf20Sopenharmony_ci } 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci if (cflag & PARENB) 24818c2ecf20Sopenharmony_ci info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN; 24828c2ecf20Sopenharmony_ci else 24838c2ecf20Sopenharmony_ci info->params.parity = ASYNC_PARITY_NONE; 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci /* calculate number of jiffies to transmit a full 24868c2ecf20Sopenharmony_ci * FIFO (32 bytes) at specified data rate 24878c2ecf20Sopenharmony_ci */ 24888c2ecf20Sopenharmony_ci bits_per_char = info->params.data_bits + 24898c2ecf20Sopenharmony_ci info->params.stop_bits + 1; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci info->params.data_rate = tty_get_baud_rate(info->port.tty); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci if (info->params.data_rate) { 24948c2ecf20Sopenharmony_ci info->timeout = (32*HZ*bits_per_char) / 24958c2ecf20Sopenharmony_ci info->params.data_rate; 24968c2ecf20Sopenharmony_ci } 24978c2ecf20Sopenharmony_ci info->timeout += HZ/50; /* Add .02 seconds of slop */ 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); 25008c2ecf20Sopenharmony_ci tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci /* process tty input control flags */ 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci info->read_status_mask = IRQ_RXOVER; 25058c2ecf20Sopenharmony_ci if (I_INPCK(info->port.tty)) 25068c2ecf20Sopenharmony_ci info->read_status_mask |= MASK_PARITY | MASK_FRAMING; 25078c2ecf20Sopenharmony_ci if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) 25088c2ecf20Sopenharmony_ci info->read_status_mask |= MASK_BREAK; 25098c2ecf20Sopenharmony_ci if (I_IGNPAR(info->port.tty)) 25108c2ecf20Sopenharmony_ci info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING; 25118c2ecf20Sopenharmony_ci if (I_IGNBRK(info->port.tty)) { 25128c2ecf20Sopenharmony_ci info->ignore_status_mask |= MASK_BREAK; 25138c2ecf20Sopenharmony_ci /* If ignoring parity and break indicators, ignore 25148c2ecf20Sopenharmony_ci * overruns too. (For real raw support). 25158c2ecf20Sopenharmony_ci */ 25168c2ecf20Sopenharmony_ci if (I_IGNPAR(info->port.tty)) 25178c2ecf20Sopenharmony_ci info->ignore_status_mask |= MASK_OVERRUN; 25188c2ecf20Sopenharmony_ci } 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci program_hw(info); 25218c2ecf20Sopenharmony_ci} 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_cistatic int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount) 25248c2ecf20Sopenharmony_ci{ 25258c2ecf20Sopenharmony_ci DBGINFO(("%s get_stats\n", info->device_name)); 25268c2ecf20Sopenharmony_ci if (!user_icount) { 25278c2ecf20Sopenharmony_ci memset(&info->icount, 0, sizeof(info->icount)); 25288c2ecf20Sopenharmony_ci } else { 25298c2ecf20Sopenharmony_ci if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount))) 25308c2ecf20Sopenharmony_ci return -EFAULT; 25318c2ecf20Sopenharmony_ci } 25328c2ecf20Sopenharmony_ci return 0; 25338c2ecf20Sopenharmony_ci} 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_cistatic int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params) 25368c2ecf20Sopenharmony_ci{ 25378c2ecf20Sopenharmony_ci DBGINFO(("%s get_params\n", info->device_name)); 25388c2ecf20Sopenharmony_ci if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS))) 25398c2ecf20Sopenharmony_ci return -EFAULT; 25408c2ecf20Sopenharmony_ci return 0; 25418c2ecf20Sopenharmony_ci} 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_cistatic int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params) 25448c2ecf20Sopenharmony_ci{ 25458c2ecf20Sopenharmony_ci unsigned long flags; 25468c2ecf20Sopenharmony_ci MGSL_PARAMS tmp_params; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci DBGINFO(("%s set_params\n", info->device_name)); 25498c2ecf20Sopenharmony_ci if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS))) 25508c2ecf20Sopenharmony_ci return -EFAULT; 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 25538c2ecf20Sopenharmony_ci if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) 25548c2ecf20Sopenharmony_ci info->base_clock = tmp_params.clock_speed; 25558c2ecf20Sopenharmony_ci else 25568c2ecf20Sopenharmony_ci memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS)); 25578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci program_hw(info); 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci return 0; 25628c2ecf20Sopenharmony_ci} 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_cistatic int get_txidle(struct slgt_info *info, int __user *idle_mode) 25658c2ecf20Sopenharmony_ci{ 25668c2ecf20Sopenharmony_ci DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode)); 25678c2ecf20Sopenharmony_ci if (put_user(info->idle_mode, idle_mode)) 25688c2ecf20Sopenharmony_ci return -EFAULT; 25698c2ecf20Sopenharmony_ci return 0; 25708c2ecf20Sopenharmony_ci} 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_cistatic int set_txidle(struct slgt_info *info, int idle_mode) 25738c2ecf20Sopenharmony_ci{ 25748c2ecf20Sopenharmony_ci unsigned long flags; 25758c2ecf20Sopenharmony_ci DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode)); 25768c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 25778c2ecf20Sopenharmony_ci info->idle_mode = idle_mode; 25788c2ecf20Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) 25798c2ecf20Sopenharmony_ci tx_set_idle(info); 25808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 25818c2ecf20Sopenharmony_ci return 0; 25828c2ecf20Sopenharmony_ci} 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_cistatic int tx_enable(struct slgt_info *info, int enable) 25858c2ecf20Sopenharmony_ci{ 25868c2ecf20Sopenharmony_ci unsigned long flags; 25878c2ecf20Sopenharmony_ci DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable)); 25888c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 25898c2ecf20Sopenharmony_ci if (enable) { 25908c2ecf20Sopenharmony_ci if (!info->tx_enabled) 25918c2ecf20Sopenharmony_ci tx_start(info); 25928c2ecf20Sopenharmony_ci } else { 25938c2ecf20Sopenharmony_ci if (info->tx_enabled) 25948c2ecf20Sopenharmony_ci tx_stop(info); 25958c2ecf20Sopenharmony_ci } 25968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 25978c2ecf20Sopenharmony_ci return 0; 25988c2ecf20Sopenharmony_ci} 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci/* 26018c2ecf20Sopenharmony_ci * abort transmit HDLC frame 26028c2ecf20Sopenharmony_ci */ 26038c2ecf20Sopenharmony_cistatic int tx_abort(struct slgt_info *info) 26048c2ecf20Sopenharmony_ci{ 26058c2ecf20Sopenharmony_ci unsigned long flags; 26068c2ecf20Sopenharmony_ci DBGINFO(("%s tx_abort\n", info->device_name)); 26078c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 26088c2ecf20Sopenharmony_ci tdma_reset(info); 26098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 26108c2ecf20Sopenharmony_ci return 0; 26118c2ecf20Sopenharmony_ci} 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_cistatic int rx_enable(struct slgt_info *info, int enable) 26148c2ecf20Sopenharmony_ci{ 26158c2ecf20Sopenharmony_ci unsigned long flags; 26168c2ecf20Sopenharmony_ci unsigned int rbuf_fill_level; 26178c2ecf20Sopenharmony_ci DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable)); 26188c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 26198c2ecf20Sopenharmony_ci /* 26208c2ecf20Sopenharmony_ci * enable[31..16] = receive DMA buffer fill level 26218c2ecf20Sopenharmony_ci * 0 = noop (leave fill level unchanged) 26228c2ecf20Sopenharmony_ci * fill level must be multiple of 4 and <= buffer size 26238c2ecf20Sopenharmony_ci */ 26248c2ecf20Sopenharmony_ci rbuf_fill_level = ((unsigned int)enable) >> 16; 26258c2ecf20Sopenharmony_ci if (rbuf_fill_level) { 26268c2ecf20Sopenharmony_ci if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) { 26278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 26288c2ecf20Sopenharmony_ci return -EINVAL; 26298c2ecf20Sopenharmony_ci } 26308c2ecf20Sopenharmony_ci info->rbuf_fill_level = rbuf_fill_level; 26318c2ecf20Sopenharmony_ci if (rbuf_fill_level < 128) 26328c2ecf20Sopenharmony_ci info->rx_pio = 1; /* PIO mode */ 26338c2ecf20Sopenharmony_ci else 26348c2ecf20Sopenharmony_ci info->rx_pio = 0; /* DMA mode */ 26358c2ecf20Sopenharmony_ci rx_stop(info); /* restart receiver to use new fill level */ 26368c2ecf20Sopenharmony_ci } 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci /* 26398c2ecf20Sopenharmony_ci * enable[1..0] = receiver enable command 26408c2ecf20Sopenharmony_ci * 0 = disable 26418c2ecf20Sopenharmony_ci * 1 = enable 26428c2ecf20Sopenharmony_ci * 2 = enable or force hunt mode if already enabled 26438c2ecf20Sopenharmony_ci */ 26448c2ecf20Sopenharmony_ci enable &= 3; 26458c2ecf20Sopenharmony_ci if (enable) { 26468c2ecf20Sopenharmony_ci if (!info->rx_enabled) 26478c2ecf20Sopenharmony_ci rx_start(info); 26488c2ecf20Sopenharmony_ci else if (enable == 2) { 26498c2ecf20Sopenharmony_ci /* force hunt mode (write 1 to RCR[3]) */ 26508c2ecf20Sopenharmony_ci wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3); 26518c2ecf20Sopenharmony_ci } 26528c2ecf20Sopenharmony_ci } else { 26538c2ecf20Sopenharmony_ci if (info->rx_enabled) 26548c2ecf20Sopenharmony_ci rx_stop(info); 26558c2ecf20Sopenharmony_ci } 26568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 26578c2ecf20Sopenharmony_ci return 0; 26588c2ecf20Sopenharmony_ci} 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci/* 26618c2ecf20Sopenharmony_ci * wait for specified event to occur 26628c2ecf20Sopenharmony_ci */ 26638c2ecf20Sopenharmony_cistatic int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr) 26648c2ecf20Sopenharmony_ci{ 26658c2ecf20Sopenharmony_ci unsigned long flags; 26668c2ecf20Sopenharmony_ci int s; 26678c2ecf20Sopenharmony_ci int rc=0; 26688c2ecf20Sopenharmony_ci struct mgsl_icount cprev, cnow; 26698c2ecf20Sopenharmony_ci int events; 26708c2ecf20Sopenharmony_ci int mask; 26718c2ecf20Sopenharmony_ci struct _input_signal_events oldsigs, newsigs; 26728c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci if (get_user(mask, mask_ptr)) 26758c2ecf20Sopenharmony_ci return -EFAULT; 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask)); 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci /* return immediately if state matches requested events */ 26828c2ecf20Sopenharmony_ci get_gtsignals(info); 26838c2ecf20Sopenharmony_ci s = info->signals; 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci events = mask & 26868c2ecf20Sopenharmony_ci ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + 26878c2ecf20Sopenharmony_ci ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + 26888c2ecf20Sopenharmony_ci ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + 26898c2ecf20Sopenharmony_ci ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); 26908c2ecf20Sopenharmony_ci if (events) { 26918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 26928c2ecf20Sopenharmony_ci goto exit; 26938c2ecf20Sopenharmony_ci } 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci /* save current irq counts */ 26968c2ecf20Sopenharmony_ci cprev = info->icount; 26978c2ecf20Sopenharmony_ci oldsigs = info->input_signal_events; 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_ci /* enable hunt and idle irqs if needed */ 27008c2ecf20Sopenharmony_ci if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) { 27018c2ecf20Sopenharmony_ci unsigned short val = rd_reg16(info, SCR); 27028c2ecf20Sopenharmony_ci if (!(val & IRQ_RXIDLE)) 27038c2ecf20Sopenharmony_ci wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE)); 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 27078c2ecf20Sopenharmony_ci add_wait_queue(&info->event_wait_q, &wait); 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci for(;;) { 27128c2ecf20Sopenharmony_ci schedule(); 27138c2ecf20Sopenharmony_ci if (signal_pending(current)) { 27148c2ecf20Sopenharmony_ci rc = -ERESTARTSYS; 27158c2ecf20Sopenharmony_ci break; 27168c2ecf20Sopenharmony_ci } 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci /* get current irq counts */ 27198c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 27208c2ecf20Sopenharmony_ci cnow = info->icount; 27218c2ecf20Sopenharmony_ci newsigs = info->input_signal_events; 27228c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 27238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_ci /* if no change, wait aborted for some reason */ 27268c2ecf20Sopenharmony_ci if (newsigs.dsr_up == oldsigs.dsr_up && 27278c2ecf20Sopenharmony_ci newsigs.dsr_down == oldsigs.dsr_down && 27288c2ecf20Sopenharmony_ci newsigs.dcd_up == oldsigs.dcd_up && 27298c2ecf20Sopenharmony_ci newsigs.dcd_down == oldsigs.dcd_down && 27308c2ecf20Sopenharmony_ci newsigs.cts_up == oldsigs.cts_up && 27318c2ecf20Sopenharmony_ci newsigs.cts_down == oldsigs.cts_down && 27328c2ecf20Sopenharmony_ci newsigs.ri_up == oldsigs.ri_up && 27338c2ecf20Sopenharmony_ci newsigs.ri_down == oldsigs.ri_down && 27348c2ecf20Sopenharmony_ci cnow.exithunt == cprev.exithunt && 27358c2ecf20Sopenharmony_ci cnow.rxidle == cprev.rxidle) { 27368c2ecf20Sopenharmony_ci rc = -EIO; 27378c2ecf20Sopenharmony_ci break; 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci events = mask & 27418c2ecf20Sopenharmony_ci ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + 27428c2ecf20Sopenharmony_ci (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + 27438c2ecf20Sopenharmony_ci (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + 27448c2ecf20Sopenharmony_ci (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + 27458c2ecf20Sopenharmony_ci (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + 27468c2ecf20Sopenharmony_ci (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + 27478c2ecf20Sopenharmony_ci (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + 27488c2ecf20Sopenharmony_ci (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + 27498c2ecf20Sopenharmony_ci (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + 27508c2ecf20Sopenharmony_ci (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); 27518c2ecf20Sopenharmony_ci if (events) 27528c2ecf20Sopenharmony_ci break; 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci cprev = cnow; 27558c2ecf20Sopenharmony_ci oldsigs = newsigs; 27568c2ecf20Sopenharmony_ci } 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci remove_wait_queue(&info->event_wait_q, &wait); 27598c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { 27638c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 27648c2ecf20Sopenharmony_ci if (!waitqueue_active(&info->event_wait_q)) { 27658c2ecf20Sopenharmony_ci /* disable enable exit hunt mode/idle rcvd IRQs */ 27668c2ecf20Sopenharmony_ci wr_reg16(info, SCR, 27678c2ecf20Sopenharmony_ci (unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE)); 27688c2ecf20Sopenharmony_ci } 27698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 27708c2ecf20Sopenharmony_ci } 27718c2ecf20Sopenharmony_ciexit: 27728c2ecf20Sopenharmony_ci if (rc == 0) 27738c2ecf20Sopenharmony_ci rc = put_user(events, mask_ptr); 27748c2ecf20Sopenharmony_ci return rc; 27758c2ecf20Sopenharmony_ci} 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_cistatic int get_interface(struct slgt_info *info, int __user *if_mode) 27788c2ecf20Sopenharmony_ci{ 27798c2ecf20Sopenharmony_ci DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode)); 27808c2ecf20Sopenharmony_ci if (put_user(info->if_mode, if_mode)) 27818c2ecf20Sopenharmony_ci return -EFAULT; 27828c2ecf20Sopenharmony_ci return 0; 27838c2ecf20Sopenharmony_ci} 27848c2ecf20Sopenharmony_ci 27858c2ecf20Sopenharmony_cistatic int set_interface(struct slgt_info *info, int if_mode) 27868c2ecf20Sopenharmony_ci{ 27878c2ecf20Sopenharmony_ci unsigned long flags; 27888c2ecf20Sopenharmony_ci unsigned short val; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode)); 27918c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 27928c2ecf20Sopenharmony_ci info->if_mode = if_mode; 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci msc_set_vcr(info); 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci /* TCR (tx control) 07 1=RTS driver control */ 27978c2ecf20Sopenharmony_ci val = rd_reg16(info, TCR); 27988c2ecf20Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_RTS_EN) 27998c2ecf20Sopenharmony_ci val |= BIT7; 28008c2ecf20Sopenharmony_ci else 28018c2ecf20Sopenharmony_ci val &= ~BIT7; 28028c2ecf20Sopenharmony_ci wr_reg16(info, TCR, val); 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 28058c2ecf20Sopenharmony_ci return 0; 28068c2ecf20Sopenharmony_ci} 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_cistatic int get_xsync(struct slgt_info *info, int __user *xsync) 28098c2ecf20Sopenharmony_ci{ 28108c2ecf20Sopenharmony_ci DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync)); 28118c2ecf20Sopenharmony_ci if (put_user(info->xsync, xsync)) 28128c2ecf20Sopenharmony_ci return -EFAULT; 28138c2ecf20Sopenharmony_ci return 0; 28148c2ecf20Sopenharmony_ci} 28158c2ecf20Sopenharmony_ci 28168c2ecf20Sopenharmony_ci/* 28178c2ecf20Sopenharmony_ci * set extended sync pattern (1 to 4 bytes) for extended sync mode 28188c2ecf20Sopenharmony_ci * 28198c2ecf20Sopenharmony_ci * sync pattern is contained in least significant bytes of value 28208c2ecf20Sopenharmony_ci * most significant byte of sync pattern is oldest (1st sent/detected) 28218c2ecf20Sopenharmony_ci */ 28228c2ecf20Sopenharmony_cistatic int set_xsync(struct slgt_info *info, int xsync) 28238c2ecf20Sopenharmony_ci{ 28248c2ecf20Sopenharmony_ci unsigned long flags; 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync)); 28278c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 28288c2ecf20Sopenharmony_ci info->xsync = xsync; 28298c2ecf20Sopenharmony_ci wr_reg32(info, XSR, xsync); 28308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 28318c2ecf20Sopenharmony_ci return 0; 28328c2ecf20Sopenharmony_ci} 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_cistatic int get_xctrl(struct slgt_info *info, int __user *xctrl) 28358c2ecf20Sopenharmony_ci{ 28368c2ecf20Sopenharmony_ci DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl)); 28378c2ecf20Sopenharmony_ci if (put_user(info->xctrl, xctrl)) 28388c2ecf20Sopenharmony_ci return -EFAULT; 28398c2ecf20Sopenharmony_ci return 0; 28408c2ecf20Sopenharmony_ci} 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci/* 28438c2ecf20Sopenharmony_ci * set extended control options 28448c2ecf20Sopenharmony_ci * 28458c2ecf20Sopenharmony_ci * xctrl[31:19] reserved, must be zero 28468c2ecf20Sopenharmony_ci * xctrl[18:17] extended sync pattern length in bytes 28478c2ecf20Sopenharmony_ci * 00 = 1 byte in xsr[7:0] 28488c2ecf20Sopenharmony_ci * 01 = 2 bytes in xsr[15:0] 28498c2ecf20Sopenharmony_ci * 10 = 3 bytes in xsr[23:0] 28508c2ecf20Sopenharmony_ci * 11 = 4 bytes in xsr[31:0] 28518c2ecf20Sopenharmony_ci * xctrl[16] 1 = enable terminal count, 0=disabled 28528c2ecf20Sopenharmony_ci * xctrl[15:0] receive terminal count for fixed length packets 28538c2ecf20Sopenharmony_ci * value is count minus one (0 = 1 byte packet) 28548c2ecf20Sopenharmony_ci * when terminal count is reached, receiver 28558c2ecf20Sopenharmony_ci * automatically returns to hunt mode and receive 28568c2ecf20Sopenharmony_ci * FIFO contents are flushed to DMA buffers with 28578c2ecf20Sopenharmony_ci * end of frame (EOF) status 28588c2ecf20Sopenharmony_ci */ 28598c2ecf20Sopenharmony_cistatic int set_xctrl(struct slgt_info *info, int xctrl) 28608c2ecf20Sopenharmony_ci{ 28618c2ecf20Sopenharmony_ci unsigned long flags; 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl)); 28648c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 28658c2ecf20Sopenharmony_ci info->xctrl = xctrl; 28668c2ecf20Sopenharmony_ci wr_reg32(info, XCR, xctrl); 28678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 28688c2ecf20Sopenharmony_ci return 0; 28698c2ecf20Sopenharmony_ci} 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci/* 28728c2ecf20Sopenharmony_ci * set general purpose IO pin state and direction 28738c2ecf20Sopenharmony_ci * 28748c2ecf20Sopenharmony_ci * user_gpio fields: 28758c2ecf20Sopenharmony_ci * state each bit indicates a pin state 28768c2ecf20Sopenharmony_ci * smask set bit indicates pin state to set 28778c2ecf20Sopenharmony_ci * dir each bit indicates a pin direction (0=input, 1=output) 28788c2ecf20Sopenharmony_ci * dmask set bit indicates pin direction to set 28798c2ecf20Sopenharmony_ci */ 28808c2ecf20Sopenharmony_cistatic int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) 28818c2ecf20Sopenharmony_ci{ 28828c2ecf20Sopenharmony_ci unsigned long flags; 28838c2ecf20Sopenharmony_ci struct gpio_desc gpio; 28848c2ecf20Sopenharmony_ci __u32 data; 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci if (!info->gpio_present) 28878c2ecf20Sopenharmony_ci return -EINVAL; 28888c2ecf20Sopenharmony_ci if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) 28898c2ecf20Sopenharmony_ci return -EFAULT; 28908c2ecf20Sopenharmony_ci DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n", 28918c2ecf20Sopenharmony_ci info->device_name, gpio.state, gpio.smask, 28928c2ecf20Sopenharmony_ci gpio.dir, gpio.dmask)); 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->port_array[0]->lock, flags); 28958c2ecf20Sopenharmony_ci if (gpio.dmask) { 28968c2ecf20Sopenharmony_ci data = rd_reg32(info, IODR); 28978c2ecf20Sopenharmony_ci data |= gpio.dmask & gpio.dir; 28988c2ecf20Sopenharmony_ci data &= ~(gpio.dmask & ~gpio.dir); 28998c2ecf20Sopenharmony_ci wr_reg32(info, IODR, data); 29008c2ecf20Sopenharmony_ci } 29018c2ecf20Sopenharmony_ci if (gpio.smask) { 29028c2ecf20Sopenharmony_ci data = rd_reg32(info, IOVR); 29038c2ecf20Sopenharmony_ci data |= gpio.smask & gpio.state; 29048c2ecf20Sopenharmony_ci data &= ~(gpio.smask & ~gpio.state); 29058c2ecf20Sopenharmony_ci wr_reg32(info, IOVR, data); 29068c2ecf20Sopenharmony_ci } 29078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->port_array[0]->lock, flags); 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci return 0; 29108c2ecf20Sopenharmony_ci} 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci/* 29138c2ecf20Sopenharmony_ci * get general purpose IO pin state and direction 29148c2ecf20Sopenharmony_ci */ 29158c2ecf20Sopenharmony_cistatic int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) 29168c2ecf20Sopenharmony_ci{ 29178c2ecf20Sopenharmony_ci struct gpio_desc gpio; 29188c2ecf20Sopenharmony_ci if (!info->gpio_present) 29198c2ecf20Sopenharmony_ci return -EINVAL; 29208c2ecf20Sopenharmony_ci gpio.state = rd_reg32(info, IOVR); 29218c2ecf20Sopenharmony_ci gpio.smask = 0xffffffff; 29228c2ecf20Sopenharmony_ci gpio.dir = rd_reg32(info, IODR); 29238c2ecf20Sopenharmony_ci gpio.dmask = 0xffffffff; 29248c2ecf20Sopenharmony_ci if (copy_to_user(user_gpio, &gpio, sizeof(gpio))) 29258c2ecf20Sopenharmony_ci return -EFAULT; 29268c2ecf20Sopenharmony_ci DBGINFO(("%s get_gpio state=%08x dir=%08x\n", 29278c2ecf20Sopenharmony_ci info->device_name, gpio.state, gpio.dir)); 29288c2ecf20Sopenharmony_ci return 0; 29298c2ecf20Sopenharmony_ci} 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci/* 29328c2ecf20Sopenharmony_ci * conditional wait facility 29338c2ecf20Sopenharmony_ci */ 29348c2ecf20Sopenharmony_cistatic void init_cond_wait(struct cond_wait *w, unsigned int data) 29358c2ecf20Sopenharmony_ci{ 29368c2ecf20Sopenharmony_ci init_waitqueue_head(&w->q); 29378c2ecf20Sopenharmony_ci init_waitqueue_entry(&w->wait, current); 29388c2ecf20Sopenharmony_ci w->data = data; 29398c2ecf20Sopenharmony_ci} 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_cistatic void add_cond_wait(struct cond_wait **head, struct cond_wait *w) 29428c2ecf20Sopenharmony_ci{ 29438c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 29448c2ecf20Sopenharmony_ci add_wait_queue(&w->q, &w->wait); 29458c2ecf20Sopenharmony_ci w->next = *head; 29468c2ecf20Sopenharmony_ci *head = w; 29478c2ecf20Sopenharmony_ci} 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_cistatic void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw) 29508c2ecf20Sopenharmony_ci{ 29518c2ecf20Sopenharmony_ci struct cond_wait *w, *prev; 29528c2ecf20Sopenharmony_ci remove_wait_queue(&cw->q, &cw->wait); 29538c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 29548c2ecf20Sopenharmony_ci for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) { 29558c2ecf20Sopenharmony_ci if (w == cw) { 29568c2ecf20Sopenharmony_ci if (prev != NULL) 29578c2ecf20Sopenharmony_ci prev->next = w->next; 29588c2ecf20Sopenharmony_ci else 29598c2ecf20Sopenharmony_ci *head = w->next; 29608c2ecf20Sopenharmony_ci break; 29618c2ecf20Sopenharmony_ci } 29628c2ecf20Sopenharmony_ci } 29638c2ecf20Sopenharmony_ci} 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_cistatic void flush_cond_wait(struct cond_wait **head) 29668c2ecf20Sopenharmony_ci{ 29678c2ecf20Sopenharmony_ci while (*head != NULL) { 29688c2ecf20Sopenharmony_ci wake_up_interruptible(&(*head)->q); 29698c2ecf20Sopenharmony_ci *head = (*head)->next; 29708c2ecf20Sopenharmony_ci } 29718c2ecf20Sopenharmony_ci} 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci/* 29748c2ecf20Sopenharmony_ci * wait for general purpose I/O pin(s) to enter specified state 29758c2ecf20Sopenharmony_ci * 29768c2ecf20Sopenharmony_ci * user_gpio fields: 29778c2ecf20Sopenharmony_ci * state - bit indicates target pin state 29788c2ecf20Sopenharmony_ci * smask - set bit indicates watched pin 29798c2ecf20Sopenharmony_ci * 29808c2ecf20Sopenharmony_ci * The wait ends when at least one watched pin enters the specified 29818c2ecf20Sopenharmony_ci * state. When 0 (no error) is returned, user_gpio->state is set to the 29828c2ecf20Sopenharmony_ci * state of all GPIO pins when the wait ends. 29838c2ecf20Sopenharmony_ci * 29848c2ecf20Sopenharmony_ci * Note: Each pin may be a dedicated input, dedicated output, or 29858c2ecf20Sopenharmony_ci * configurable input/output. The number and configuration of pins 29868c2ecf20Sopenharmony_ci * varies with the specific adapter model. Only input pins (dedicated 29878c2ecf20Sopenharmony_ci * or configured) can be monitored with this function. 29888c2ecf20Sopenharmony_ci */ 29898c2ecf20Sopenharmony_cistatic int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) 29908c2ecf20Sopenharmony_ci{ 29918c2ecf20Sopenharmony_ci unsigned long flags; 29928c2ecf20Sopenharmony_ci int rc = 0; 29938c2ecf20Sopenharmony_ci struct gpio_desc gpio; 29948c2ecf20Sopenharmony_ci struct cond_wait wait; 29958c2ecf20Sopenharmony_ci u32 state; 29968c2ecf20Sopenharmony_ci 29978c2ecf20Sopenharmony_ci if (!info->gpio_present) 29988c2ecf20Sopenharmony_ci return -EINVAL; 29998c2ecf20Sopenharmony_ci if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) 30008c2ecf20Sopenharmony_ci return -EFAULT; 30018c2ecf20Sopenharmony_ci DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n", 30028c2ecf20Sopenharmony_ci info->device_name, gpio.state, gpio.smask)); 30038c2ecf20Sopenharmony_ci /* ignore output pins identified by set IODR bit */ 30048c2ecf20Sopenharmony_ci if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0) 30058c2ecf20Sopenharmony_ci return -EINVAL; 30068c2ecf20Sopenharmony_ci init_cond_wait(&wait, gpio.smask); 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->port_array[0]->lock, flags); 30098c2ecf20Sopenharmony_ci /* enable interrupts for watched pins */ 30108c2ecf20Sopenharmony_ci wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask); 30118c2ecf20Sopenharmony_ci /* get current pin states */ 30128c2ecf20Sopenharmony_ci state = rd_reg32(info, IOVR); 30138c2ecf20Sopenharmony_ci 30148c2ecf20Sopenharmony_ci if (gpio.smask & ~(state ^ gpio.state)) { 30158c2ecf20Sopenharmony_ci /* already in target state */ 30168c2ecf20Sopenharmony_ci gpio.state = state; 30178c2ecf20Sopenharmony_ci } else { 30188c2ecf20Sopenharmony_ci /* wait for target state */ 30198c2ecf20Sopenharmony_ci add_cond_wait(&info->gpio_wait_q, &wait); 30208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->port_array[0]->lock, flags); 30218c2ecf20Sopenharmony_ci schedule(); 30228c2ecf20Sopenharmony_ci if (signal_pending(current)) 30238c2ecf20Sopenharmony_ci rc = -ERESTARTSYS; 30248c2ecf20Sopenharmony_ci else 30258c2ecf20Sopenharmony_ci gpio.state = wait.data; 30268c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->port_array[0]->lock, flags); 30278c2ecf20Sopenharmony_ci remove_cond_wait(&info->gpio_wait_q, &wait); 30288c2ecf20Sopenharmony_ci } 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci /* disable all GPIO interrupts if no waiting processes */ 30318c2ecf20Sopenharmony_ci if (info->gpio_wait_q == NULL) 30328c2ecf20Sopenharmony_ci wr_reg32(info, IOER, 0); 30338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->port_array[0]->lock, flags); 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ci if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio))) 30368c2ecf20Sopenharmony_ci rc = -EFAULT; 30378c2ecf20Sopenharmony_ci return rc; 30388c2ecf20Sopenharmony_ci} 30398c2ecf20Sopenharmony_ci 30408c2ecf20Sopenharmony_cistatic int modem_input_wait(struct slgt_info *info,int arg) 30418c2ecf20Sopenharmony_ci{ 30428c2ecf20Sopenharmony_ci unsigned long flags; 30438c2ecf20Sopenharmony_ci int rc; 30448c2ecf20Sopenharmony_ci struct mgsl_icount cprev, cnow; 30458c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_ci /* save current irq counts */ 30488c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 30498c2ecf20Sopenharmony_ci cprev = info->icount; 30508c2ecf20Sopenharmony_ci add_wait_queue(&info->status_event_wait_q, &wait); 30518c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 30528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci for(;;) { 30558c2ecf20Sopenharmony_ci schedule(); 30568c2ecf20Sopenharmony_ci if (signal_pending(current)) { 30578c2ecf20Sopenharmony_ci rc = -ERESTARTSYS; 30588c2ecf20Sopenharmony_ci break; 30598c2ecf20Sopenharmony_ci } 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci /* get new irq counts */ 30628c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 30638c2ecf20Sopenharmony_ci cnow = info->icount; 30648c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 30658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci /* if no change, wait aborted for some reason */ 30688c2ecf20Sopenharmony_ci if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 30698c2ecf20Sopenharmony_ci cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { 30708c2ecf20Sopenharmony_ci rc = -EIO; 30718c2ecf20Sopenharmony_ci break; 30728c2ecf20Sopenharmony_ci } 30738c2ecf20Sopenharmony_ci 30748c2ecf20Sopenharmony_ci /* check for change in caller specified modem input */ 30758c2ecf20Sopenharmony_ci if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || 30768c2ecf20Sopenharmony_ci (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || 30778c2ecf20Sopenharmony_ci (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || 30788c2ecf20Sopenharmony_ci (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { 30798c2ecf20Sopenharmony_ci rc = 0; 30808c2ecf20Sopenharmony_ci break; 30818c2ecf20Sopenharmony_ci } 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci cprev = cnow; 30848c2ecf20Sopenharmony_ci } 30858c2ecf20Sopenharmony_ci remove_wait_queue(&info->status_event_wait_q, &wait); 30868c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 30878c2ecf20Sopenharmony_ci return rc; 30888c2ecf20Sopenharmony_ci} 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci/* 30918c2ecf20Sopenharmony_ci * return state of serial control and status signals 30928c2ecf20Sopenharmony_ci */ 30938c2ecf20Sopenharmony_cistatic int tiocmget(struct tty_struct *tty) 30948c2ecf20Sopenharmony_ci{ 30958c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 30968c2ecf20Sopenharmony_ci unsigned int result; 30978c2ecf20Sopenharmony_ci unsigned long flags; 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 31008c2ecf20Sopenharmony_ci get_gtsignals(info); 31018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 31028c2ecf20Sopenharmony_ci 31038c2ecf20Sopenharmony_ci result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) + 31048c2ecf20Sopenharmony_ci ((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) + 31058c2ecf20Sopenharmony_ci ((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) + 31068c2ecf20Sopenharmony_ci ((info->signals & SerialSignal_RI) ? TIOCM_RNG:0) + 31078c2ecf20Sopenharmony_ci ((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) + 31088c2ecf20Sopenharmony_ci ((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0); 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result)); 31118c2ecf20Sopenharmony_ci return result; 31128c2ecf20Sopenharmony_ci} 31138c2ecf20Sopenharmony_ci 31148c2ecf20Sopenharmony_ci/* 31158c2ecf20Sopenharmony_ci * set modem control signals (DTR/RTS) 31168c2ecf20Sopenharmony_ci * 31178c2ecf20Sopenharmony_ci * cmd signal command: TIOCMBIS = set bit TIOCMBIC = clear bit 31188c2ecf20Sopenharmony_ci * TIOCMSET = set/clear signal values 31198c2ecf20Sopenharmony_ci * value bit mask for command 31208c2ecf20Sopenharmony_ci */ 31218c2ecf20Sopenharmony_cistatic int tiocmset(struct tty_struct *tty, 31228c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear) 31238c2ecf20Sopenharmony_ci{ 31248c2ecf20Sopenharmony_ci struct slgt_info *info = tty->driver_data; 31258c2ecf20Sopenharmony_ci unsigned long flags; 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear)); 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci if (set & TIOCM_RTS) 31308c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RTS; 31318c2ecf20Sopenharmony_ci if (set & TIOCM_DTR) 31328c2ecf20Sopenharmony_ci info->signals |= SerialSignal_DTR; 31338c2ecf20Sopenharmony_ci if (clear & TIOCM_RTS) 31348c2ecf20Sopenharmony_ci info->signals &= ~SerialSignal_RTS; 31358c2ecf20Sopenharmony_ci if (clear & TIOCM_DTR) 31368c2ecf20Sopenharmony_ci info->signals &= ~SerialSignal_DTR; 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 31398c2ecf20Sopenharmony_ci set_gtsignals(info); 31408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 31418c2ecf20Sopenharmony_ci return 0; 31428c2ecf20Sopenharmony_ci} 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_cistatic int carrier_raised(struct tty_port *port) 31458c2ecf20Sopenharmony_ci{ 31468c2ecf20Sopenharmony_ci unsigned long flags; 31478c2ecf20Sopenharmony_ci struct slgt_info *info = container_of(port, struct slgt_info, port); 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 31508c2ecf20Sopenharmony_ci get_gtsignals(info); 31518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 31528c2ecf20Sopenharmony_ci return (info->signals & SerialSignal_DCD) ? 1 : 0; 31538c2ecf20Sopenharmony_ci} 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_cistatic void dtr_rts(struct tty_port *port, int on) 31568c2ecf20Sopenharmony_ci{ 31578c2ecf20Sopenharmony_ci unsigned long flags; 31588c2ecf20Sopenharmony_ci struct slgt_info *info = container_of(port, struct slgt_info, port); 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 31618c2ecf20Sopenharmony_ci if (on) 31628c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RTS | SerialSignal_DTR; 31638c2ecf20Sopenharmony_ci else 31648c2ecf20Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 31658c2ecf20Sopenharmony_ci set_gtsignals(info); 31668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 31678c2ecf20Sopenharmony_ci} 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_ci/* 31718c2ecf20Sopenharmony_ci * block current process until the device is ready to open 31728c2ecf20Sopenharmony_ci */ 31738c2ecf20Sopenharmony_cistatic int block_til_ready(struct tty_struct *tty, struct file *filp, 31748c2ecf20Sopenharmony_ci struct slgt_info *info) 31758c2ecf20Sopenharmony_ci{ 31768c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 31778c2ecf20Sopenharmony_ci int retval; 31788c2ecf20Sopenharmony_ci bool do_clocal = false; 31798c2ecf20Sopenharmony_ci unsigned long flags; 31808c2ecf20Sopenharmony_ci int cd; 31818c2ecf20Sopenharmony_ci struct tty_port *port = &info->port; 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci DBGINFO(("%s block_til_ready\n", tty->driver->name)); 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) { 31868c2ecf20Sopenharmony_ci /* nonblock mode is set or port is not enabled */ 31878c2ecf20Sopenharmony_ci tty_port_set_active(port, 1); 31888c2ecf20Sopenharmony_ci return 0; 31898c2ecf20Sopenharmony_ci } 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_ci if (C_CLOCAL(tty)) 31928c2ecf20Sopenharmony_ci do_clocal = true; 31938c2ecf20Sopenharmony_ci 31948c2ecf20Sopenharmony_ci /* Wait for carrier detect and the line to become 31958c2ecf20Sopenharmony_ci * free (i.e., not in use by the callout). While we are in 31968c2ecf20Sopenharmony_ci * this loop, port->count is dropped by one, so that 31978c2ecf20Sopenharmony_ci * close() knows when to free things. We restore it upon 31988c2ecf20Sopenharmony_ci * exit, either normal or abnormal. 31998c2ecf20Sopenharmony_ci */ 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_ci retval = 0; 32028c2ecf20Sopenharmony_ci add_wait_queue(&port->open_wait, &wait); 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 32058c2ecf20Sopenharmony_ci port->count--; 32068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 32078c2ecf20Sopenharmony_ci port->blocked_open++; 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci while (1) { 32108c2ecf20Sopenharmony_ci if (C_BAUD(tty) && tty_port_initialized(port)) 32118c2ecf20Sopenharmony_ci tty_port_raise_dtr_rts(port); 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { 32168c2ecf20Sopenharmony_ci retval = (port->flags & ASYNC_HUP_NOTIFY) ? 32178c2ecf20Sopenharmony_ci -EAGAIN : -ERESTARTSYS; 32188c2ecf20Sopenharmony_ci break; 32198c2ecf20Sopenharmony_ci } 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci cd = tty_port_carrier_raised(port); 32228c2ecf20Sopenharmony_ci if (do_clocal || cd) 32238c2ecf20Sopenharmony_ci break; 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ci if (signal_pending(current)) { 32268c2ecf20Sopenharmony_ci retval = -ERESTARTSYS; 32278c2ecf20Sopenharmony_ci break; 32288c2ecf20Sopenharmony_ci } 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); 32318c2ecf20Sopenharmony_ci tty_unlock(tty); 32328c2ecf20Sopenharmony_ci schedule(); 32338c2ecf20Sopenharmony_ci tty_lock(tty); 32348c2ecf20Sopenharmony_ci } 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 32378c2ecf20Sopenharmony_ci remove_wait_queue(&port->open_wait, &wait); 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_ci if (!tty_hung_up_p(filp)) 32408c2ecf20Sopenharmony_ci port->count++; 32418c2ecf20Sopenharmony_ci port->blocked_open--; 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci if (!retval) 32448c2ecf20Sopenharmony_ci tty_port_set_active(port, 1); 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_ci DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); 32478c2ecf20Sopenharmony_ci return retval; 32488c2ecf20Sopenharmony_ci} 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci/* 32518c2ecf20Sopenharmony_ci * allocate buffers used for calling line discipline receive_buf 32528c2ecf20Sopenharmony_ci * directly in synchronous mode 32538c2ecf20Sopenharmony_ci * note: add 5 bytes to max frame size to allow appending 32548c2ecf20Sopenharmony_ci * 32-bit CRC and status byte when configured to do so 32558c2ecf20Sopenharmony_ci */ 32568c2ecf20Sopenharmony_cistatic int alloc_tmp_rbuf(struct slgt_info *info) 32578c2ecf20Sopenharmony_ci{ 32588c2ecf20Sopenharmony_ci info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL); 32598c2ecf20Sopenharmony_ci if (info->tmp_rbuf == NULL) 32608c2ecf20Sopenharmony_ci return -ENOMEM; 32618c2ecf20Sopenharmony_ci /* unused flag buffer to satisfy receive_buf calling interface */ 32628c2ecf20Sopenharmony_ci info->flag_buf = kzalloc(info->max_frame_size + 5, GFP_KERNEL); 32638c2ecf20Sopenharmony_ci if (!info->flag_buf) { 32648c2ecf20Sopenharmony_ci kfree(info->tmp_rbuf); 32658c2ecf20Sopenharmony_ci info->tmp_rbuf = NULL; 32668c2ecf20Sopenharmony_ci return -ENOMEM; 32678c2ecf20Sopenharmony_ci } 32688c2ecf20Sopenharmony_ci return 0; 32698c2ecf20Sopenharmony_ci} 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_cistatic void free_tmp_rbuf(struct slgt_info *info) 32728c2ecf20Sopenharmony_ci{ 32738c2ecf20Sopenharmony_ci kfree(info->tmp_rbuf); 32748c2ecf20Sopenharmony_ci info->tmp_rbuf = NULL; 32758c2ecf20Sopenharmony_ci kfree(info->flag_buf); 32768c2ecf20Sopenharmony_ci info->flag_buf = NULL; 32778c2ecf20Sopenharmony_ci} 32788c2ecf20Sopenharmony_ci 32798c2ecf20Sopenharmony_ci/* 32808c2ecf20Sopenharmony_ci * allocate DMA descriptor lists. 32818c2ecf20Sopenharmony_ci */ 32828c2ecf20Sopenharmony_cistatic int alloc_desc(struct slgt_info *info) 32838c2ecf20Sopenharmony_ci{ 32848c2ecf20Sopenharmony_ci unsigned int i; 32858c2ecf20Sopenharmony_ci unsigned int pbufs; 32868c2ecf20Sopenharmony_ci 32878c2ecf20Sopenharmony_ci /* allocate memory to hold descriptor lists */ 32888c2ecf20Sopenharmony_ci info->bufs = dma_alloc_coherent(&info->pdev->dev, DESC_LIST_SIZE, 32898c2ecf20Sopenharmony_ci &info->bufs_dma_addr, GFP_KERNEL); 32908c2ecf20Sopenharmony_ci if (info->bufs == NULL) 32918c2ecf20Sopenharmony_ci return -ENOMEM; 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_ci info->rbufs = (struct slgt_desc*)info->bufs; 32948c2ecf20Sopenharmony_ci info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count; 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci pbufs = (unsigned int)info->bufs_dma_addr; 32978c2ecf20Sopenharmony_ci 32988c2ecf20Sopenharmony_ci /* 32998c2ecf20Sopenharmony_ci * Build circular lists of descriptors 33008c2ecf20Sopenharmony_ci */ 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_ci for (i=0; i < info->rbuf_count; i++) { 33038c2ecf20Sopenharmony_ci /* physical address of this descriptor */ 33048c2ecf20Sopenharmony_ci info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc)); 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci /* physical address of next descriptor */ 33078c2ecf20Sopenharmony_ci if (i == info->rbuf_count - 1) 33088c2ecf20Sopenharmony_ci info->rbufs[i].next = cpu_to_le32(pbufs); 33098c2ecf20Sopenharmony_ci else 33108c2ecf20Sopenharmony_ci info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc))); 33118c2ecf20Sopenharmony_ci set_desc_count(info->rbufs[i], DMABUFSIZE); 33128c2ecf20Sopenharmony_ci } 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci for (i=0; i < info->tbuf_count; i++) { 33158c2ecf20Sopenharmony_ci /* physical address of this descriptor */ 33168c2ecf20Sopenharmony_ci info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc)); 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci /* physical address of next descriptor */ 33198c2ecf20Sopenharmony_ci if (i == info->tbuf_count - 1) 33208c2ecf20Sopenharmony_ci info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc)); 33218c2ecf20Sopenharmony_ci else 33228c2ecf20Sopenharmony_ci info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc))); 33238c2ecf20Sopenharmony_ci } 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci return 0; 33268c2ecf20Sopenharmony_ci} 33278c2ecf20Sopenharmony_ci 33288c2ecf20Sopenharmony_cistatic void free_desc(struct slgt_info *info) 33298c2ecf20Sopenharmony_ci{ 33308c2ecf20Sopenharmony_ci if (info->bufs != NULL) { 33318c2ecf20Sopenharmony_ci dma_free_coherent(&info->pdev->dev, DESC_LIST_SIZE, 33328c2ecf20Sopenharmony_ci info->bufs, info->bufs_dma_addr); 33338c2ecf20Sopenharmony_ci info->bufs = NULL; 33348c2ecf20Sopenharmony_ci info->rbufs = NULL; 33358c2ecf20Sopenharmony_ci info->tbufs = NULL; 33368c2ecf20Sopenharmony_ci } 33378c2ecf20Sopenharmony_ci} 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_cistatic int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) 33408c2ecf20Sopenharmony_ci{ 33418c2ecf20Sopenharmony_ci int i; 33428c2ecf20Sopenharmony_ci for (i=0; i < count; i++) { 33438c2ecf20Sopenharmony_ci bufs[i].buf = dma_alloc_coherent(&info->pdev->dev, DMABUFSIZE, 33448c2ecf20Sopenharmony_ci &bufs[i].buf_dma_addr, GFP_KERNEL); 33458c2ecf20Sopenharmony_ci if (!bufs[i].buf) 33468c2ecf20Sopenharmony_ci return -ENOMEM; 33478c2ecf20Sopenharmony_ci bufs[i].pbuf = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr); 33488c2ecf20Sopenharmony_ci } 33498c2ecf20Sopenharmony_ci return 0; 33508c2ecf20Sopenharmony_ci} 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_cistatic void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) 33538c2ecf20Sopenharmony_ci{ 33548c2ecf20Sopenharmony_ci int i; 33558c2ecf20Sopenharmony_ci for (i=0; i < count; i++) { 33568c2ecf20Sopenharmony_ci if (bufs[i].buf == NULL) 33578c2ecf20Sopenharmony_ci continue; 33588c2ecf20Sopenharmony_ci dma_free_coherent(&info->pdev->dev, DMABUFSIZE, bufs[i].buf, 33598c2ecf20Sopenharmony_ci bufs[i].buf_dma_addr); 33608c2ecf20Sopenharmony_ci bufs[i].buf = NULL; 33618c2ecf20Sopenharmony_ci } 33628c2ecf20Sopenharmony_ci} 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_cistatic int alloc_dma_bufs(struct slgt_info *info) 33658c2ecf20Sopenharmony_ci{ 33668c2ecf20Sopenharmony_ci info->rbuf_count = 32; 33678c2ecf20Sopenharmony_ci info->tbuf_count = 32; 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci if (alloc_desc(info) < 0 || 33708c2ecf20Sopenharmony_ci alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 || 33718c2ecf20Sopenharmony_ci alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 || 33728c2ecf20Sopenharmony_ci alloc_tmp_rbuf(info) < 0) { 33738c2ecf20Sopenharmony_ci DBGERR(("%s DMA buffer alloc fail\n", info->device_name)); 33748c2ecf20Sopenharmony_ci return -ENOMEM; 33758c2ecf20Sopenharmony_ci } 33768c2ecf20Sopenharmony_ci reset_rbufs(info); 33778c2ecf20Sopenharmony_ci return 0; 33788c2ecf20Sopenharmony_ci} 33798c2ecf20Sopenharmony_ci 33808c2ecf20Sopenharmony_cistatic void free_dma_bufs(struct slgt_info *info) 33818c2ecf20Sopenharmony_ci{ 33828c2ecf20Sopenharmony_ci if (info->bufs) { 33838c2ecf20Sopenharmony_ci free_bufs(info, info->rbufs, info->rbuf_count); 33848c2ecf20Sopenharmony_ci free_bufs(info, info->tbufs, info->tbuf_count); 33858c2ecf20Sopenharmony_ci free_desc(info); 33868c2ecf20Sopenharmony_ci } 33878c2ecf20Sopenharmony_ci free_tmp_rbuf(info); 33888c2ecf20Sopenharmony_ci} 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_cistatic int claim_resources(struct slgt_info *info) 33918c2ecf20Sopenharmony_ci{ 33928c2ecf20Sopenharmony_ci if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) { 33938c2ecf20Sopenharmony_ci DBGERR(("%s reg addr conflict, addr=%08X\n", 33948c2ecf20Sopenharmony_ci info->device_name, info->phys_reg_addr)); 33958c2ecf20Sopenharmony_ci info->init_error = DiagStatus_AddressConflict; 33968c2ecf20Sopenharmony_ci goto errout; 33978c2ecf20Sopenharmony_ci } 33988c2ecf20Sopenharmony_ci else 33998c2ecf20Sopenharmony_ci info->reg_addr_requested = true; 34008c2ecf20Sopenharmony_ci 34018c2ecf20Sopenharmony_ci info->reg_addr = ioremap(info->phys_reg_addr, SLGT_REG_SIZE); 34028c2ecf20Sopenharmony_ci if (!info->reg_addr) { 34038c2ecf20Sopenharmony_ci DBGERR(("%s can't map device registers, addr=%08X\n", 34048c2ecf20Sopenharmony_ci info->device_name, info->phys_reg_addr)); 34058c2ecf20Sopenharmony_ci info->init_error = DiagStatus_CantAssignPciResources; 34068c2ecf20Sopenharmony_ci goto errout; 34078c2ecf20Sopenharmony_ci } 34088c2ecf20Sopenharmony_ci return 0; 34098c2ecf20Sopenharmony_ci 34108c2ecf20Sopenharmony_cierrout: 34118c2ecf20Sopenharmony_ci release_resources(info); 34128c2ecf20Sopenharmony_ci return -ENODEV; 34138c2ecf20Sopenharmony_ci} 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_cistatic void release_resources(struct slgt_info *info) 34168c2ecf20Sopenharmony_ci{ 34178c2ecf20Sopenharmony_ci if (info->irq_requested) { 34188c2ecf20Sopenharmony_ci free_irq(info->irq_level, info); 34198c2ecf20Sopenharmony_ci info->irq_requested = false; 34208c2ecf20Sopenharmony_ci } 34218c2ecf20Sopenharmony_ci 34228c2ecf20Sopenharmony_ci if (info->reg_addr_requested) { 34238c2ecf20Sopenharmony_ci release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE); 34248c2ecf20Sopenharmony_ci info->reg_addr_requested = false; 34258c2ecf20Sopenharmony_ci } 34268c2ecf20Sopenharmony_ci 34278c2ecf20Sopenharmony_ci if (info->reg_addr) { 34288c2ecf20Sopenharmony_ci iounmap(info->reg_addr); 34298c2ecf20Sopenharmony_ci info->reg_addr = NULL; 34308c2ecf20Sopenharmony_ci } 34318c2ecf20Sopenharmony_ci} 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci/* Add the specified device instance data structure to the 34348c2ecf20Sopenharmony_ci * global linked list of devices and increment the device count. 34358c2ecf20Sopenharmony_ci */ 34368c2ecf20Sopenharmony_cistatic void add_device(struct slgt_info *info) 34378c2ecf20Sopenharmony_ci{ 34388c2ecf20Sopenharmony_ci char *devstr; 34398c2ecf20Sopenharmony_ci 34408c2ecf20Sopenharmony_ci info->next_device = NULL; 34418c2ecf20Sopenharmony_ci info->line = slgt_device_count; 34428c2ecf20Sopenharmony_ci sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line); 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_ci if (info->line < MAX_DEVICES) { 34458c2ecf20Sopenharmony_ci if (maxframe[info->line]) 34468c2ecf20Sopenharmony_ci info->max_frame_size = maxframe[info->line]; 34478c2ecf20Sopenharmony_ci } 34488c2ecf20Sopenharmony_ci 34498c2ecf20Sopenharmony_ci slgt_device_count++; 34508c2ecf20Sopenharmony_ci 34518c2ecf20Sopenharmony_ci if (!slgt_device_list) 34528c2ecf20Sopenharmony_ci slgt_device_list = info; 34538c2ecf20Sopenharmony_ci else { 34548c2ecf20Sopenharmony_ci struct slgt_info *current_dev = slgt_device_list; 34558c2ecf20Sopenharmony_ci while(current_dev->next_device) 34568c2ecf20Sopenharmony_ci current_dev = current_dev->next_device; 34578c2ecf20Sopenharmony_ci current_dev->next_device = info; 34588c2ecf20Sopenharmony_ci } 34598c2ecf20Sopenharmony_ci 34608c2ecf20Sopenharmony_ci if (info->max_frame_size < 4096) 34618c2ecf20Sopenharmony_ci info->max_frame_size = 4096; 34628c2ecf20Sopenharmony_ci else if (info->max_frame_size > 65535) 34638c2ecf20Sopenharmony_ci info->max_frame_size = 65535; 34648c2ecf20Sopenharmony_ci 34658c2ecf20Sopenharmony_ci switch(info->pdev->device) { 34668c2ecf20Sopenharmony_ci case SYNCLINK_GT_DEVICE_ID: 34678c2ecf20Sopenharmony_ci devstr = "GT"; 34688c2ecf20Sopenharmony_ci break; 34698c2ecf20Sopenharmony_ci case SYNCLINK_GT2_DEVICE_ID: 34708c2ecf20Sopenharmony_ci devstr = "GT2"; 34718c2ecf20Sopenharmony_ci break; 34728c2ecf20Sopenharmony_ci case SYNCLINK_GT4_DEVICE_ID: 34738c2ecf20Sopenharmony_ci devstr = "GT4"; 34748c2ecf20Sopenharmony_ci break; 34758c2ecf20Sopenharmony_ci case SYNCLINK_AC_DEVICE_ID: 34768c2ecf20Sopenharmony_ci devstr = "AC"; 34778c2ecf20Sopenharmony_ci info->params.mode = MGSL_MODE_ASYNC; 34788c2ecf20Sopenharmony_ci break; 34798c2ecf20Sopenharmony_ci default: 34808c2ecf20Sopenharmony_ci devstr = "(unknown model)"; 34818c2ecf20Sopenharmony_ci } 34828c2ecf20Sopenharmony_ci printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n", 34838c2ecf20Sopenharmony_ci devstr, info->device_name, info->phys_reg_addr, 34848c2ecf20Sopenharmony_ci info->irq_level, info->max_frame_size); 34858c2ecf20Sopenharmony_ci 34868c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 34878c2ecf20Sopenharmony_ci hdlcdev_init(info); 34888c2ecf20Sopenharmony_ci#endif 34898c2ecf20Sopenharmony_ci} 34908c2ecf20Sopenharmony_ci 34918c2ecf20Sopenharmony_cistatic const struct tty_port_operations slgt_port_ops = { 34928c2ecf20Sopenharmony_ci .carrier_raised = carrier_raised, 34938c2ecf20Sopenharmony_ci .dtr_rts = dtr_rts, 34948c2ecf20Sopenharmony_ci}; 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci/* 34978c2ecf20Sopenharmony_ci * allocate device instance structure, return NULL on failure 34988c2ecf20Sopenharmony_ci */ 34998c2ecf20Sopenharmony_cistatic struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) 35008c2ecf20Sopenharmony_ci{ 35018c2ecf20Sopenharmony_ci struct slgt_info *info; 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL); 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_ci if (!info) { 35068c2ecf20Sopenharmony_ci DBGERR(("%s device alloc failed adapter=%d port=%d\n", 35078c2ecf20Sopenharmony_ci driver_name, adapter_num, port_num)); 35088c2ecf20Sopenharmony_ci } else { 35098c2ecf20Sopenharmony_ci tty_port_init(&info->port); 35108c2ecf20Sopenharmony_ci info->port.ops = &slgt_port_ops; 35118c2ecf20Sopenharmony_ci info->magic = MGSL_MAGIC; 35128c2ecf20Sopenharmony_ci INIT_WORK(&info->task, bh_handler); 35138c2ecf20Sopenharmony_ci info->max_frame_size = 4096; 35148c2ecf20Sopenharmony_ci info->base_clock = 14745600; 35158c2ecf20Sopenharmony_ci info->rbuf_fill_level = DMABUFSIZE; 35168c2ecf20Sopenharmony_ci info->port.close_delay = 5*HZ/10; 35178c2ecf20Sopenharmony_ci info->port.closing_wait = 30*HZ; 35188c2ecf20Sopenharmony_ci init_waitqueue_head(&info->status_event_wait_q); 35198c2ecf20Sopenharmony_ci init_waitqueue_head(&info->event_wait_q); 35208c2ecf20Sopenharmony_ci spin_lock_init(&info->netlock); 35218c2ecf20Sopenharmony_ci memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); 35228c2ecf20Sopenharmony_ci info->idle_mode = HDLC_TXIDLE_FLAGS; 35238c2ecf20Sopenharmony_ci info->adapter_num = adapter_num; 35248c2ecf20Sopenharmony_ci info->port_num = port_num; 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci timer_setup(&info->tx_timer, tx_timeout, 0); 35278c2ecf20Sopenharmony_ci timer_setup(&info->rx_timer, rx_timeout, 0); 35288c2ecf20Sopenharmony_ci 35298c2ecf20Sopenharmony_ci /* Copy configuration info to device instance data */ 35308c2ecf20Sopenharmony_ci info->pdev = pdev; 35318c2ecf20Sopenharmony_ci info->irq_level = pdev->irq; 35328c2ecf20Sopenharmony_ci info->phys_reg_addr = pci_resource_start(pdev,0); 35338c2ecf20Sopenharmony_ci 35348c2ecf20Sopenharmony_ci info->bus_type = MGSL_BUS_TYPE_PCI; 35358c2ecf20Sopenharmony_ci info->irq_flags = IRQF_SHARED; 35368c2ecf20Sopenharmony_ci 35378c2ecf20Sopenharmony_ci info->init_error = -1; /* assume error, set to 0 on successful init */ 35388c2ecf20Sopenharmony_ci } 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci return info; 35418c2ecf20Sopenharmony_ci} 35428c2ecf20Sopenharmony_ci 35438c2ecf20Sopenharmony_cistatic void device_init(int adapter_num, struct pci_dev *pdev) 35448c2ecf20Sopenharmony_ci{ 35458c2ecf20Sopenharmony_ci struct slgt_info *port_array[SLGT_MAX_PORTS]; 35468c2ecf20Sopenharmony_ci int i; 35478c2ecf20Sopenharmony_ci int port_count = 1; 35488c2ecf20Sopenharmony_ci 35498c2ecf20Sopenharmony_ci if (pdev->device == SYNCLINK_GT2_DEVICE_ID) 35508c2ecf20Sopenharmony_ci port_count = 2; 35518c2ecf20Sopenharmony_ci else if (pdev->device == SYNCLINK_GT4_DEVICE_ID) 35528c2ecf20Sopenharmony_ci port_count = 4; 35538c2ecf20Sopenharmony_ci 35548c2ecf20Sopenharmony_ci /* allocate device instances for all ports */ 35558c2ecf20Sopenharmony_ci for (i=0; i < port_count; ++i) { 35568c2ecf20Sopenharmony_ci port_array[i] = alloc_dev(adapter_num, i, pdev); 35578c2ecf20Sopenharmony_ci if (port_array[i] == NULL) { 35588c2ecf20Sopenharmony_ci for (--i; i >= 0; --i) { 35598c2ecf20Sopenharmony_ci tty_port_destroy(&port_array[i]->port); 35608c2ecf20Sopenharmony_ci kfree(port_array[i]); 35618c2ecf20Sopenharmony_ci } 35628c2ecf20Sopenharmony_ci return; 35638c2ecf20Sopenharmony_ci } 35648c2ecf20Sopenharmony_ci } 35658c2ecf20Sopenharmony_ci 35668c2ecf20Sopenharmony_ci /* give copy of port_array to all ports and add to device list */ 35678c2ecf20Sopenharmony_ci for (i=0; i < port_count; ++i) { 35688c2ecf20Sopenharmony_ci memcpy(port_array[i]->port_array, port_array, sizeof(port_array)); 35698c2ecf20Sopenharmony_ci add_device(port_array[i]); 35708c2ecf20Sopenharmony_ci port_array[i]->port_count = port_count; 35718c2ecf20Sopenharmony_ci spin_lock_init(&port_array[i]->lock); 35728c2ecf20Sopenharmony_ci } 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_ci /* Allocate and claim adapter resources */ 35758c2ecf20Sopenharmony_ci if (!claim_resources(port_array[0])) { 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_ci alloc_dma_bufs(port_array[0]); 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci /* copy resource information from first port to others */ 35808c2ecf20Sopenharmony_ci for (i = 1; i < port_count; ++i) { 35818c2ecf20Sopenharmony_ci port_array[i]->irq_level = port_array[0]->irq_level; 35828c2ecf20Sopenharmony_ci port_array[i]->reg_addr = port_array[0]->reg_addr; 35838c2ecf20Sopenharmony_ci alloc_dma_bufs(port_array[i]); 35848c2ecf20Sopenharmony_ci } 35858c2ecf20Sopenharmony_ci 35868c2ecf20Sopenharmony_ci if (request_irq(port_array[0]->irq_level, 35878c2ecf20Sopenharmony_ci slgt_interrupt, 35888c2ecf20Sopenharmony_ci port_array[0]->irq_flags, 35898c2ecf20Sopenharmony_ci port_array[0]->device_name, 35908c2ecf20Sopenharmony_ci port_array[0]) < 0) { 35918c2ecf20Sopenharmony_ci DBGERR(("%s request_irq failed IRQ=%d\n", 35928c2ecf20Sopenharmony_ci port_array[0]->device_name, 35938c2ecf20Sopenharmony_ci port_array[0]->irq_level)); 35948c2ecf20Sopenharmony_ci } else { 35958c2ecf20Sopenharmony_ci port_array[0]->irq_requested = true; 35968c2ecf20Sopenharmony_ci adapter_test(port_array[0]); 35978c2ecf20Sopenharmony_ci for (i=1 ; i < port_count ; i++) { 35988c2ecf20Sopenharmony_ci port_array[i]->init_error = port_array[0]->init_error; 35998c2ecf20Sopenharmony_ci port_array[i]->gpio_present = port_array[0]->gpio_present; 36008c2ecf20Sopenharmony_ci } 36018c2ecf20Sopenharmony_ci } 36028c2ecf20Sopenharmony_ci } 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci for (i = 0; i < port_count; ++i) { 36058c2ecf20Sopenharmony_ci struct slgt_info *info = port_array[i]; 36068c2ecf20Sopenharmony_ci tty_port_register_device(&info->port, serial_driver, info->line, 36078c2ecf20Sopenharmony_ci &info->pdev->dev); 36088c2ecf20Sopenharmony_ci } 36098c2ecf20Sopenharmony_ci} 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_cistatic int init_one(struct pci_dev *dev, 36128c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 36138c2ecf20Sopenharmony_ci{ 36148c2ecf20Sopenharmony_ci if (pci_enable_device(dev)) { 36158c2ecf20Sopenharmony_ci printk("error enabling pci device %p\n", dev); 36168c2ecf20Sopenharmony_ci return -EIO; 36178c2ecf20Sopenharmony_ci } 36188c2ecf20Sopenharmony_ci pci_set_master(dev); 36198c2ecf20Sopenharmony_ci device_init(slgt_device_count, dev); 36208c2ecf20Sopenharmony_ci return 0; 36218c2ecf20Sopenharmony_ci} 36228c2ecf20Sopenharmony_ci 36238c2ecf20Sopenharmony_cistatic void remove_one(struct pci_dev *dev) 36248c2ecf20Sopenharmony_ci{ 36258c2ecf20Sopenharmony_ci} 36268c2ecf20Sopenharmony_ci 36278c2ecf20Sopenharmony_cistatic const struct tty_operations ops = { 36288c2ecf20Sopenharmony_ci .open = open, 36298c2ecf20Sopenharmony_ci .close = close, 36308c2ecf20Sopenharmony_ci .write = write, 36318c2ecf20Sopenharmony_ci .put_char = put_char, 36328c2ecf20Sopenharmony_ci .flush_chars = flush_chars, 36338c2ecf20Sopenharmony_ci .write_room = write_room, 36348c2ecf20Sopenharmony_ci .chars_in_buffer = chars_in_buffer, 36358c2ecf20Sopenharmony_ci .flush_buffer = flush_buffer, 36368c2ecf20Sopenharmony_ci .ioctl = ioctl, 36378c2ecf20Sopenharmony_ci .compat_ioctl = slgt_compat_ioctl, 36388c2ecf20Sopenharmony_ci .throttle = throttle, 36398c2ecf20Sopenharmony_ci .unthrottle = unthrottle, 36408c2ecf20Sopenharmony_ci .send_xchar = send_xchar, 36418c2ecf20Sopenharmony_ci .break_ctl = set_break, 36428c2ecf20Sopenharmony_ci .wait_until_sent = wait_until_sent, 36438c2ecf20Sopenharmony_ci .set_termios = set_termios, 36448c2ecf20Sopenharmony_ci .stop = tx_hold, 36458c2ecf20Sopenharmony_ci .start = tx_release, 36468c2ecf20Sopenharmony_ci .hangup = hangup, 36478c2ecf20Sopenharmony_ci .tiocmget = tiocmget, 36488c2ecf20Sopenharmony_ci .tiocmset = tiocmset, 36498c2ecf20Sopenharmony_ci .get_icount = get_icount, 36508c2ecf20Sopenharmony_ci .proc_show = synclink_gt_proc_show, 36518c2ecf20Sopenharmony_ci}; 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_cistatic void slgt_cleanup(void) 36548c2ecf20Sopenharmony_ci{ 36558c2ecf20Sopenharmony_ci int rc; 36568c2ecf20Sopenharmony_ci struct slgt_info *info; 36578c2ecf20Sopenharmony_ci struct slgt_info *tmp; 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci printk(KERN_INFO "unload %s\n", driver_name); 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_ci if (serial_driver) { 36628c2ecf20Sopenharmony_ci for (info=slgt_device_list ; info != NULL ; info=info->next_device) 36638c2ecf20Sopenharmony_ci tty_unregister_device(serial_driver, info->line); 36648c2ecf20Sopenharmony_ci rc = tty_unregister_driver(serial_driver); 36658c2ecf20Sopenharmony_ci if (rc) 36668c2ecf20Sopenharmony_ci DBGERR(("tty_unregister_driver error=%d\n", rc)); 36678c2ecf20Sopenharmony_ci put_tty_driver(serial_driver); 36688c2ecf20Sopenharmony_ci } 36698c2ecf20Sopenharmony_ci 36708c2ecf20Sopenharmony_ci /* reset devices */ 36718c2ecf20Sopenharmony_ci info = slgt_device_list; 36728c2ecf20Sopenharmony_ci while(info) { 36738c2ecf20Sopenharmony_ci reset_port(info); 36748c2ecf20Sopenharmony_ci info = info->next_device; 36758c2ecf20Sopenharmony_ci } 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci /* release devices */ 36788c2ecf20Sopenharmony_ci info = slgt_device_list; 36798c2ecf20Sopenharmony_ci while(info) { 36808c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 36818c2ecf20Sopenharmony_ci hdlcdev_exit(info); 36828c2ecf20Sopenharmony_ci#endif 36838c2ecf20Sopenharmony_ci free_dma_bufs(info); 36848c2ecf20Sopenharmony_ci free_tmp_rbuf(info); 36858c2ecf20Sopenharmony_ci if (info->port_num == 0) 36868c2ecf20Sopenharmony_ci release_resources(info); 36878c2ecf20Sopenharmony_ci tmp = info; 36888c2ecf20Sopenharmony_ci info = info->next_device; 36898c2ecf20Sopenharmony_ci tty_port_destroy(&tmp->port); 36908c2ecf20Sopenharmony_ci kfree(tmp); 36918c2ecf20Sopenharmony_ci } 36928c2ecf20Sopenharmony_ci 36938c2ecf20Sopenharmony_ci if (pci_registered) 36948c2ecf20Sopenharmony_ci pci_unregister_driver(&pci_driver); 36958c2ecf20Sopenharmony_ci} 36968c2ecf20Sopenharmony_ci 36978c2ecf20Sopenharmony_ci/* 36988c2ecf20Sopenharmony_ci * Driver initialization entry point. 36998c2ecf20Sopenharmony_ci */ 37008c2ecf20Sopenharmony_cistatic int __init slgt_init(void) 37018c2ecf20Sopenharmony_ci{ 37028c2ecf20Sopenharmony_ci int rc; 37038c2ecf20Sopenharmony_ci 37048c2ecf20Sopenharmony_ci printk(KERN_INFO "%s\n", driver_name); 37058c2ecf20Sopenharmony_ci 37068c2ecf20Sopenharmony_ci serial_driver = alloc_tty_driver(MAX_DEVICES); 37078c2ecf20Sopenharmony_ci if (!serial_driver) { 37088c2ecf20Sopenharmony_ci printk("%s can't allocate tty driver\n", driver_name); 37098c2ecf20Sopenharmony_ci return -ENOMEM; 37108c2ecf20Sopenharmony_ci } 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ci /* Initialize the tty_driver structure */ 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ci serial_driver->driver_name = slgt_driver_name; 37158c2ecf20Sopenharmony_ci serial_driver->name = tty_dev_prefix; 37168c2ecf20Sopenharmony_ci serial_driver->major = ttymajor; 37178c2ecf20Sopenharmony_ci serial_driver->minor_start = 64; 37188c2ecf20Sopenharmony_ci serial_driver->type = TTY_DRIVER_TYPE_SERIAL; 37198c2ecf20Sopenharmony_ci serial_driver->subtype = SERIAL_TYPE_NORMAL; 37208c2ecf20Sopenharmony_ci serial_driver->init_termios = tty_std_termios; 37218c2ecf20Sopenharmony_ci serial_driver->init_termios.c_cflag = 37228c2ecf20Sopenharmony_ci B9600 | CS8 | CREAD | HUPCL | CLOCAL; 37238c2ecf20Sopenharmony_ci serial_driver->init_termios.c_ispeed = 9600; 37248c2ecf20Sopenharmony_ci serial_driver->init_termios.c_ospeed = 9600; 37258c2ecf20Sopenharmony_ci serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 37268c2ecf20Sopenharmony_ci tty_set_operations(serial_driver, &ops); 37278c2ecf20Sopenharmony_ci if ((rc = tty_register_driver(serial_driver)) < 0) { 37288c2ecf20Sopenharmony_ci DBGERR(("%s can't register serial driver\n", driver_name)); 37298c2ecf20Sopenharmony_ci put_tty_driver(serial_driver); 37308c2ecf20Sopenharmony_ci serial_driver = NULL; 37318c2ecf20Sopenharmony_ci goto error; 37328c2ecf20Sopenharmony_ci } 37338c2ecf20Sopenharmony_ci 37348c2ecf20Sopenharmony_ci printk(KERN_INFO "%s, tty major#%d\n", 37358c2ecf20Sopenharmony_ci driver_name, serial_driver->major); 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci slgt_device_count = 0; 37388c2ecf20Sopenharmony_ci if ((rc = pci_register_driver(&pci_driver)) < 0) { 37398c2ecf20Sopenharmony_ci printk("%s pci_register_driver error=%d\n", driver_name, rc); 37408c2ecf20Sopenharmony_ci goto error; 37418c2ecf20Sopenharmony_ci } 37428c2ecf20Sopenharmony_ci pci_registered = true; 37438c2ecf20Sopenharmony_ci 37448c2ecf20Sopenharmony_ci if (!slgt_device_list) 37458c2ecf20Sopenharmony_ci printk("%s no devices found\n",driver_name); 37468c2ecf20Sopenharmony_ci 37478c2ecf20Sopenharmony_ci return 0; 37488c2ecf20Sopenharmony_ci 37498c2ecf20Sopenharmony_cierror: 37508c2ecf20Sopenharmony_ci slgt_cleanup(); 37518c2ecf20Sopenharmony_ci return rc; 37528c2ecf20Sopenharmony_ci} 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_cistatic void __exit slgt_exit(void) 37558c2ecf20Sopenharmony_ci{ 37568c2ecf20Sopenharmony_ci slgt_cleanup(); 37578c2ecf20Sopenharmony_ci} 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_cimodule_init(slgt_init); 37608c2ecf20Sopenharmony_cimodule_exit(slgt_exit); 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci/* 37638c2ecf20Sopenharmony_ci * register access routines 37648c2ecf20Sopenharmony_ci */ 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci#define CALC_REGADDR() \ 37678c2ecf20Sopenharmony_ci unsigned long reg_addr = ((unsigned long)info->reg_addr) + addr; \ 37688c2ecf20Sopenharmony_ci if (addr >= 0x80) \ 37698c2ecf20Sopenharmony_ci reg_addr += (info->port_num) * 32; \ 37708c2ecf20Sopenharmony_ci else if (addr >= 0x40) \ 37718c2ecf20Sopenharmony_ci reg_addr += (info->port_num) * 16; 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_cistatic __u8 rd_reg8(struct slgt_info *info, unsigned int addr) 37748c2ecf20Sopenharmony_ci{ 37758c2ecf20Sopenharmony_ci CALC_REGADDR(); 37768c2ecf20Sopenharmony_ci return readb((void __iomem *)reg_addr); 37778c2ecf20Sopenharmony_ci} 37788c2ecf20Sopenharmony_ci 37798c2ecf20Sopenharmony_cistatic void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value) 37808c2ecf20Sopenharmony_ci{ 37818c2ecf20Sopenharmony_ci CALC_REGADDR(); 37828c2ecf20Sopenharmony_ci writeb(value, (void __iomem *)reg_addr); 37838c2ecf20Sopenharmony_ci} 37848c2ecf20Sopenharmony_ci 37858c2ecf20Sopenharmony_cistatic __u16 rd_reg16(struct slgt_info *info, unsigned int addr) 37868c2ecf20Sopenharmony_ci{ 37878c2ecf20Sopenharmony_ci CALC_REGADDR(); 37888c2ecf20Sopenharmony_ci return readw((void __iomem *)reg_addr); 37898c2ecf20Sopenharmony_ci} 37908c2ecf20Sopenharmony_ci 37918c2ecf20Sopenharmony_cistatic void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value) 37928c2ecf20Sopenharmony_ci{ 37938c2ecf20Sopenharmony_ci CALC_REGADDR(); 37948c2ecf20Sopenharmony_ci writew(value, (void __iomem *)reg_addr); 37958c2ecf20Sopenharmony_ci} 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_cistatic __u32 rd_reg32(struct slgt_info *info, unsigned int addr) 37988c2ecf20Sopenharmony_ci{ 37998c2ecf20Sopenharmony_ci CALC_REGADDR(); 38008c2ecf20Sopenharmony_ci return readl((void __iomem *)reg_addr); 38018c2ecf20Sopenharmony_ci} 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_cistatic void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value) 38048c2ecf20Sopenharmony_ci{ 38058c2ecf20Sopenharmony_ci CALC_REGADDR(); 38068c2ecf20Sopenharmony_ci writel(value, (void __iomem *)reg_addr); 38078c2ecf20Sopenharmony_ci} 38088c2ecf20Sopenharmony_ci 38098c2ecf20Sopenharmony_cistatic void rdma_reset(struct slgt_info *info) 38108c2ecf20Sopenharmony_ci{ 38118c2ecf20Sopenharmony_ci unsigned int i; 38128c2ecf20Sopenharmony_ci 38138c2ecf20Sopenharmony_ci /* set reset bit */ 38148c2ecf20Sopenharmony_ci wr_reg32(info, RDCSR, BIT1); 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_ci /* wait for enable bit cleared */ 38178c2ecf20Sopenharmony_ci for(i=0 ; i < 1000 ; i++) 38188c2ecf20Sopenharmony_ci if (!(rd_reg32(info, RDCSR) & BIT0)) 38198c2ecf20Sopenharmony_ci break; 38208c2ecf20Sopenharmony_ci} 38218c2ecf20Sopenharmony_ci 38228c2ecf20Sopenharmony_cistatic void tdma_reset(struct slgt_info *info) 38238c2ecf20Sopenharmony_ci{ 38248c2ecf20Sopenharmony_ci unsigned int i; 38258c2ecf20Sopenharmony_ci 38268c2ecf20Sopenharmony_ci /* set reset bit */ 38278c2ecf20Sopenharmony_ci wr_reg32(info, TDCSR, BIT1); 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_ci /* wait for enable bit cleared */ 38308c2ecf20Sopenharmony_ci for(i=0 ; i < 1000 ; i++) 38318c2ecf20Sopenharmony_ci if (!(rd_reg32(info, TDCSR) & BIT0)) 38328c2ecf20Sopenharmony_ci break; 38338c2ecf20Sopenharmony_ci} 38348c2ecf20Sopenharmony_ci 38358c2ecf20Sopenharmony_ci/* 38368c2ecf20Sopenharmony_ci * enable internal loopback 38378c2ecf20Sopenharmony_ci * TxCLK and RxCLK are generated from BRG 38388c2ecf20Sopenharmony_ci * and TxD is looped back to RxD internally. 38398c2ecf20Sopenharmony_ci */ 38408c2ecf20Sopenharmony_cistatic void enable_loopback(struct slgt_info *info) 38418c2ecf20Sopenharmony_ci{ 38428c2ecf20Sopenharmony_ci /* SCR (serial control) BIT2=loopback enable */ 38438c2ecf20Sopenharmony_ci wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2)); 38448c2ecf20Sopenharmony_ci 38458c2ecf20Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 38468c2ecf20Sopenharmony_ci /* CCR (clock control) 38478c2ecf20Sopenharmony_ci * 07..05 tx clock source (010 = BRG) 38488c2ecf20Sopenharmony_ci * 04..02 rx clock source (010 = BRG) 38498c2ecf20Sopenharmony_ci * 01 auxclk enable (0 = disable) 38508c2ecf20Sopenharmony_ci * 00 BRG enable (1 = enable) 38518c2ecf20Sopenharmony_ci * 38528c2ecf20Sopenharmony_ci * 0100 1001 38538c2ecf20Sopenharmony_ci */ 38548c2ecf20Sopenharmony_ci wr_reg8(info, CCR, 0x49); 38558c2ecf20Sopenharmony_ci 38568c2ecf20Sopenharmony_ci /* set speed if available, otherwise use default */ 38578c2ecf20Sopenharmony_ci if (info->params.clock_speed) 38588c2ecf20Sopenharmony_ci set_rate(info, info->params.clock_speed); 38598c2ecf20Sopenharmony_ci else 38608c2ecf20Sopenharmony_ci set_rate(info, 3686400); 38618c2ecf20Sopenharmony_ci } 38628c2ecf20Sopenharmony_ci} 38638c2ecf20Sopenharmony_ci 38648c2ecf20Sopenharmony_ci/* 38658c2ecf20Sopenharmony_ci * set baud rate generator to specified rate 38668c2ecf20Sopenharmony_ci */ 38678c2ecf20Sopenharmony_cistatic void set_rate(struct slgt_info *info, u32 rate) 38688c2ecf20Sopenharmony_ci{ 38698c2ecf20Sopenharmony_ci unsigned int div; 38708c2ecf20Sopenharmony_ci unsigned int osc = info->base_clock; 38718c2ecf20Sopenharmony_ci 38728c2ecf20Sopenharmony_ci /* div = osc/rate - 1 38738c2ecf20Sopenharmony_ci * 38748c2ecf20Sopenharmony_ci * Round div up if osc/rate is not integer to 38758c2ecf20Sopenharmony_ci * force to next slowest rate. 38768c2ecf20Sopenharmony_ci */ 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ci if (rate) { 38798c2ecf20Sopenharmony_ci div = osc/rate; 38808c2ecf20Sopenharmony_ci if (!(osc % rate) && div) 38818c2ecf20Sopenharmony_ci div--; 38828c2ecf20Sopenharmony_ci wr_reg16(info, BDR, (unsigned short)div); 38838c2ecf20Sopenharmony_ci } 38848c2ecf20Sopenharmony_ci} 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_cistatic void rx_stop(struct slgt_info *info) 38878c2ecf20Sopenharmony_ci{ 38888c2ecf20Sopenharmony_ci unsigned short val; 38898c2ecf20Sopenharmony_ci 38908c2ecf20Sopenharmony_ci /* disable and reset receiver */ 38918c2ecf20Sopenharmony_ci val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ 38928c2ecf20Sopenharmony_ci wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ 38938c2ecf20Sopenharmony_ci wr_reg16(info, RCR, val); /* clear reset bit */ 38948c2ecf20Sopenharmony_ci 38958c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE); 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci /* clear pending rx interrupts */ 38988c2ecf20Sopenharmony_ci wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER); 38998c2ecf20Sopenharmony_ci 39008c2ecf20Sopenharmony_ci rdma_reset(info); 39018c2ecf20Sopenharmony_ci 39028c2ecf20Sopenharmony_ci info->rx_enabled = false; 39038c2ecf20Sopenharmony_ci info->rx_restart = false; 39048c2ecf20Sopenharmony_ci} 39058c2ecf20Sopenharmony_ci 39068c2ecf20Sopenharmony_cistatic void rx_start(struct slgt_info *info) 39078c2ecf20Sopenharmony_ci{ 39088c2ecf20Sopenharmony_ci unsigned short val; 39098c2ecf20Sopenharmony_ci 39108c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA); 39118c2ecf20Sopenharmony_ci 39128c2ecf20Sopenharmony_ci /* clear pending rx overrun IRQ */ 39138c2ecf20Sopenharmony_ci wr_reg16(info, SSR, IRQ_RXOVER); 39148c2ecf20Sopenharmony_ci 39158c2ecf20Sopenharmony_ci /* reset and disable receiver */ 39168c2ecf20Sopenharmony_ci val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ 39178c2ecf20Sopenharmony_ci wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ 39188c2ecf20Sopenharmony_ci wr_reg16(info, RCR, val); /* clear reset bit */ 39198c2ecf20Sopenharmony_ci 39208c2ecf20Sopenharmony_ci rdma_reset(info); 39218c2ecf20Sopenharmony_ci reset_rbufs(info); 39228c2ecf20Sopenharmony_ci 39238c2ecf20Sopenharmony_ci if (info->rx_pio) { 39248c2ecf20Sopenharmony_ci /* rx request when rx FIFO not empty */ 39258c2ecf20Sopenharmony_ci wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14)); 39268c2ecf20Sopenharmony_ci slgt_irq_on(info, IRQ_RXDATA); 39278c2ecf20Sopenharmony_ci if (info->params.mode == MGSL_MODE_ASYNC) { 39288c2ecf20Sopenharmony_ci /* enable saving of rx status */ 39298c2ecf20Sopenharmony_ci wr_reg32(info, RDCSR, BIT6); 39308c2ecf20Sopenharmony_ci } 39318c2ecf20Sopenharmony_ci } else { 39328c2ecf20Sopenharmony_ci /* rx request when rx FIFO half full */ 39338c2ecf20Sopenharmony_ci wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14)); 39348c2ecf20Sopenharmony_ci /* set 1st descriptor address */ 39358c2ecf20Sopenharmony_ci wr_reg32(info, RDDAR, info->rbufs[0].pdesc); 39368c2ecf20Sopenharmony_ci 39378c2ecf20Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 39388c2ecf20Sopenharmony_ci /* enable rx DMA and DMA interrupt */ 39398c2ecf20Sopenharmony_ci wr_reg32(info, RDCSR, (BIT2 + BIT0)); 39408c2ecf20Sopenharmony_ci } else { 39418c2ecf20Sopenharmony_ci /* enable saving of rx status, rx DMA and DMA interrupt */ 39428c2ecf20Sopenharmony_ci wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0)); 39438c2ecf20Sopenharmony_ci } 39448c2ecf20Sopenharmony_ci } 39458c2ecf20Sopenharmony_ci 39468c2ecf20Sopenharmony_ci slgt_irq_on(info, IRQ_RXOVER); 39478c2ecf20Sopenharmony_ci 39488c2ecf20Sopenharmony_ci /* enable receiver */ 39498c2ecf20Sopenharmony_ci wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1)); 39508c2ecf20Sopenharmony_ci 39518c2ecf20Sopenharmony_ci info->rx_restart = false; 39528c2ecf20Sopenharmony_ci info->rx_enabled = true; 39538c2ecf20Sopenharmony_ci} 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_cistatic void tx_start(struct slgt_info *info) 39568c2ecf20Sopenharmony_ci{ 39578c2ecf20Sopenharmony_ci if (!info->tx_enabled) { 39588c2ecf20Sopenharmony_ci wr_reg16(info, TCR, 39598c2ecf20Sopenharmony_ci (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2)); 39608c2ecf20Sopenharmony_ci info->tx_enabled = true; 39618c2ecf20Sopenharmony_ci } 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_ci if (desc_count(info->tbufs[info->tbuf_start])) { 39648c2ecf20Sopenharmony_ci info->drop_rts_on_tx_done = false; 39658c2ecf20Sopenharmony_ci 39668c2ecf20Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 39678c2ecf20Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_RTS) { 39688c2ecf20Sopenharmony_ci get_gtsignals(info); 39698c2ecf20Sopenharmony_ci if (!(info->signals & SerialSignal_RTS)) { 39708c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RTS; 39718c2ecf20Sopenharmony_ci set_gtsignals(info); 39728c2ecf20Sopenharmony_ci info->drop_rts_on_tx_done = true; 39738c2ecf20Sopenharmony_ci } 39748c2ecf20Sopenharmony_ci } 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_TXDATA); 39778c2ecf20Sopenharmony_ci slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE); 39788c2ecf20Sopenharmony_ci /* clear tx idle and underrun status bits */ 39798c2ecf20Sopenharmony_ci wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); 39808c2ecf20Sopenharmony_ci } else { 39818c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_TXDATA); 39828c2ecf20Sopenharmony_ci slgt_irq_on(info, IRQ_TXIDLE); 39838c2ecf20Sopenharmony_ci /* clear tx idle status bit */ 39848c2ecf20Sopenharmony_ci wr_reg16(info, SSR, IRQ_TXIDLE); 39858c2ecf20Sopenharmony_ci } 39868c2ecf20Sopenharmony_ci /* set 1st descriptor address and start DMA */ 39878c2ecf20Sopenharmony_ci wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); 39888c2ecf20Sopenharmony_ci wr_reg32(info, TDCSR, BIT2 + BIT0); 39898c2ecf20Sopenharmony_ci info->tx_active = true; 39908c2ecf20Sopenharmony_ci } 39918c2ecf20Sopenharmony_ci} 39928c2ecf20Sopenharmony_ci 39938c2ecf20Sopenharmony_cistatic void tx_stop(struct slgt_info *info) 39948c2ecf20Sopenharmony_ci{ 39958c2ecf20Sopenharmony_ci unsigned short val; 39968c2ecf20Sopenharmony_ci 39978c2ecf20Sopenharmony_ci del_timer(&info->tx_timer); 39988c2ecf20Sopenharmony_ci 39998c2ecf20Sopenharmony_ci tdma_reset(info); 40008c2ecf20Sopenharmony_ci 40018c2ecf20Sopenharmony_ci /* reset and disable transmitter */ 40028c2ecf20Sopenharmony_ci val = rd_reg16(info, TCR) & ~BIT1; /* clear enable bit */ 40038c2ecf20Sopenharmony_ci wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ 40048c2ecf20Sopenharmony_ci 40058c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_ci /* clear tx idle and underrun status bit */ 40088c2ecf20Sopenharmony_ci wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); 40098c2ecf20Sopenharmony_ci 40108c2ecf20Sopenharmony_ci reset_tbufs(info); 40118c2ecf20Sopenharmony_ci 40128c2ecf20Sopenharmony_ci info->tx_enabled = false; 40138c2ecf20Sopenharmony_ci info->tx_active = false; 40148c2ecf20Sopenharmony_ci} 40158c2ecf20Sopenharmony_ci 40168c2ecf20Sopenharmony_cistatic void reset_port(struct slgt_info *info) 40178c2ecf20Sopenharmony_ci{ 40188c2ecf20Sopenharmony_ci if (!info->reg_addr) 40198c2ecf20Sopenharmony_ci return; 40208c2ecf20Sopenharmony_ci 40218c2ecf20Sopenharmony_ci tx_stop(info); 40228c2ecf20Sopenharmony_ci rx_stop(info); 40238c2ecf20Sopenharmony_ci 40248c2ecf20Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 40258c2ecf20Sopenharmony_ci set_gtsignals(info); 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); 40288c2ecf20Sopenharmony_ci} 40298c2ecf20Sopenharmony_ci 40308c2ecf20Sopenharmony_cistatic void reset_adapter(struct slgt_info *info) 40318c2ecf20Sopenharmony_ci{ 40328c2ecf20Sopenharmony_ci int i; 40338c2ecf20Sopenharmony_ci for (i=0; i < info->port_count; ++i) { 40348c2ecf20Sopenharmony_ci if (info->port_array[i]) 40358c2ecf20Sopenharmony_ci reset_port(info->port_array[i]); 40368c2ecf20Sopenharmony_ci } 40378c2ecf20Sopenharmony_ci} 40388c2ecf20Sopenharmony_ci 40398c2ecf20Sopenharmony_cistatic void async_mode(struct slgt_info *info) 40408c2ecf20Sopenharmony_ci{ 40418c2ecf20Sopenharmony_ci unsigned short val; 40428c2ecf20Sopenharmony_ci 40438c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); 40448c2ecf20Sopenharmony_ci tx_stop(info); 40458c2ecf20Sopenharmony_ci rx_stop(info); 40468c2ecf20Sopenharmony_ci 40478c2ecf20Sopenharmony_ci /* TCR (tx control) 40488c2ecf20Sopenharmony_ci * 40498c2ecf20Sopenharmony_ci * 15..13 mode, 010=async 40508c2ecf20Sopenharmony_ci * 12..10 encoding, 000=NRZ 40518c2ecf20Sopenharmony_ci * 09 parity enable 40528c2ecf20Sopenharmony_ci * 08 1=odd parity, 0=even parity 40538c2ecf20Sopenharmony_ci * 07 1=RTS driver control 40548c2ecf20Sopenharmony_ci * 06 1=break enable 40558c2ecf20Sopenharmony_ci * 05..04 character length 40568c2ecf20Sopenharmony_ci * 00=5 bits 40578c2ecf20Sopenharmony_ci * 01=6 bits 40588c2ecf20Sopenharmony_ci * 10=7 bits 40598c2ecf20Sopenharmony_ci * 11=8 bits 40608c2ecf20Sopenharmony_ci * 03 0=1 stop bit, 1=2 stop bits 40618c2ecf20Sopenharmony_ci * 02 reset 40628c2ecf20Sopenharmony_ci * 01 enable 40638c2ecf20Sopenharmony_ci * 00 auto-CTS enable 40648c2ecf20Sopenharmony_ci */ 40658c2ecf20Sopenharmony_ci val = 0x4000; 40668c2ecf20Sopenharmony_ci 40678c2ecf20Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_RTS_EN) 40688c2ecf20Sopenharmony_ci val |= BIT7; 40698c2ecf20Sopenharmony_ci 40708c2ecf20Sopenharmony_ci if (info->params.parity != ASYNC_PARITY_NONE) { 40718c2ecf20Sopenharmony_ci val |= BIT9; 40728c2ecf20Sopenharmony_ci if (info->params.parity == ASYNC_PARITY_ODD) 40738c2ecf20Sopenharmony_ci val |= BIT8; 40748c2ecf20Sopenharmony_ci } 40758c2ecf20Sopenharmony_ci 40768c2ecf20Sopenharmony_ci switch (info->params.data_bits) 40778c2ecf20Sopenharmony_ci { 40788c2ecf20Sopenharmony_ci case 6: val |= BIT4; break; 40798c2ecf20Sopenharmony_ci case 7: val |= BIT5; break; 40808c2ecf20Sopenharmony_ci case 8: val |= BIT5 + BIT4; break; 40818c2ecf20Sopenharmony_ci } 40828c2ecf20Sopenharmony_ci 40838c2ecf20Sopenharmony_ci if (info->params.stop_bits != 1) 40848c2ecf20Sopenharmony_ci val |= BIT3; 40858c2ecf20Sopenharmony_ci 40868c2ecf20Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_CTS) 40878c2ecf20Sopenharmony_ci val |= BIT0; 40888c2ecf20Sopenharmony_ci 40898c2ecf20Sopenharmony_ci wr_reg16(info, TCR, val); 40908c2ecf20Sopenharmony_ci 40918c2ecf20Sopenharmony_ci /* RCR (rx control) 40928c2ecf20Sopenharmony_ci * 40938c2ecf20Sopenharmony_ci * 15..13 mode, 010=async 40948c2ecf20Sopenharmony_ci * 12..10 encoding, 000=NRZ 40958c2ecf20Sopenharmony_ci * 09 parity enable 40968c2ecf20Sopenharmony_ci * 08 1=odd parity, 0=even parity 40978c2ecf20Sopenharmony_ci * 07..06 reserved, must be 0 40988c2ecf20Sopenharmony_ci * 05..04 character length 40998c2ecf20Sopenharmony_ci * 00=5 bits 41008c2ecf20Sopenharmony_ci * 01=6 bits 41018c2ecf20Sopenharmony_ci * 10=7 bits 41028c2ecf20Sopenharmony_ci * 11=8 bits 41038c2ecf20Sopenharmony_ci * 03 reserved, must be zero 41048c2ecf20Sopenharmony_ci * 02 reset 41058c2ecf20Sopenharmony_ci * 01 enable 41068c2ecf20Sopenharmony_ci * 00 auto-DCD enable 41078c2ecf20Sopenharmony_ci */ 41088c2ecf20Sopenharmony_ci val = 0x4000; 41098c2ecf20Sopenharmony_ci 41108c2ecf20Sopenharmony_ci if (info->params.parity != ASYNC_PARITY_NONE) { 41118c2ecf20Sopenharmony_ci val |= BIT9; 41128c2ecf20Sopenharmony_ci if (info->params.parity == ASYNC_PARITY_ODD) 41138c2ecf20Sopenharmony_ci val |= BIT8; 41148c2ecf20Sopenharmony_ci } 41158c2ecf20Sopenharmony_ci 41168c2ecf20Sopenharmony_ci switch (info->params.data_bits) 41178c2ecf20Sopenharmony_ci { 41188c2ecf20Sopenharmony_ci case 6: val |= BIT4; break; 41198c2ecf20Sopenharmony_ci case 7: val |= BIT5; break; 41208c2ecf20Sopenharmony_ci case 8: val |= BIT5 + BIT4; break; 41218c2ecf20Sopenharmony_ci } 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_DCD) 41248c2ecf20Sopenharmony_ci val |= BIT0; 41258c2ecf20Sopenharmony_ci 41268c2ecf20Sopenharmony_ci wr_reg16(info, RCR, val); 41278c2ecf20Sopenharmony_ci 41288c2ecf20Sopenharmony_ci /* CCR (clock control) 41298c2ecf20Sopenharmony_ci * 41308c2ecf20Sopenharmony_ci * 07..05 011 = tx clock source is BRG/16 41318c2ecf20Sopenharmony_ci * 04..02 010 = rx clock source is BRG 41328c2ecf20Sopenharmony_ci * 01 0 = auxclk disabled 41338c2ecf20Sopenharmony_ci * 00 1 = BRG enabled 41348c2ecf20Sopenharmony_ci * 41358c2ecf20Sopenharmony_ci * 0110 1001 41368c2ecf20Sopenharmony_ci */ 41378c2ecf20Sopenharmony_ci wr_reg8(info, CCR, 0x69); 41388c2ecf20Sopenharmony_ci 41398c2ecf20Sopenharmony_ci msc_set_vcr(info); 41408c2ecf20Sopenharmony_ci 41418c2ecf20Sopenharmony_ci /* SCR (serial control) 41428c2ecf20Sopenharmony_ci * 41438c2ecf20Sopenharmony_ci * 15 1=tx req on FIFO half empty 41448c2ecf20Sopenharmony_ci * 14 1=rx req on FIFO half full 41458c2ecf20Sopenharmony_ci * 13 tx data IRQ enable 41468c2ecf20Sopenharmony_ci * 12 tx idle IRQ enable 41478c2ecf20Sopenharmony_ci * 11 rx break on IRQ enable 41488c2ecf20Sopenharmony_ci * 10 rx data IRQ enable 41498c2ecf20Sopenharmony_ci * 09 rx break off IRQ enable 41508c2ecf20Sopenharmony_ci * 08 overrun IRQ enable 41518c2ecf20Sopenharmony_ci * 07 DSR IRQ enable 41528c2ecf20Sopenharmony_ci * 06 CTS IRQ enable 41538c2ecf20Sopenharmony_ci * 05 DCD IRQ enable 41548c2ecf20Sopenharmony_ci * 04 RI IRQ enable 41558c2ecf20Sopenharmony_ci * 03 0=16x sampling, 1=8x sampling 41568c2ecf20Sopenharmony_ci * 02 1=txd->rxd internal loopback enable 41578c2ecf20Sopenharmony_ci * 01 reserved, must be zero 41588c2ecf20Sopenharmony_ci * 00 1=master IRQ enable 41598c2ecf20Sopenharmony_ci */ 41608c2ecf20Sopenharmony_ci val = BIT15 + BIT14 + BIT0; 41618c2ecf20Sopenharmony_ci /* JCR[8] : 1 = x8 async mode feature available */ 41628c2ecf20Sopenharmony_ci if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate && 41638c2ecf20Sopenharmony_ci ((info->base_clock < (info->params.data_rate * 16)) || 41648c2ecf20Sopenharmony_ci (info->base_clock % (info->params.data_rate * 16)))) { 41658c2ecf20Sopenharmony_ci /* use 8x sampling */ 41668c2ecf20Sopenharmony_ci val |= BIT3; 41678c2ecf20Sopenharmony_ci set_rate(info, info->params.data_rate * 8); 41688c2ecf20Sopenharmony_ci } else { 41698c2ecf20Sopenharmony_ci /* use 16x sampling */ 41708c2ecf20Sopenharmony_ci set_rate(info, info->params.data_rate * 16); 41718c2ecf20Sopenharmony_ci } 41728c2ecf20Sopenharmony_ci wr_reg16(info, SCR, val); 41738c2ecf20Sopenharmony_ci 41748c2ecf20Sopenharmony_ci slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER); 41758c2ecf20Sopenharmony_ci 41768c2ecf20Sopenharmony_ci if (info->params.loopback) 41778c2ecf20Sopenharmony_ci enable_loopback(info); 41788c2ecf20Sopenharmony_ci} 41798c2ecf20Sopenharmony_ci 41808c2ecf20Sopenharmony_cistatic void sync_mode(struct slgt_info *info) 41818c2ecf20Sopenharmony_ci{ 41828c2ecf20Sopenharmony_ci unsigned short val; 41838c2ecf20Sopenharmony_ci 41848c2ecf20Sopenharmony_ci slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); 41858c2ecf20Sopenharmony_ci tx_stop(info); 41868c2ecf20Sopenharmony_ci rx_stop(info); 41878c2ecf20Sopenharmony_ci 41888c2ecf20Sopenharmony_ci /* TCR (tx control) 41898c2ecf20Sopenharmony_ci * 41908c2ecf20Sopenharmony_ci * 15..13 mode 41918c2ecf20Sopenharmony_ci * 000=HDLC/SDLC 41928c2ecf20Sopenharmony_ci * 001=raw bit synchronous 41938c2ecf20Sopenharmony_ci * 010=asynchronous/isochronous 41948c2ecf20Sopenharmony_ci * 011=monosync byte synchronous 41958c2ecf20Sopenharmony_ci * 100=bisync byte synchronous 41968c2ecf20Sopenharmony_ci * 101=xsync byte synchronous 41978c2ecf20Sopenharmony_ci * 12..10 encoding 41988c2ecf20Sopenharmony_ci * 09 CRC enable 41998c2ecf20Sopenharmony_ci * 08 CRC32 42008c2ecf20Sopenharmony_ci * 07 1=RTS driver control 42018c2ecf20Sopenharmony_ci * 06 preamble enable 42028c2ecf20Sopenharmony_ci * 05..04 preamble length 42038c2ecf20Sopenharmony_ci * 03 share open/close flag 42048c2ecf20Sopenharmony_ci * 02 reset 42058c2ecf20Sopenharmony_ci * 01 enable 42068c2ecf20Sopenharmony_ci * 00 auto-CTS enable 42078c2ecf20Sopenharmony_ci */ 42088c2ecf20Sopenharmony_ci val = BIT2; 42098c2ecf20Sopenharmony_ci 42108c2ecf20Sopenharmony_ci switch(info->params.mode) { 42118c2ecf20Sopenharmony_ci case MGSL_MODE_XSYNC: 42128c2ecf20Sopenharmony_ci val |= BIT15 + BIT13; 42138c2ecf20Sopenharmony_ci break; 42148c2ecf20Sopenharmony_ci case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; 42158c2ecf20Sopenharmony_ci case MGSL_MODE_BISYNC: val |= BIT15; break; 42168c2ecf20Sopenharmony_ci case MGSL_MODE_RAW: val |= BIT13; break; 42178c2ecf20Sopenharmony_ci } 42188c2ecf20Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_RTS_EN) 42198c2ecf20Sopenharmony_ci val |= BIT7; 42208c2ecf20Sopenharmony_ci 42218c2ecf20Sopenharmony_ci switch(info->params.encoding) 42228c2ecf20Sopenharmony_ci { 42238c2ecf20Sopenharmony_ci case HDLC_ENCODING_NRZB: val |= BIT10; break; 42248c2ecf20Sopenharmony_ci case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; 42258c2ecf20Sopenharmony_ci case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; 42268c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; 42278c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; 42288c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; 42298c2ecf20Sopenharmony_ci case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; 42308c2ecf20Sopenharmony_ci } 42318c2ecf20Sopenharmony_ci 42328c2ecf20Sopenharmony_ci switch (info->params.crc_type & HDLC_CRC_MASK) 42338c2ecf20Sopenharmony_ci { 42348c2ecf20Sopenharmony_ci case HDLC_CRC_16_CCITT: val |= BIT9; break; 42358c2ecf20Sopenharmony_ci case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; 42368c2ecf20Sopenharmony_ci } 42378c2ecf20Sopenharmony_ci 42388c2ecf20Sopenharmony_ci if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE) 42398c2ecf20Sopenharmony_ci val |= BIT6; 42408c2ecf20Sopenharmony_ci 42418c2ecf20Sopenharmony_ci switch (info->params.preamble_length) 42428c2ecf20Sopenharmony_ci { 42438c2ecf20Sopenharmony_ci case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break; 42448c2ecf20Sopenharmony_ci case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break; 42458c2ecf20Sopenharmony_ci case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break; 42468c2ecf20Sopenharmony_ci } 42478c2ecf20Sopenharmony_ci 42488c2ecf20Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_CTS) 42498c2ecf20Sopenharmony_ci val |= BIT0; 42508c2ecf20Sopenharmony_ci 42518c2ecf20Sopenharmony_ci wr_reg16(info, TCR, val); 42528c2ecf20Sopenharmony_ci 42538c2ecf20Sopenharmony_ci /* TPR (transmit preamble) */ 42548c2ecf20Sopenharmony_ci 42558c2ecf20Sopenharmony_ci switch (info->params.preamble) 42568c2ecf20Sopenharmony_ci { 42578c2ecf20Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break; 42588c2ecf20Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_ONES: val = 0xff; break; 42598c2ecf20Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break; 42608c2ecf20Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_10: val = 0x55; break; 42618c2ecf20Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_01: val = 0xaa; break; 42628c2ecf20Sopenharmony_ci default: val = 0x7e; break; 42638c2ecf20Sopenharmony_ci } 42648c2ecf20Sopenharmony_ci wr_reg8(info, TPR, (unsigned char)val); 42658c2ecf20Sopenharmony_ci 42668c2ecf20Sopenharmony_ci /* RCR (rx control) 42678c2ecf20Sopenharmony_ci * 42688c2ecf20Sopenharmony_ci * 15..13 mode 42698c2ecf20Sopenharmony_ci * 000=HDLC/SDLC 42708c2ecf20Sopenharmony_ci * 001=raw bit synchronous 42718c2ecf20Sopenharmony_ci * 010=asynchronous/isochronous 42728c2ecf20Sopenharmony_ci * 011=monosync byte synchronous 42738c2ecf20Sopenharmony_ci * 100=bisync byte synchronous 42748c2ecf20Sopenharmony_ci * 101=xsync byte synchronous 42758c2ecf20Sopenharmony_ci * 12..10 encoding 42768c2ecf20Sopenharmony_ci * 09 CRC enable 42778c2ecf20Sopenharmony_ci * 08 CRC32 42788c2ecf20Sopenharmony_ci * 07..03 reserved, must be 0 42798c2ecf20Sopenharmony_ci * 02 reset 42808c2ecf20Sopenharmony_ci * 01 enable 42818c2ecf20Sopenharmony_ci * 00 auto-DCD enable 42828c2ecf20Sopenharmony_ci */ 42838c2ecf20Sopenharmony_ci val = 0; 42848c2ecf20Sopenharmony_ci 42858c2ecf20Sopenharmony_ci switch(info->params.mode) { 42868c2ecf20Sopenharmony_ci case MGSL_MODE_XSYNC: 42878c2ecf20Sopenharmony_ci val |= BIT15 + BIT13; 42888c2ecf20Sopenharmony_ci break; 42898c2ecf20Sopenharmony_ci case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; 42908c2ecf20Sopenharmony_ci case MGSL_MODE_BISYNC: val |= BIT15; break; 42918c2ecf20Sopenharmony_ci case MGSL_MODE_RAW: val |= BIT13; break; 42928c2ecf20Sopenharmony_ci } 42938c2ecf20Sopenharmony_ci 42948c2ecf20Sopenharmony_ci switch(info->params.encoding) 42958c2ecf20Sopenharmony_ci { 42968c2ecf20Sopenharmony_ci case HDLC_ENCODING_NRZB: val |= BIT10; break; 42978c2ecf20Sopenharmony_ci case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; 42988c2ecf20Sopenharmony_ci case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; 42998c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; 43008c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; 43018c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; 43028c2ecf20Sopenharmony_ci case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; 43038c2ecf20Sopenharmony_ci } 43048c2ecf20Sopenharmony_ci 43058c2ecf20Sopenharmony_ci switch (info->params.crc_type & HDLC_CRC_MASK) 43068c2ecf20Sopenharmony_ci { 43078c2ecf20Sopenharmony_ci case HDLC_CRC_16_CCITT: val |= BIT9; break; 43088c2ecf20Sopenharmony_ci case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; 43098c2ecf20Sopenharmony_ci } 43108c2ecf20Sopenharmony_ci 43118c2ecf20Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_DCD) 43128c2ecf20Sopenharmony_ci val |= BIT0; 43138c2ecf20Sopenharmony_ci 43148c2ecf20Sopenharmony_ci wr_reg16(info, RCR, val); 43158c2ecf20Sopenharmony_ci 43168c2ecf20Sopenharmony_ci /* CCR (clock control) 43178c2ecf20Sopenharmony_ci * 43188c2ecf20Sopenharmony_ci * 07..05 tx clock source 43198c2ecf20Sopenharmony_ci * 04..02 rx clock source 43208c2ecf20Sopenharmony_ci * 01 auxclk enable 43218c2ecf20Sopenharmony_ci * 00 BRG enable 43228c2ecf20Sopenharmony_ci */ 43238c2ecf20Sopenharmony_ci val = 0; 43248c2ecf20Sopenharmony_ci 43258c2ecf20Sopenharmony_ci if (info->params.flags & HDLC_FLAG_TXC_BRG) 43268c2ecf20Sopenharmony_ci { 43278c2ecf20Sopenharmony_ci // when RxC source is DPLL, BRG generates 16X DPLL 43288c2ecf20Sopenharmony_ci // reference clock, so take TxC from BRG/16 to get 43298c2ecf20Sopenharmony_ci // transmit clock at actual data rate 43308c2ecf20Sopenharmony_ci if (info->params.flags & HDLC_FLAG_RXC_DPLL) 43318c2ecf20Sopenharmony_ci val |= BIT6 + BIT5; /* 011, txclk = BRG/16 */ 43328c2ecf20Sopenharmony_ci else 43338c2ecf20Sopenharmony_ci val |= BIT6; /* 010, txclk = BRG */ 43348c2ecf20Sopenharmony_ci } 43358c2ecf20Sopenharmony_ci else if (info->params.flags & HDLC_FLAG_TXC_DPLL) 43368c2ecf20Sopenharmony_ci val |= BIT7; /* 100, txclk = DPLL Input */ 43378c2ecf20Sopenharmony_ci else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN) 43388c2ecf20Sopenharmony_ci val |= BIT5; /* 001, txclk = RXC Input */ 43398c2ecf20Sopenharmony_ci 43408c2ecf20Sopenharmony_ci if (info->params.flags & HDLC_FLAG_RXC_BRG) 43418c2ecf20Sopenharmony_ci val |= BIT3; /* 010, rxclk = BRG */ 43428c2ecf20Sopenharmony_ci else if (info->params.flags & HDLC_FLAG_RXC_DPLL) 43438c2ecf20Sopenharmony_ci val |= BIT4; /* 100, rxclk = DPLL */ 43448c2ecf20Sopenharmony_ci else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN) 43458c2ecf20Sopenharmony_ci val |= BIT2; /* 001, rxclk = TXC Input */ 43468c2ecf20Sopenharmony_ci 43478c2ecf20Sopenharmony_ci if (info->params.clock_speed) 43488c2ecf20Sopenharmony_ci val |= BIT1 + BIT0; 43498c2ecf20Sopenharmony_ci 43508c2ecf20Sopenharmony_ci wr_reg8(info, CCR, (unsigned char)val); 43518c2ecf20Sopenharmony_ci 43528c2ecf20Sopenharmony_ci if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL)) 43538c2ecf20Sopenharmony_ci { 43548c2ecf20Sopenharmony_ci // program DPLL mode 43558c2ecf20Sopenharmony_ci switch(info->params.encoding) 43568c2ecf20Sopenharmony_ci { 43578c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_MARK: 43588c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_SPACE: 43598c2ecf20Sopenharmony_ci val = BIT7; break; 43608c2ecf20Sopenharmony_ci case HDLC_ENCODING_BIPHASE_LEVEL: 43618c2ecf20Sopenharmony_ci case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: 43628c2ecf20Sopenharmony_ci val = BIT7 + BIT6; break; 43638c2ecf20Sopenharmony_ci default: val = BIT6; // NRZ encodings 43648c2ecf20Sopenharmony_ci } 43658c2ecf20Sopenharmony_ci wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val)); 43668c2ecf20Sopenharmony_ci 43678c2ecf20Sopenharmony_ci // DPLL requires a 16X reference clock from BRG 43688c2ecf20Sopenharmony_ci set_rate(info, info->params.clock_speed * 16); 43698c2ecf20Sopenharmony_ci } 43708c2ecf20Sopenharmony_ci else 43718c2ecf20Sopenharmony_ci set_rate(info, info->params.clock_speed); 43728c2ecf20Sopenharmony_ci 43738c2ecf20Sopenharmony_ci tx_set_idle(info); 43748c2ecf20Sopenharmony_ci 43758c2ecf20Sopenharmony_ci msc_set_vcr(info); 43768c2ecf20Sopenharmony_ci 43778c2ecf20Sopenharmony_ci /* SCR (serial control) 43788c2ecf20Sopenharmony_ci * 43798c2ecf20Sopenharmony_ci * 15 1=tx req on FIFO half empty 43808c2ecf20Sopenharmony_ci * 14 1=rx req on FIFO half full 43818c2ecf20Sopenharmony_ci * 13 tx data IRQ enable 43828c2ecf20Sopenharmony_ci * 12 tx idle IRQ enable 43838c2ecf20Sopenharmony_ci * 11 underrun IRQ enable 43848c2ecf20Sopenharmony_ci * 10 rx data IRQ enable 43858c2ecf20Sopenharmony_ci * 09 rx idle IRQ enable 43868c2ecf20Sopenharmony_ci * 08 overrun IRQ enable 43878c2ecf20Sopenharmony_ci * 07 DSR IRQ enable 43888c2ecf20Sopenharmony_ci * 06 CTS IRQ enable 43898c2ecf20Sopenharmony_ci * 05 DCD IRQ enable 43908c2ecf20Sopenharmony_ci * 04 RI IRQ enable 43918c2ecf20Sopenharmony_ci * 03 reserved, must be zero 43928c2ecf20Sopenharmony_ci * 02 1=txd->rxd internal loopback enable 43938c2ecf20Sopenharmony_ci * 01 reserved, must be zero 43948c2ecf20Sopenharmony_ci * 00 1=master IRQ enable 43958c2ecf20Sopenharmony_ci */ 43968c2ecf20Sopenharmony_ci wr_reg16(info, SCR, BIT15 + BIT14 + BIT0); 43978c2ecf20Sopenharmony_ci 43988c2ecf20Sopenharmony_ci if (info->params.loopback) 43998c2ecf20Sopenharmony_ci enable_loopback(info); 44008c2ecf20Sopenharmony_ci} 44018c2ecf20Sopenharmony_ci 44028c2ecf20Sopenharmony_ci/* 44038c2ecf20Sopenharmony_ci * set transmit idle mode 44048c2ecf20Sopenharmony_ci */ 44058c2ecf20Sopenharmony_cistatic void tx_set_idle(struct slgt_info *info) 44068c2ecf20Sopenharmony_ci{ 44078c2ecf20Sopenharmony_ci unsigned char val; 44088c2ecf20Sopenharmony_ci unsigned short tcr; 44098c2ecf20Sopenharmony_ci 44108c2ecf20Sopenharmony_ci /* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits 44118c2ecf20Sopenharmony_ci * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits 44128c2ecf20Sopenharmony_ci */ 44138c2ecf20Sopenharmony_ci tcr = rd_reg16(info, TCR); 44148c2ecf20Sopenharmony_ci if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) { 44158c2ecf20Sopenharmony_ci /* disable preamble, set idle size to 16 bits */ 44168c2ecf20Sopenharmony_ci tcr = (tcr & ~(BIT6 + BIT5)) | BIT4; 44178c2ecf20Sopenharmony_ci /* MSB of 16 bit idle specified in tx preamble register (TPR) */ 44188c2ecf20Sopenharmony_ci wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff)); 44198c2ecf20Sopenharmony_ci } else if (!(tcr & BIT6)) { 44208c2ecf20Sopenharmony_ci /* preamble is disabled, set idle size to 8 bits */ 44218c2ecf20Sopenharmony_ci tcr &= ~(BIT5 + BIT4); 44228c2ecf20Sopenharmony_ci } 44238c2ecf20Sopenharmony_ci wr_reg16(info, TCR, tcr); 44248c2ecf20Sopenharmony_ci 44258c2ecf20Sopenharmony_ci if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) { 44268c2ecf20Sopenharmony_ci /* LSB of custom tx idle specified in tx idle register */ 44278c2ecf20Sopenharmony_ci val = (unsigned char)(info->idle_mode & 0xff); 44288c2ecf20Sopenharmony_ci } else { 44298c2ecf20Sopenharmony_ci /* standard 8 bit idle patterns */ 44308c2ecf20Sopenharmony_ci switch(info->idle_mode) 44318c2ecf20Sopenharmony_ci { 44328c2ecf20Sopenharmony_ci case HDLC_TXIDLE_FLAGS: val = 0x7e; break; 44338c2ecf20Sopenharmony_ci case HDLC_TXIDLE_ALT_ZEROS_ONES: 44348c2ecf20Sopenharmony_ci case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break; 44358c2ecf20Sopenharmony_ci case HDLC_TXIDLE_ZEROS: 44368c2ecf20Sopenharmony_ci case HDLC_TXIDLE_SPACE: val = 0x00; break; 44378c2ecf20Sopenharmony_ci default: val = 0xff; 44388c2ecf20Sopenharmony_ci } 44398c2ecf20Sopenharmony_ci } 44408c2ecf20Sopenharmony_ci 44418c2ecf20Sopenharmony_ci wr_reg8(info, TIR, val); 44428c2ecf20Sopenharmony_ci} 44438c2ecf20Sopenharmony_ci 44448c2ecf20Sopenharmony_ci/* 44458c2ecf20Sopenharmony_ci * get state of V24 status (input) signals 44468c2ecf20Sopenharmony_ci */ 44478c2ecf20Sopenharmony_cistatic void get_gtsignals(struct slgt_info *info) 44488c2ecf20Sopenharmony_ci{ 44498c2ecf20Sopenharmony_ci unsigned short status = rd_reg16(info, SSR); 44508c2ecf20Sopenharmony_ci 44518c2ecf20Sopenharmony_ci /* clear all serial signals except RTS and DTR */ 44528c2ecf20Sopenharmony_ci info->signals &= SerialSignal_RTS | SerialSignal_DTR; 44538c2ecf20Sopenharmony_ci 44548c2ecf20Sopenharmony_ci if (status & BIT3) 44558c2ecf20Sopenharmony_ci info->signals |= SerialSignal_DSR; 44568c2ecf20Sopenharmony_ci if (status & BIT2) 44578c2ecf20Sopenharmony_ci info->signals |= SerialSignal_CTS; 44588c2ecf20Sopenharmony_ci if (status & BIT1) 44598c2ecf20Sopenharmony_ci info->signals |= SerialSignal_DCD; 44608c2ecf20Sopenharmony_ci if (status & BIT0) 44618c2ecf20Sopenharmony_ci info->signals |= SerialSignal_RI; 44628c2ecf20Sopenharmony_ci} 44638c2ecf20Sopenharmony_ci 44648c2ecf20Sopenharmony_ci/* 44658c2ecf20Sopenharmony_ci * set V.24 Control Register based on current configuration 44668c2ecf20Sopenharmony_ci */ 44678c2ecf20Sopenharmony_cistatic void msc_set_vcr(struct slgt_info *info) 44688c2ecf20Sopenharmony_ci{ 44698c2ecf20Sopenharmony_ci unsigned char val = 0; 44708c2ecf20Sopenharmony_ci 44718c2ecf20Sopenharmony_ci /* VCR (V.24 control) 44728c2ecf20Sopenharmony_ci * 44738c2ecf20Sopenharmony_ci * 07..04 serial IF select 44748c2ecf20Sopenharmony_ci * 03 DTR 44758c2ecf20Sopenharmony_ci * 02 RTS 44768c2ecf20Sopenharmony_ci * 01 LL 44778c2ecf20Sopenharmony_ci * 00 RL 44788c2ecf20Sopenharmony_ci */ 44798c2ecf20Sopenharmony_ci 44808c2ecf20Sopenharmony_ci switch(info->if_mode & MGSL_INTERFACE_MASK) 44818c2ecf20Sopenharmony_ci { 44828c2ecf20Sopenharmony_ci case MGSL_INTERFACE_RS232: 44838c2ecf20Sopenharmony_ci val |= BIT5; /* 0010 */ 44848c2ecf20Sopenharmony_ci break; 44858c2ecf20Sopenharmony_ci case MGSL_INTERFACE_V35: 44868c2ecf20Sopenharmony_ci val |= BIT7 + BIT6 + BIT5; /* 1110 */ 44878c2ecf20Sopenharmony_ci break; 44888c2ecf20Sopenharmony_ci case MGSL_INTERFACE_RS422: 44898c2ecf20Sopenharmony_ci val |= BIT6; /* 0100 */ 44908c2ecf20Sopenharmony_ci break; 44918c2ecf20Sopenharmony_ci } 44928c2ecf20Sopenharmony_ci 44938c2ecf20Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_MSB_FIRST) 44948c2ecf20Sopenharmony_ci val |= BIT4; 44958c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_DTR) 44968c2ecf20Sopenharmony_ci val |= BIT3; 44978c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_RTS) 44988c2ecf20Sopenharmony_ci val |= BIT2; 44998c2ecf20Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_LL) 45008c2ecf20Sopenharmony_ci val |= BIT1; 45018c2ecf20Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_RL) 45028c2ecf20Sopenharmony_ci val |= BIT0; 45038c2ecf20Sopenharmony_ci wr_reg8(info, VCR, val); 45048c2ecf20Sopenharmony_ci} 45058c2ecf20Sopenharmony_ci 45068c2ecf20Sopenharmony_ci/* 45078c2ecf20Sopenharmony_ci * set state of V24 control (output) signals 45088c2ecf20Sopenharmony_ci */ 45098c2ecf20Sopenharmony_cistatic void set_gtsignals(struct slgt_info *info) 45108c2ecf20Sopenharmony_ci{ 45118c2ecf20Sopenharmony_ci unsigned char val = rd_reg8(info, VCR); 45128c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_DTR) 45138c2ecf20Sopenharmony_ci val |= BIT3; 45148c2ecf20Sopenharmony_ci else 45158c2ecf20Sopenharmony_ci val &= ~BIT3; 45168c2ecf20Sopenharmony_ci if (info->signals & SerialSignal_RTS) 45178c2ecf20Sopenharmony_ci val |= BIT2; 45188c2ecf20Sopenharmony_ci else 45198c2ecf20Sopenharmony_ci val &= ~BIT2; 45208c2ecf20Sopenharmony_ci wr_reg8(info, VCR, val); 45218c2ecf20Sopenharmony_ci} 45228c2ecf20Sopenharmony_ci 45238c2ecf20Sopenharmony_ci/* 45248c2ecf20Sopenharmony_ci * free range of receive DMA buffers (i to last) 45258c2ecf20Sopenharmony_ci */ 45268c2ecf20Sopenharmony_cistatic void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last) 45278c2ecf20Sopenharmony_ci{ 45288c2ecf20Sopenharmony_ci int done = 0; 45298c2ecf20Sopenharmony_ci 45308c2ecf20Sopenharmony_ci while(!done) { 45318c2ecf20Sopenharmony_ci /* reset current buffer for reuse */ 45328c2ecf20Sopenharmony_ci info->rbufs[i].status = 0; 45338c2ecf20Sopenharmony_ci set_desc_count(info->rbufs[i], info->rbuf_fill_level); 45348c2ecf20Sopenharmony_ci if (i == last) 45358c2ecf20Sopenharmony_ci done = 1; 45368c2ecf20Sopenharmony_ci if (++i == info->rbuf_count) 45378c2ecf20Sopenharmony_ci i = 0; 45388c2ecf20Sopenharmony_ci } 45398c2ecf20Sopenharmony_ci info->rbuf_current = i; 45408c2ecf20Sopenharmony_ci} 45418c2ecf20Sopenharmony_ci 45428c2ecf20Sopenharmony_ci/* 45438c2ecf20Sopenharmony_ci * mark all receive DMA buffers as free 45448c2ecf20Sopenharmony_ci */ 45458c2ecf20Sopenharmony_cistatic void reset_rbufs(struct slgt_info *info) 45468c2ecf20Sopenharmony_ci{ 45478c2ecf20Sopenharmony_ci free_rbufs(info, 0, info->rbuf_count - 1); 45488c2ecf20Sopenharmony_ci info->rbuf_fill_index = 0; 45498c2ecf20Sopenharmony_ci info->rbuf_fill_count = 0; 45508c2ecf20Sopenharmony_ci} 45518c2ecf20Sopenharmony_ci 45528c2ecf20Sopenharmony_ci/* 45538c2ecf20Sopenharmony_ci * pass receive HDLC frame to upper layer 45548c2ecf20Sopenharmony_ci * 45558c2ecf20Sopenharmony_ci * return true if frame available, otherwise false 45568c2ecf20Sopenharmony_ci */ 45578c2ecf20Sopenharmony_cistatic bool rx_get_frame(struct slgt_info *info) 45588c2ecf20Sopenharmony_ci{ 45598c2ecf20Sopenharmony_ci unsigned int start, end; 45608c2ecf20Sopenharmony_ci unsigned short status; 45618c2ecf20Sopenharmony_ci unsigned int framesize = 0; 45628c2ecf20Sopenharmony_ci unsigned long flags; 45638c2ecf20Sopenharmony_ci struct tty_struct *tty = info->port.tty; 45648c2ecf20Sopenharmony_ci unsigned char addr_field = 0xff; 45658c2ecf20Sopenharmony_ci unsigned int crc_size = 0; 45668c2ecf20Sopenharmony_ci 45678c2ecf20Sopenharmony_ci switch (info->params.crc_type & HDLC_CRC_MASK) { 45688c2ecf20Sopenharmony_ci case HDLC_CRC_16_CCITT: crc_size = 2; break; 45698c2ecf20Sopenharmony_ci case HDLC_CRC_32_CCITT: crc_size = 4; break; 45708c2ecf20Sopenharmony_ci } 45718c2ecf20Sopenharmony_ci 45728c2ecf20Sopenharmony_cicheck_again: 45738c2ecf20Sopenharmony_ci 45748c2ecf20Sopenharmony_ci framesize = 0; 45758c2ecf20Sopenharmony_ci addr_field = 0xff; 45768c2ecf20Sopenharmony_ci start = end = info->rbuf_current; 45778c2ecf20Sopenharmony_ci 45788c2ecf20Sopenharmony_ci for (;;) { 45798c2ecf20Sopenharmony_ci if (!desc_complete(info->rbufs[end])) 45808c2ecf20Sopenharmony_ci goto cleanup; 45818c2ecf20Sopenharmony_ci 45828c2ecf20Sopenharmony_ci if (framesize == 0 && info->params.addr_filter != 0xff) 45838c2ecf20Sopenharmony_ci addr_field = info->rbufs[end].buf[0]; 45848c2ecf20Sopenharmony_ci 45858c2ecf20Sopenharmony_ci framesize += desc_count(info->rbufs[end]); 45868c2ecf20Sopenharmony_ci 45878c2ecf20Sopenharmony_ci if (desc_eof(info->rbufs[end])) 45888c2ecf20Sopenharmony_ci break; 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_ci if (++end == info->rbuf_count) 45918c2ecf20Sopenharmony_ci end = 0; 45928c2ecf20Sopenharmony_ci 45938c2ecf20Sopenharmony_ci if (end == info->rbuf_current) { 45948c2ecf20Sopenharmony_ci if (info->rx_enabled){ 45958c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 45968c2ecf20Sopenharmony_ci rx_start(info); 45978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 45988c2ecf20Sopenharmony_ci } 45998c2ecf20Sopenharmony_ci goto cleanup; 46008c2ecf20Sopenharmony_ci } 46018c2ecf20Sopenharmony_ci } 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_ci /* status 46048c2ecf20Sopenharmony_ci * 46058c2ecf20Sopenharmony_ci * 15 buffer complete 46068c2ecf20Sopenharmony_ci * 14..06 reserved 46078c2ecf20Sopenharmony_ci * 05..04 residue 46088c2ecf20Sopenharmony_ci * 02 eof (end of frame) 46098c2ecf20Sopenharmony_ci * 01 CRC error 46108c2ecf20Sopenharmony_ci * 00 abort 46118c2ecf20Sopenharmony_ci */ 46128c2ecf20Sopenharmony_ci status = desc_status(info->rbufs[end]); 46138c2ecf20Sopenharmony_ci 46148c2ecf20Sopenharmony_ci /* ignore CRC bit if not using CRC (bit is undefined) */ 46158c2ecf20Sopenharmony_ci if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE) 46168c2ecf20Sopenharmony_ci status &= ~BIT1; 46178c2ecf20Sopenharmony_ci 46188c2ecf20Sopenharmony_ci if (framesize == 0 || 46198c2ecf20Sopenharmony_ci (addr_field != 0xff && addr_field != info->params.addr_filter)) { 46208c2ecf20Sopenharmony_ci free_rbufs(info, start, end); 46218c2ecf20Sopenharmony_ci goto check_again; 46228c2ecf20Sopenharmony_ci } 46238c2ecf20Sopenharmony_ci 46248c2ecf20Sopenharmony_ci if (framesize < (2 + crc_size) || status & BIT0) { 46258c2ecf20Sopenharmony_ci info->icount.rxshort++; 46268c2ecf20Sopenharmony_ci framesize = 0; 46278c2ecf20Sopenharmony_ci } else if (status & BIT1) { 46288c2ecf20Sopenharmony_ci info->icount.rxcrc++; 46298c2ecf20Sopenharmony_ci if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) 46308c2ecf20Sopenharmony_ci framesize = 0; 46318c2ecf20Sopenharmony_ci } 46328c2ecf20Sopenharmony_ci 46338c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 46348c2ecf20Sopenharmony_ci if (framesize == 0) { 46358c2ecf20Sopenharmony_ci info->netdev->stats.rx_errors++; 46368c2ecf20Sopenharmony_ci info->netdev->stats.rx_frame_errors++; 46378c2ecf20Sopenharmony_ci } 46388c2ecf20Sopenharmony_ci#endif 46398c2ecf20Sopenharmony_ci 46408c2ecf20Sopenharmony_ci DBGBH(("%s rx frame status=%04X size=%d\n", 46418c2ecf20Sopenharmony_ci info->device_name, status, framesize)); 46428c2ecf20Sopenharmony_ci DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx"); 46438c2ecf20Sopenharmony_ci 46448c2ecf20Sopenharmony_ci if (framesize) { 46458c2ecf20Sopenharmony_ci if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) { 46468c2ecf20Sopenharmony_ci framesize -= crc_size; 46478c2ecf20Sopenharmony_ci crc_size = 0; 46488c2ecf20Sopenharmony_ci } 46498c2ecf20Sopenharmony_ci 46508c2ecf20Sopenharmony_ci if (framesize > info->max_frame_size + crc_size) 46518c2ecf20Sopenharmony_ci info->icount.rxlong++; 46528c2ecf20Sopenharmony_ci else { 46538c2ecf20Sopenharmony_ci /* copy dma buffer(s) to contiguous temp buffer */ 46548c2ecf20Sopenharmony_ci int copy_count = framesize; 46558c2ecf20Sopenharmony_ci int i = start; 46568c2ecf20Sopenharmony_ci unsigned char *p = info->tmp_rbuf; 46578c2ecf20Sopenharmony_ci info->tmp_rbuf_count = framesize; 46588c2ecf20Sopenharmony_ci 46598c2ecf20Sopenharmony_ci info->icount.rxok++; 46608c2ecf20Sopenharmony_ci 46618c2ecf20Sopenharmony_ci while(copy_count) { 46628c2ecf20Sopenharmony_ci int partial_count = min_t(int, copy_count, info->rbuf_fill_level); 46638c2ecf20Sopenharmony_ci memcpy(p, info->rbufs[i].buf, partial_count); 46648c2ecf20Sopenharmony_ci p += partial_count; 46658c2ecf20Sopenharmony_ci copy_count -= partial_count; 46668c2ecf20Sopenharmony_ci if (++i == info->rbuf_count) 46678c2ecf20Sopenharmony_ci i = 0; 46688c2ecf20Sopenharmony_ci } 46698c2ecf20Sopenharmony_ci 46708c2ecf20Sopenharmony_ci if (info->params.crc_type & HDLC_CRC_RETURN_EX) { 46718c2ecf20Sopenharmony_ci *p = (status & BIT1) ? RX_CRC_ERROR : RX_OK; 46728c2ecf20Sopenharmony_ci framesize++; 46738c2ecf20Sopenharmony_ci } 46748c2ecf20Sopenharmony_ci 46758c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 46768c2ecf20Sopenharmony_ci if (info->netcount) 46778c2ecf20Sopenharmony_ci hdlcdev_rx(info,info->tmp_rbuf, framesize); 46788c2ecf20Sopenharmony_ci else 46798c2ecf20Sopenharmony_ci#endif 46808c2ecf20Sopenharmony_ci ldisc_receive_buf(tty, info->tmp_rbuf, info->flag_buf, framesize); 46818c2ecf20Sopenharmony_ci } 46828c2ecf20Sopenharmony_ci } 46838c2ecf20Sopenharmony_ci free_rbufs(info, start, end); 46848c2ecf20Sopenharmony_ci return true; 46858c2ecf20Sopenharmony_ci 46868c2ecf20Sopenharmony_cicleanup: 46878c2ecf20Sopenharmony_ci return false; 46888c2ecf20Sopenharmony_ci} 46898c2ecf20Sopenharmony_ci 46908c2ecf20Sopenharmony_ci/* 46918c2ecf20Sopenharmony_ci * pass receive buffer (RAW synchronous mode) to tty layer 46928c2ecf20Sopenharmony_ci * return true if buffer available, otherwise false 46938c2ecf20Sopenharmony_ci */ 46948c2ecf20Sopenharmony_cistatic bool rx_get_buf(struct slgt_info *info) 46958c2ecf20Sopenharmony_ci{ 46968c2ecf20Sopenharmony_ci unsigned int i = info->rbuf_current; 46978c2ecf20Sopenharmony_ci unsigned int count; 46988c2ecf20Sopenharmony_ci 46998c2ecf20Sopenharmony_ci if (!desc_complete(info->rbufs[i])) 47008c2ecf20Sopenharmony_ci return false; 47018c2ecf20Sopenharmony_ci count = desc_count(info->rbufs[i]); 47028c2ecf20Sopenharmony_ci switch(info->params.mode) { 47038c2ecf20Sopenharmony_ci case MGSL_MODE_MONOSYNC: 47048c2ecf20Sopenharmony_ci case MGSL_MODE_BISYNC: 47058c2ecf20Sopenharmony_ci case MGSL_MODE_XSYNC: 47068c2ecf20Sopenharmony_ci /* ignore residue in byte synchronous modes */ 47078c2ecf20Sopenharmony_ci if (desc_residue(info->rbufs[i])) 47088c2ecf20Sopenharmony_ci count--; 47098c2ecf20Sopenharmony_ci break; 47108c2ecf20Sopenharmony_ci } 47118c2ecf20Sopenharmony_ci DBGDATA(info, info->rbufs[i].buf, count, "rx"); 47128c2ecf20Sopenharmony_ci DBGINFO(("rx_get_buf size=%d\n", count)); 47138c2ecf20Sopenharmony_ci if (count) 47148c2ecf20Sopenharmony_ci ldisc_receive_buf(info->port.tty, info->rbufs[i].buf, 47158c2ecf20Sopenharmony_ci info->flag_buf, count); 47168c2ecf20Sopenharmony_ci free_rbufs(info, i, i); 47178c2ecf20Sopenharmony_ci return true; 47188c2ecf20Sopenharmony_ci} 47198c2ecf20Sopenharmony_ci 47208c2ecf20Sopenharmony_cistatic void reset_tbufs(struct slgt_info *info) 47218c2ecf20Sopenharmony_ci{ 47228c2ecf20Sopenharmony_ci unsigned int i; 47238c2ecf20Sopenharmony_ci info->tbuf_current = 0; 47248c2ecf20Sopenharmony_ci for (i=0 ; i < info->tbuf_count ; i++) { 47258c2ecf20Sopenharmony_ci info->tbufs[i].status = 0; 47268c2ecf20Sopenharmony_ci info->tbufs[i].count = 0; 47278c2ecf20Sopenharmony_ci } 47288c2ecf20Sopenharmony_ci} 47298c2ecf20Sopenharmony_ci 47308c2ecf20Sopenharmony_ci/* 47318c2ecf20Sopenharmony_ci * return number of free transmit DMA buffers 47328c2ecf20Sopenharmony_ci */ 47338c2ecf20Sopenharmony_cistatic unsigned int free_tbuf_count(struct slgt_info *info) 47348c2ecf20Sopenharmony_ci{ 47358c2ecf20Sopenharmony_ci unsigned int count = 0; 47368c2ecf20Sopenharmony_ci unsigned int i = info->tbuf_current; 47378c2ecf20Sopenharmony_ci 47388c2ecf20Sopenharmony_ci do 47398c2ecf20Sopenharmony_ci { 47408c2ecf20Sopenharmony_ci if (desc_count(info->tbufs[i])) 47418c2ecf20Sopenharmony_ci break; /* buffer in use */ 47428c2ecf20Sopenharmony_ci ++count; 47438c2ecf20Sopenharmony_ci if (++i == info->tbuf_count) 47448c2ecf20Sopenharmony_ci i=0; 47458c2ecf20Sopenharmony_ci } while (i != info->tbuf_current); 47468c2ecf20Sopenharmony_ci 47478c2ecf20Sopenharmony_ci /* if tx DMA active, last zero count buffer is in use */ 47488c2ecf20Sopenharmony_ci if (count && (rd_reg32(info, TDCSR) & BIT0)) 47498c2ecf20Sopenharmony_ci --count; 47508c2ecf20Sopenharmony_ci 47518c2ecf20Sopenharmony_ci return count; 47528c2ecf20Sopenharmony_ci} 47538c2ecf20Sopenharmony_ci 47548c2ecf20Sopenharmony_ci/* 47558c2ecf20Sopenharmony_ci * return number of bytes in unsent transmit DMA buffers 47568c2ecf20Sopenharmony_ci * and the serial controller tx FIFO 47578c2ecf20Sopenharmony_ci */ 47588c2ecf20Sopenharmony_cistatic unsigned int tbuf_bytes(struct slgt_info *info) 47598c2ecf20Sopenharmony_ci{ 47608c2ecf20Sopenharmony_ci unsigned int total_count = 0; 47618c2ecf20Sopenharmony_ci unsigned int i = info->tbuf_current; 47628c2ecf20Sopenharmony_ci unsigned int reg_value; 47638c2ecf20Sopenharmony_ci unsigned int count; 47648c2ecf20Sopenharmony_ci unsigned int active_buf_count = 0; 47658c2ecf20Sopenharmony_ci 47668c2ecf20Sopenharmony_ci /* 47678c2ecf20Sopenharmony_ci * Add descriptor counts for all tx DMA buffers. 47688c2ecf20Sopenharmony_ci * If count is zero (cleared by DMA controller after read), 47698c2ecf20Sopenharmony_ci * the buffer is complete or is actively being read from. 47708c2ecf20Sopenharmony_ci * 47718c2ecf20Sopenharmony_ci * Record buf_count of last buffer with zero count starting 47728c2ecf20Sopenharmony_ci * from current ring position. buf_count is mirror 47738c2ecf20Sopenharmony_ci * copy of count and is not cleared by serial controller. 47748c2ecf20Sopenharmony_ci * If DMA controller is active, that buffer is actively 47758c2ecf20Sopenharmony_ci * being read so add to total. 47768c2ecf20Sopenharmony_ci */ 47778c2ecf20Sopenharmony_ci do { 47788c2ecf20Sopenharmony_ci count = desc_count(info->tbufs[i]); 47798c2ecf20Sopenharmony_ci if (count) 47808c2ecf20Sopenharmony_ci total_count += count; 47818c2ecf20Sopenharmony_ci else if (!total_count) 47828c2ecf20Sopenharmony_ci active_buf_count = info->tbufs[i].buf_count; 47838c2ecf20Sopenharmony_ci if (++i == info->tbuf_count) 47848c2ecf20Sopenharmony_ci i = 0; 47858c2ecf20Sopenharmony_ci } while (i != info->tbuf_current); 47868c2ecf20Sopenharmony_ci 47878c2ecf20Sopenharmony_ci /* read tx DMA status register */ 47888c2ecf20Sopenharmony_ci reg_value = rd_reg32(info, TDCSR); 47898c2ecf20Sopenharmony_ci 47908c2ecf20Sopenharmony_ci /* if tx DMA active, last zero count buffer is in use */ 47918c2ecf20Sopenharmony_ci if (reg_value & BIT0) 47928c2ecf20Sopenharmony_ci total_count += active_buf_count; 47938c2ecf20Sopenharmony_ci 47948c2ecf20Sopenharmony_ci /* add tx FIFO count = reg_value[15..8] */ 47958c2ecf20Sopenharmony_ci total_count += (reg_value >> 8) & 0xff; 47968c2ecf20Sopenharmony_ci 47978c2ecf20Sopenharmony_ci /* if transmitter active add one byte for shift register */ 47988c2ecf20Sopenharmony_ci if (info->tx_active) 47998c2ecf20Sopenharmony_ci total_count++; 48008c2ecf20Sopenharmony_ci 48018c2ecf20Sopenharmony_ci return total_count; 48028c2ecf20Sopenharmony_ci} 48038c2ecf20Sopenharmony_ci 48048c2ecf20Sopenharmony_ci/* 48058c2ecf20Sopenharmony_ci * load data into transmit DMA buffer ring and start transmitter if needed 48068c2ecf20Sopenharmony_ci * return true if data accepted, otherwise false (buffers full) 48078c2ecf20Sopenharmony_ci */ 48088c2ecf20Sopenharmony_cistatic bool tx_load(struct slgt_info *info, const char *buf, unsigned int size) 48098c2ecf20Sopenharmony_ci{ 48108c2ecf20Sopenharmony_ci unsigned short count; 48118c2ecf20Sopenharmony_ci unsigned int i; 48128c2ecf20Sopenharmony_ci struct slgt_desc *d; 48138c2ecf20Sopenharmony_ci 48148c2ecf20Sopenharmony_ci /* check required buffer space */ 48158c2ecf20Sopenharmony_ci if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info)) 48168c2ecf20Sopenharmony_ci return false; 48178c2ecf20Sopenharmony_ci 48188c2ecf20Sopenharmony_ci DBGDATA(info, buf, size, "tx"); 48198c2ecf20Sopenharmony_ci 48208c2ecf20Sopenharmony_ci /* 48218c2ecf20Sopenharmony_ci * copy data to one or more DMA buffers in circular ring 48228c2ecf20Sopenharmony_ci * tbuf_start = first buffer for this data 48238c2ecf20Sopenharmony_ci * tbuf_current = next free buffer 48248c2ecf20Sopenharmony_ci * 48258c2ecf20Sopenharmony_ci * Copy all data before making data visible to DMA controller by 48268c2ecf20Sopenharmony_ci * setting descriptor count of the first buffer. 48278c2ecf20Sopenharmony_ci * This prevents an active DMA controller from reading the first DMA 48288c2ecf20Sopenharmony_ci * buffers of a frame and stopping before the final buffers are filled. 48298c2ecf20Sopenharmony_ci */ 48308c2ecf20Sopenharmony_ci 48318c2ecf20Sopenharmony_ci info->tbuf_start = i = info->tbuf_current; 48328c2ecf20Sopenharmony_ci 48338c2ecf20Sopenharmony_ci while (size) { 48348c2ecf20Sopenharmony_ci d = &info->tbufs[i]; 48358c2ecf20Sopenharmony_ci 48368c2ecf20Sopenharmony_ci count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size); 48378c2ecf20Sopenharmony_ci memcpy(d->buf, buf, count); 48388c2ecf20Sopenharmony_ci 48398c2ecf20Sopenharmony_ci size -= count; 48408c2ecf20Sopenharmony_ci buf += count; 48418c2ecf20Sopenharmony_ci 48428c2ecf20Sopenharmony_ci /* 48438c2ecf20Sopenharmony_ci * set EOF bit for last buffer of HDLC frame or 48448c2ecf20Sopenharmony_ci * for every buffer in raw mode 48458c2ecf20Sopenharmony_ci */ 48468c2ecf20Sopenharmony_ci if ((!size && info->params.mode == MGSL_MODE_HDLC) || 48478c2ecf20Sopenharmony_ci info->params.mode == MGSL_MODE_RAW) 48488c2ecf20Sopenharmony_ci set_desc_eof(*d, 1); 48498c2ecf20Sopenharmony_ci else 48508c2ecf20Sopenharmony_ci set_desc_eof(*d, 0); 48518c2ecf20Sopenharmony_ci 48528c2ecf20Sopenharmony_ci /* set descriptor count for all but first buffer */ 48538c2ecf20Sopenharmony_ci if (i != info->tbuf_start) 48548c2ecf20Sopenharmony_ci set_desc_count(*d, count); 48558c2ecf20Sopenharmony_ci d->buf_count = count; 48568c2ecf20Sopenharmony_ci 48578c2ecf20Sopenharmony_ci if (++i == info->tbuf_count) 48588c2ecf20Sopenharmony_ci i = 0; 48598c2ecf20Sopenharmony_ci } 48608c2ecf20Sopenharmony_ci 48618c2ecf20Sopenharmony_ci info->tbuf_current = i; 48628c2ecf20Sopenharmony_ci 48638c2ecf20Sopenharmony_ci /* set first buffer count to make new data visible to DMA controller */ 48648c2ecf20Sopenharmony_ci d = &info->tbufs[info->tbuf_start]; 48658c2ecf20Sopenharmony_ci set_desc_count(*d, d->buf_count); 48668c2ecf20Sopenharmony_ci 48678c2ecf20Sopenharmony_ci /* start transmitter if needed and update transmit timeout */ 48688c2ecf20Sopenharmony_ci if (!info->tx_active) 48698c2ecf20Sopenharmony_ci tx_start(info); 48708c2ecf20Sopenharmony_ci update_tx_timer(info); 48718c2ecf20Sopenharmony_ci 48728c2ecf20Sopenharmony_ci return true; 48738c2ecf20Sopenharmony_ci} 48748c2ecf20Sopenharmony_ci 48758c2ecf20Sopenharmony_cistatic int register_test(struct slgt_info *info) 48768c2ecf20Sopenharmony_ci{ 48778c2ecf20Sopenharmony_ci static unsigned short patterns[] = 48788c2ecf20Sopenharmony_ci {0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696}; 48798c2ecf20Sopenharmony_ci static unsigned int count = ARRAY_SIZE(patterns); 48808c2ecf20Sopenharmony_ci unsigned int i; 48818c2ecf20Sopenharmony_ci int rc = 0; 48828c2ecf20Sopenharmony_ci 48838c2ecf20Sopenharmony_ci for (i=0 ; i < count ; i++) { 48848c2ecf20Sopenharmony_ci wr_reg16(info, TIR, patterns[i]); 48858c2ecf20Sopenharmony_ci wr_reg16(info, BDR, patterns[(i+1)%count]); 48868c2ecf20Sopenharmony_ci if ((rd_reg16(info, TIR) != patterns[i]) || 48878c2ecf20Sopenharmony_ci (rd_reg16(info, BDR) != patterns[(i+1)%count])) { 48888c2ecf20Sopenharmony_ci rc = -ENODEV; 48898c2ecf20Sopenharmony_ci break; 48908c2ecf20Sopenharmony_ci } 48918c2ecf20Sopenharmony_ci } 48928c2ecf20Sopenharmony_ci info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0; 48938c2ecf20Sopenharmony_ci info->init_error = rc ? 0 : DiagStatus_AddressFailure; 48948c2ecf20Sopenharmony_ci return rc; 48958c2ecf20Sopenharmony_ci} 48968c2ecf20Sopenharmony_ci 48978c2ecf20Sopenharmony_cistatic int irq_test(struct slgt_info *info) 48988c2ecf20Sopenharmony_ci{ 48998c2ecf20Sopenharmony_ci unsigned long timeout; 49008c2ecf20Sopenharmony_ci unsigned long flags; 49018c2ecf20Sopenharmony_ci struct tty_struct *oldtty = info->port.tty; 49028c2ecf20Sopenharmony_ci u32 speed = info->params.data_rate; 49038c2ecf20Sopenharmony_ci 49048c2ecf20Sopenharmony_ci info->params.data_rate = 921600; 49058c2ecf20Sopenharmony_ci info->port.tty = NULL; 49068c2ecf20Sopenharmony_ci 49078c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 49088c2ecf20Sopenharmony_ci async_mode(info); 49098c2ecf20Sopenharmony_ci slgt_irq_on(info, IRQ_TXIDLE); 49108c2ecf20Sopenharmony_ci 49118c2ecf20Sopenharmony_ci /* enable transmitter */ 49128c2ecf20Sopenharmony_ci wr_reg16(info, TCR, 49138c2ecf20Sopenharmony_ci (unsigned short)(rd_reg16(info, TCR) | BIT1)); 49148c2ecf20Sopenharmony_ci 49158c2ecf20Sopenharmony_ci /* write one byte and wait for tx idle */ 49168c2ecf20Sopenharmony_ci wr_reg16(info, TDR, 0); 49178c2ecf20Sopenharmony_ci 49188c2ecf20Sopenharmony_ci /* assume failure */ 49198c2ecf20Sopenharmony_ci info->init_error = DiagStatus_IrqFailure; 49208c2ecf20Sopenharmony_ci info->irq_occurred = false; 49218c2ecf20Sopenharmony_ci 49228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 49238c2ecf20Sopenharmony_ci 49248c2ecf20Sopenharmony_ci timeout=100; 49258c2ecf20Sopenharmony_ci while(timeout-- && !info->irq_occurred) 49268c2ecf20Sopenharmony_ci msleep_interruptible(10); 49278c2ecf20Sopenharmony_ci 49288c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 49298c2ecf20Sopenharmony_ci reset_port(info); 49308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 49318c2ecf20Sopenharmony_ci 49328c2ecf20Sopenharmony_ci info->params.data_rate = speed; 49338c2ecf20Sopenharmony_ci info->port.tty = oldtty; 49348c2ecf20Sopenharmony_ci 49358c2ecf20Sopenharmony_ci info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure; 49368c2ecf20Sopenharmony_ci return info->irq_occurred ? 0 : -ENODEV; 49378c2ecf20Sopenharmony_ci} 49388c2ecf20Sopenharmony_ci 49398c2ecf20Sopenharmony_cistatic int loopback_test_rx(struct slgt_info *info) 49408c2ecf20Sopenharmony_ci{ 49418c2ecf20Sopenharmony_ci unsigned char *src, *dest; 49428c2ecf20Sopenharmony_ci int count; 49438c2ecf20Sopenharmony_ci 49448c2ecf20Sopenharmony_ci if (desc_complete(info->rbufs[0])) { 49458c2ecf20Sopenharmony_ci count = desc_count(info->rbufs[0]); 49468c2ecf20Sopenharmony_ci src = info->rbufs[0].buf; 49478c2ecf20Sopenharmony_ci dest = info->tmp_rbuf; 49488c2ecf20Sopenharmony_ci 49498c2ecf20Sopenharmony_ci for( ; count ; count-=2, src+=2) { 49508c2ecf20Sopenharmony_ci /* src=data byte (src+1)=status byte */ 49518c2ecf20Sopenharmony_ci if (!(*(src+1) & (BIT9 + BIT8))) { 49528c2ecf20Sopenharmony_ci *dest = *src; 49538c2ecf20Sopenharmony_ci dest++; 49548c2ecf20Sopenharmony_ci info->tmp_rbuf_count++; 49558c2ecf20Sopenharmony_ci } 49568c2ecf20Sopenharmony_ci } 49578c2ecf20Sopenharmony_ci DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx"); 49588c2ecf20Sopenharmony_ci return 1; 49598c2ecf20Sopenharmony_ci } 49608c2ecf20Sopenharmony_ci return 0; 49618c2ecf20Sopenharmony_ci} 49628c2ecf20Sopenharmony_ci 49638c2ecf20Sopenharmony_cistatic int loopback_test(struct slgt_info *info) 49648c2ecf20Sopenharmony_ci{ 49658c2ecf20Sopenharmony_ci#define TESTFRAMESIZE 20 49668c2ecf20Sopenharmony_ci 49678c2ecf20Sopenharmony_ci unsigned long timeout; 49688c2ecf20Sopenharmony_ci u16 count = TESTFRAMESIZE; 49698c2ecf20Sopenharmony_ci unsigned char buf[TESTFRAMESIZE]; 49708c2ecf20Sopenharmony_ci int rc = -ENODEV; 49718c2ecf20Sopenharmony_ci unsigned long flags; 49728c2ecf20Sopenharmony_ci 49738c2ecf20Sopenharmony_ci struct tty_struct *oldtty = info->port.tty; 49748c2ecf20Sopenharmony_ci MGSL_PARAMS params; 49758c2ecf20Sopenharmony_ci 49768c2ecf20Sopenharmony_ci memcpy(¶ms, &info->params, sizeof(params)); 49778c2ecf20Sopenharmony_ci 49788c2ecf20Sopenharmony_ci info->params.mode = MGSL_MODE_ASYNC; 49798c2ecf20Sopenharmony_ci info->params.data_rate = 921600; 49808c2ecf20Sopenharmony_ci info->params.loopback = 1; 49818c2ecf20Sopenharmony_ci info->port.tty = NULL; 49828c2ecf20Sopenharmony_ci 49838c2ecf20Sopenharmony_ci /* build and send transmit frame */ 49848c2ecf20Sopenharmony_ci for (count = 0; count < TESTFRAMESIZE; ++count) 49858c2ecf20Sopenharmony_ci buf[count] = (unsigned char)count; 49868c2ecf20Sopenharmony_ci 49878c2ecf20Sopenharmony_ci info->tmp_rbuf_count = 0; 49888c2ecf20Sopenharmony_ci memset(info->tmp_rbuf, 0, TESTFRAMESIZE); 49898c2ecf20Sopenharmony_ci 49908c2ecf20Sopenharmony_ci /* program hardware for HDLC and enabled receiver */ 49918c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 49928c2ecf20Sopenharmony_ci async_mode(info); 49938c2ecf20Sopenharmony_ci rx_start(info); 49948c2ecf20Sopenharmony_ci tx_load(info, buf, count); 49958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 49968c2ecf20Sopenharmony_ci 49978c2ecf20Sopenharmony_ci /* wait for receive complete */ 49988c2ecf20Sopenharmony_ci for (timeout = 100; timeout; --timeout) { 49998c2ecf20Sopenharmony_ci msleep_interruptible(10); 50008c2ecf20Sopenharmony_ci if (loopback_test_rx(info)) { 50018c2ecf20Sopenharmony_ci rc = 0; 50028c2ecf20Sopenharmony_ci break; 50038c2ecf20Sopenharmony_ci } 50048c2ecf20Sopenharmony_ci } 50058c2ecf20Sopenharmony_ci 50068c2ecf20Sopenharmony_ci /* verify received frame length and contents */ 50078c2ecf20Sopenharmony_ci if (!rc && (info->tmp_rbuf_count != count || 50088c2ecf20Sopenharmony_ci memcmp(buf, info->tmp_rbuf, count))) { 50098c2ecf20Sopenharmony_ci rc = -ENODEV; 50108c2ecf20Sopenharmony_ci } 50118c2ecf20Sopenharmony_ci 50128c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 50138c2ecf20Sopenharmony_ci reset_adapter(info); 50148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 50158c2ecf20Sopenharmony_ci 50168c2ecf20Sopenharmony_ci memcpy(&info->params, ¶ms, sizeof(info->params)); 50178c2ecf20Sopenharmony_ci info->port.tty = oldtty; 50188c2ecf20Sopenharmony_ci 50198c2ecf20Sopenharmony_ci info->init_error = rc ? DiagStatus_DmaFailure : 0; 50208c2ecf20Sopenharmony_ci return rc; 50218c2ecf20Sopenharmony_ci} 50228c2ecf20Sopenharmony_ci 50238c2ecf20Sopenharmony_cistatic int adapter_test(struct slgt_info *info) 50248c2ecf20Sopenharmony_ci{ 50258c2ecf20Sopenharmony_ci DBGINFO(("testing %s\n", info->device_name)); 50268c2ecf20Sopenharmony_ci if (register_test(info) < 0) { 50278c2ecf20Sopenharmony_ci printk("register test failure %s addr=%08X\n", 50288c2ecf20Sopenharmony_ci info->device_name, info->phys_reg_addr); 50298c2ecf20Sopenharmony_ci } else if (irq_test(info) < 0) { 50308c2ecf20Sopenharmony_ci printk("IRQ test failure %s IRQ=%d\n", 50318c2ecf20Sopenharmony_ci info->device_name, info->irq_level); 50328c2ecf20Sopenharmony_ci } else if (loopback_test(info) < 0) { 50338c2ecf20Sopenharmony_ci printk("loopback test failure %s\n", info->device_name); 50348c2ecf20Sopenharmony_ci } 50358c2ecf20Sopenharmony_ci return info->init_error; 50368c2ecf20Sopenharmony_ci} 50378c2ecf20Sopenharmony_ci 50388c2ecf20Sopenharmony_ci/* 50398c2ecf20Sopenharmony_ci * transmit timeout handler 50408c2ecf20Sopenharmony_ci */ 50418c2ecf20Sopenharmony_cistatic void tx_timeout(struct timer_list *t) 50428c2ecf20Sopenharmony_ci{ 50438c2ecf20Sopenharmony_ci struct slgt_info *info = from_timer(info, t, tx_timer); 50448c2ecf20Sopenharmony_ci unsigned long flags; 50458c2ecf20Sopenharmony_ci 50468c2ecf20Sopenharmony_ci DBGINFO(("%s tx_timeout\n", info->device_name)); 50478c2ecf20Sopenharmony_ci if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { 50488c2ecf20Sopenharmony_ci info->icount.txtimeout++; 50498c2ecf20Sopenharmony_ci } 50508c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 50518c2ecf20Sopenharmony_ci tx_stop(info); 50528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 50538c2ecf20Sopenharmony_ci 50548c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 50558c2ecf20Sopenharmony_ci if (info->netcount) 50568c2ecf20Sopenharmony_ci hdlcdev_tx_done(info); 50578c2ecf20Sopenharmony_ci else 50588c2ecf20Sopenharmony_ci#endif 50598c2ecf20Sopenharmony_ci bh_transmit(info); 50608c2ecf20Sopenharmony_ci} 50618c2ecf20Sopenharmony_ci 50628c2ecf20Sopenharmony_ci/* 50638c2ecf20Sopenharmony_ci * receive buffer polling timer 50648c2ecf20Sopenharmony_ci */ 50658c2ecf20Sopenharmony_cistatic void rx_timeout(struct timer_list *t) 50668c2ecf20Sopenharmony_ci{ 50678c2ecf20Sopenharmony_ci struct slgt_info *info = from_timer(info, t, rx_timer); 50688c2ecf20Sopenharmony_ci unsigned long flags; 50698c2ecf20Sopenharmony_ci 50708c2ecf20Sopenharmony_ci DBGINFO(("%s rx_timeout\n", info->device_name)); 50718c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 50728c2ecf20Sopenharmony_ci info->pending_bh |= BH_RECEIVE; 50738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 50748c2ecf20Sopenharmony_ci bh_handler(&info->task); 50758c2ecf20Sopenharmony_ci} 50768c2ecf20Sopenharmony_ci 5077