162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device driver for Microgate SyncLink GT serial adapters. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * written by Paul Fulghum for Microgate Corporation 662306a36Sopenharmony_ci * paulkf@microgate.com 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Microgate and SyncLink are trademarks of Microgate Corporation 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 1162306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1262306a36Sopenharmony_ci * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1362306a36Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 1462306a36Sopenharmony_ci * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1562306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 1662306a36Sopenharmony_ci * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1762306a36Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 1862306a36Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1962306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 2062306a36Sopenharmony_ci * OF THE POSSIBILITY OF SUCH DAMAGE. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * DEBUG OUTPUT DEFINITIONS 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * uncomment lines below to enable specific types of debug output 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * DBGINFO information - most verbose output 2962306a36Sopenharmony_ci * DBGERR serious errors 3062306a36Sopenharmony_ci * DBGBH bottom half service routine debugging 3162306a36Sopenharmony_ci * DBGISR interrupt service routine debugging 3262306a36Sopenharmony_ci * DBGDATA output receive and transmit data 3362306a36Sopenharmony_ci * DBGTBUF output transmit DMA buffers and registers 3462306a36Sopenharmony_ci * DBGRBUF output receive DMA buffers and registers 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt 3862306a36Sopenharmony_ci#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt 3962306a36Sopenharmony_ci#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt 4062306a36Sopenharmony_ci#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt 4162306a36Sopenharmony_ci#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label)) 4262306a36Sopenharmony_ci/*#define DBGTBUF(info) dump_tbufs(info)*/ 4362306a36Sopenharmony_ci/*#define DBGRBUF(info) dump_rbufs(info)*/ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include <linux/module.h> 4762306a36Sopenharmony_ci#include <linux/errno.h> 4862306a36Sopenharmony_ci#include <linux/signal.h> 4962306a36Sopenharmony_ci#include <linux/sched.h> 5062306a36Sopenharmony_ci#include <linux/timer.h> 5162306a36Sopenharmony_ci#include <linux/interrupt.h> 5262306a36Sopenharmony_ci#include <linux/pci.h> 5362306a36Sopenharmony_ci#include <linux/tty.h> 5462306a36Sopenharmony_ci#include <linux/tty_flip.h> 5562306a36Sopenharmony_ci#include <linux/serial.h> 5662306a36Sopenharmony_ci#include <linux/major.h> 5762306a36Sopenharmony_ci#include <linux/string.h> 5862306a36Sopenharmony_ci#include <linux/fcntl.h> 5962306a36Sopenharmony_ci#include <linux/ptrace.h> 6062306a36Sopenharmony_ci#include <linux/ioport.h> 6162306a36Sopenharmony_ci#include <linux/mm.h> 6262306a36Sopenharmony_ci#include <linux/seq_file.h> 6362306a36Sopenharmony_ci#include <linux/slab.h> 6462306a36Sopenharmony_ci#include <linux/netdevice.h> 6562306a36Sopenharmony_ci#include <linux/vmalloc.h> 6662306a36Sopenharmony_ci#include <linux/init.h> 6762306a36Sopenharmony_ci#include <linux/delay.h> 6862306a36Sopenharmony_ci#include <linux/ioctl.h> 6962306a36Sopenharmony_ci#include <linux/termios.h> 7062306a36Sopenharmony_ci#include <linux/bitops.h> 7162306a36Sopenharmony_ci#include <linux/workqueue.h> 7262306a36Sopenharmony_ci#include <linux/hdlc.h> 7362306a36Sopenharmony_ci#include <linux/synclink.h> 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#include <asm/io.h> 7662306a36Sopenharmony_ci#include <asm/irq.h> 7762306a36Sopenharmony_ci#include <asm/dma.h> 7862306a36Sopenharmony_ci#include <asm/types.h> 7962306a36Sopenharmony_ci#include <linux/uaccess.h> 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE)) 8262306a36Sopenharmony_ci#define SYNCLINK_GENERIC_HDLC 1 8362306a36Sopenharmony_ci#else 8462306a36Sopenharmony_ci#define SYNCLINK_GENERIC_HDLC 0 8562306a36Sopenharmony_ci#endif 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * module identification 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic const char driver_name[] = "SyncLink GT"; 9162306a36Sopenharmony_cistatic const char tty_dev_prefix[] = "ttySLG"; 9262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 9362306a36Sopenharmony_ci#define MAX_DEVICES 32 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic const struct pci_device_id pci_table[] = { 9662306a36Sopenharmony_ci { PCI_VDEVICE(MICROGATE, SYNCLINK_GT_DEVICE_ID) }, 9762306a36Sopenharmony_ci { PCI_VDEVICE(MICROGATE, SYNCLINK_GT2_DEVICE_ID) }, 9862306a36Sopenharmony_ci { PCI_VDEVICE(MICROGATE, SYNCLINK_GT4_DEVICE_ID) }, 9962306a36Sopenharmony_ci { PCI_VDEVICE(MICROGATE, SYNCLINK_AC_DEVICE_ID) }, 10062306a36Sopenharmony_ci { 0 }, /* terminate list */ 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_table); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int init_one(struct pci_dev *dev,const struct pci_device_id *ent); 10562306a36Sopenharmony_cistatic void remove_one(struct pci_dev *dev); 10662306a36Sopenharmony_cistatic struct pci_driver pci_driver = { 10762306a36Sopenharmony_ci .name = "synclink_gt", 10862306a36Sopenharmony_ci .id_table = pci_table, 10962306a36Sopenharmony_ci .probe = init_one, 11062306a36Sopenharmony_ci .remove = remove_one, 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic bool pci_registered; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * module configuration and status 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic struct slgt_info *slgt_device_list; 11962306a36Sopenharmony_cistatic int slgt_device_count; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int ttymajor; 12262306a36Sopenharmony_cistatic int debug_level; 12362306a36Sopenharmony_cistatic int maxframe[MAX_DEVICES]; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cimodule_param(ttymajor, int, 0); 12662306a36Sopenharmony_cimodule_param(debug_level, int, 0); 12762306a36Sopenharmony_cimodule_param_array(maxframe, int, NULL, 0); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciMODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned"); 13062306a36Sopenharmony_ciMODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail"); 13162306a36Sopenharmony_ciMODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)"); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* 13462306a36Sopenharmony_ci * tty support and callbacks 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_cistatic struct tty_driver *serial_driver; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void wait_until_sent(struct tty_struct *tty, int timeout); 13962306a36Sopenharmony_cistatic void flush_buffer(struct tty_struct *tty); 14062306a36Sopenharmony_cistatic void tx_release(struct tty_struct *tty); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * generic HDLC support 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci#define dev_to_port(D) (dev_to_hdlc(D)->priv) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * device specific structures, macros and functions 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define SLGT_MAX_PORTS 4 15362306a36Sopenharmony_ci#define SLGT_REG_SIZE 256 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* 15662306a36Sopenharmony_ci * conditional wait facility 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistruct cond_wait { 15962306a36Sopenharmony_ci struct cond_wait *next; 16062306a36Sopenharmony_ci wait_queue_head_t q; 16162306a36Sopenharmony_ci wait_queue_entry_t wait; 16262306a36Sopenharmony_ci unsigned int data; 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_cistatic void flush_cond_wait(struct cond_wait **head); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* 16762306a36Sopenharmony_ci * DMA buffer descriptor and access macros 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistruct slgt_desc 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci __le16 count; 17262306a36Sopenharmony_ci __le16 status; 17362306a36Sopenharmony_ci __le32 pbuf; /* physical address of data buffer */ 17462306a36Sopenharmony_ci __le32 next; /* physical address of next descriptor */ 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* driver book keeping */ 17762306a36Sopenharmony_ci char *buf; /* virtual address of data buffer */ 17862306a36Sopenharmony_ci unsigned int pdesc; /* physical address of this descriptor */ 17962306a36Sopenharmony_ci dma_addr_t buf_dma_addr; 18062306a36Sopenharmony_ci unsigned short buf_count; 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b)) 18462306a36Sopenharmony_ci#define set_desc_next(a,b) (a).next = cpu_to_le32((unsigned int)(b)) 18562306a36Sopenharmony_ci#define set_desc_count(a,b)(a).count = cpu_to_le16((unsigned short)(b)) 18662306a36Sopenharmony_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)) 18762306a36Sopenharmony_ci#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b)) 18862306a36Sopenharmony_ci#define desc_count(a) (le16_to_cpu((a).count)) 18962306a36Sopenharmony_ci#define desc_status(a) (le16_to_cpu((a).status)) 19062306a36Sopenharmony_ci#define desc_complete(a) (le16_to_cpu((a).status) & BIT15) 19162306a36Sopenharmony_ci#define desc_eof(a) (le16_to_cpu((a).status) & BIT2) 19262306a36Sopenharmony_ci#define desc_crc_error(a) (le16_to_cpu((a).status) & BIT1) 19362306a36Sopenharmony_ci#define desc_abort(a) (le16_to_cpu((a).status) & BIT0) 19462306a36Sopenharmony_ci#define desc_residue(a) ((le16_to_cpu((a).status) & 0x38) >> 3) 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistruct _input_signal_events { 19762306a36Sopenharmony_ci int ri_up; 19862306a36Sopenharmony_ci int ri_down; 19962306a36Sopenharmony_ci int dsr_up; 20062306a36Sopenharmony_ci int dsr_down; 20162306a36Sopenharmony_ci int dcd_up; 20262306a36Sopenharmony_ci int dcd_down; 20362306a36Sopenharmony_ci int cts_up; 20462306a36Sopenharmony_ci int cts_down; 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * device instance data structure 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_cistruct slgt_info { 21162306a36Sopenharmony_ci void *if_ptr; /* General purpose pointer (used by SPPP) */ 21262306a36Sopenharmony_ci struct tty_port port; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci struct slgt_info *next_device; /* device list link */ 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci char device_name[25]; 21762306a36Sopenharmony_ci struct pci_dev *pdev; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci int port_count; /* count of ports on adapter */ 22062306a36Sopenharmony_ci int adapter_num; /* adapter instance number */ 22162306a36Sopenharmony_ci int port_num; /* port instance number */ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* array of pointers to port contexts on this adapter */ 22462306a36Sopenharmony_ci struct slgt_info *port_array[SLGT_MAX_PORTS]; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci int line; /* tty line instance number */ 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci struct mgsl_icount icount; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci int timeout; 23162306a36Sopenharmony_ci int x_char; /* xon/xoff character */ 23262306a36Sopenharmony_ci unsigned int read_status_mask; 23362306a36Sopenharmony_ci unsigned int ignore_status_mask; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci wait_queue_head_t status_event_wait_q; 23662306a36Sopenharmony_ci wait_queue_head_t event_wait_q; 23762306a36Sopenharmony_ci struct timer_list tx_timer; 23862306a36Sopenharmony_ci struct timer_list rx_timer; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci unsigned int gpio_present; 24162306a36Sopenharmony_ci struct cond_wait *gpio_wait_q; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci spinlock_t lock; /* spinlock for synchronizing with ISR */ 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci struct work_struct task; 24662306a36Sopenharmony_ci u32 pending_bh; 24762306a36Sopenharmony_ci bool bh_requested; 24862306a36Sopenharmony_ci bool bh_running; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci int isr_overflow; 25162306a36Sopenharmony_ci bool irq_requested; /* true if IRQ requested */ 25262306a36Sopenharmony_ci bool irq_occurred; /* for diagnostics use */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* device configuration */ 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci unsigned int bus_type; 25762306a36Sopenharmony_ci unsigned int irq_level; 25862306a36Sopenharmony_ci unsigned long irq_flags; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci unsigned char __iomem * reg_addr; /* memory mapped registers address */ 26162306a36Sopenharmony_ci u32 phys_reg_addr; 26262306a36Sopenharmony_ci bool reg_addr_requested; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci MGSL_PARAMS params; /* communications parameters */ 26562306a36Sopenharmony_ci u32 idle_mode; 26662306a36Sopenharmony_ci u32 max_frame_size; /* as set by device config */ 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci unsigned int rbuf_fill_level; 26962306a36Sopenharmony_ci unsigned int rx_pio; 27062306a36Sopenharmony_ci unsigned int if_mode; 27162306a36Sopenharmony_ci unsigned int base_clock; 27262306a36Sopenharmony_ci unsigned int xsync; 27362306a36Sopenharmony_ci unsigned int xctrl; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* device status */ 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci bool rx_enabled; 27862306a36Sopenharmony_ci bool rx_restart; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci bool tx_enabled; 28162306a36Sopenharmony_ci bool tx_active; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci unsigned char signals; /* serial signal states */ 28462306a36Sopenharmony_ci int init_error; /* initialization error */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci unsigned char *tx_buf; 28762306a36Sopenharmony_ci int tx_count; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci bool drop_rts_on_tx_done; 29062306a36Sopenharmony_ci struct _input_signal_events input_signal_events; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci int dcd_chkcount; /* check counts to prevent */ 29362306a36Sopenharmony_ci int cts_chkcount; /* too many IRQs if a signal */ 29462306a36Sopenharmony_ci int dsr_chkcount; /* is floating */ 29562306a36Sopenharmony_ci int ri_chkcount; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci char *bufs; /* virtual address of DMA buffer lists */ 29862306a36Sopenharmony_ci dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */ 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci unsigned int rbuf_count; 30162306a36Sopenharmony_ci struct slgt_desc *rbufs; 30262306a36Sopenharmony_ci unsigned int rbuf_current; 30362306a36Sopenharmony_ci unsigned int rbuf_index; 30462306a36Sopenharmony_ci unsigned int rbuf_fill_index; 30562306a36Sopenharmony_ci unsigned short rbuf_fill_count; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci unsigned int tbuf_count; 30862306a36Sopenharmony_ci struct slgt_desc *tbufs; 30962306a36Sopenharmony_ci unsigned int tbuf_current; 31062306a36Sopenharmony_ci unsigned int tbuf_start; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci unsigned char *tmp_rbuf; 31362306a36Sopenharmony_ci unsigned int tmp_rbuf_count; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* SPPP/Cisco HDLC device parts */ 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci int netcount; 31862306a36Sopenharmony_ci spinlock_t netlock; 31962306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 32062306a36Sopenharmony_ci struct net_device *netdev; 32162306a36Sopenharmony_ci#endif 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci}; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic const MGSL_PARAMS default_params = { 32662306a36Sopenharmony_ci .mode = MGSL_MODE_HDLC, 32762306a36Sopenharmony_ci .loopback = 0, 32862306a36Sopenharmony_ci .flags = HDLC_FLAG_UNDERRUN_ABORT15, 32962306a36Sopenharmony_ci .encoding = HDLC_ENCODING_NRZI_SPACE, 33062306a36Sopenharmony_ci .clock_speed = 0, 33162306a36Sopenharmony_ci .addr_filter = 0xff, 33262306a36Sopenharmony_ci .crc_type = HDLC_CRC_16_CCITT, 33362306a36Sopenharmony_ci .preamble_length = HDLC_PREAMBLE_LENGTH_8BITS, 33462306a36Sopenharmony_ci .preamble = HDLC_PREAMBLE_PATTERN_NONE, 33562306a36Sopenharmony_ci .data_rate = 9600, 33662306a36Sopenharmony_ci .data_bits = 8, 33762306a36Sopenharmony_ci .stop_bits = 1, 33862306a36Sopenharmony_ci .parity = ASYNC_PARITY_NONE 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci#define BH_RECEIVE 1 34362306a36Sopenharmony_ci#define BH_TRANSMIT 2 34462306a36Sopenharmony_ci#define BH_STATUS 4 34562306a36Sopenharmony_ci#define IO_PIN_SHUTDOWN_LIMIT 100 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci#define DMABUFSIZE 256 34862306a36Sopenharmony_ci#define DESC_LIST_SIZE 4096 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci#define MASK_PARITY BIT1 35162306a36Sopenharmony_ci#define MASK_FRAMING BIT0 35262306a36Sopenharmony_ci#define MASK_BREAK BIT14 35362306a36Sopenharmony_ci#define MASK_OVERRUN BIT4 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci#define GSR 0x00 /* global status */ 35662306a36Sopenharmony_ci#define JCR 0x04 /* JTAG control */ 35762306a36Sopenharmony_ci#define IODR 0x08 /* GPIO direction */ 35862306a36Sopenharmony_ci#define IOER 0x0c /* GPIO interrupt enable */ 35962306a36Sopenharmony_ci#define IOVR 0x10 /* GPIO value */ 36062306a36Sopenharmony_ci#define IOSR 0x14 /* GPIO interrupt status */ 36162306a36Sopenharmony_ci#define TDR 0x80 /* tx data */ 36262306a36Sopenharmony_ci#define RDR 0x80 /* rx data */ 36362306a36Sopenharmony_ci#define TCR 0x82 /* tx control */ 36462306a36Sopenharmony_ci#define TIR 0x84 /* tx idle */ 36562306a36Sopenharmony_ci#define TPR 0x85 /* tx preamble */ 36662306a36Sopenharmony_ci#define RCR 0x86 /* rx control */ 36762306a36Sopenharmony_ci#define VCR 0x88 /* V.24 control */ 36862306a36Sopenharmony_ci#define CCR 0x89 /* clock control */ 36962306a36Sopenharmony_ci#define BDR 0x8a /* baud divisor */ 37062306a36Sopenharmony_ci#define SCR 0x8c /* serial control */ 37162306a36Sopenharmony_ci#define SSR 0x8e /* serial status */ 37262306a36Sopenharmony_ci#define RDCSR 0x90 /* rx DMA control/status */ 37362306a36Sopenharmony_ci#define TDCSR 0x94 /* tx DMA control/status */ 37462306a36Sopenharmony_ci#define RDDAR 0x98 /* rx DMA descriptor address */ 37562306a36Sopenharmony_ci#define TDDAR 0x9c /* tx DMA descriptor address */ 37662306a36Sopenharmony_ci#define XSR 0x40 /* extended sync pattern */ 37762306a36Sopenharmony_ci#define XCR 0x44 /* extended control */ 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci#define RXIDLE BIT14 38062306a36Sopenharmony_ci#define RXBREAK BIT14 38162306a36Sopenharmony_ci#define IRQ_TXDATA BIT13 38262306a36Sopenharmony_ci#define IRQ_TXIDLE BIT12 38362306a36Sopenharmony_ci#define IRQ_TXUNDER BIT11 /* HDLC */ 38462306a36Sopenharmony_ci#define IRQ_RXDATA BIT10 38562306a36Sopenharmony_ci#define IRQ_RXIDLE BIT9 /* HDLC */ 38662306a36Sopenharmony_ci#define IRQ_RXBREAK BIT9 /* async */ 38762306a36Sopenharmony_ci#define IRQ_RXOVER BIT8 38862306a36Sopenharmony_ci#define IRQ_DSR BIT7 38962306a36Sopenharmony_ci#define IRQ_CTS BIT6 39062306a36Sopenharmony_ci#define IRQ_DCD BIT5 39162306a36Sopenharmony_ci#define IRQ_RI BIT4 39262306a36Sopenharmony_ci#define IRQ_ALL 0x3ff0 39362306a36Sopenharmony_ci#define IRQ_MASTER BIT0 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci#define slgt_irq_on(info, mask) \ 39662306a36Sopenharmony_ci wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask))) 39762306a36Sopenharmony_ci#define slgt_irq_off(info, mask) \ 39862306a36Sopenharmony_ci wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask))) 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic __u8 rd_reg8(struct slgt_info *info, unsigned int addr); 40162306a36Sopenharmony_cistatic void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value); 40262306a36Sopenharmony_cistatic __u16 rd_reg16(struct slgt_info *info, unsigned int addr); 40362306a36Sopenharmony_cistatic void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value); 40462306a36Sopenharmony_cistatic __u32 rd_reg32(struct slgt_info *info, unsigned int addr); 40562306a36Sopenharmony_cistatic void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void msc_set_vcr(struct slgt_info *info); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int startup(struct slgt_info *info); 41062306a36Sopenharmony_cistatic int block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info); 41162306a36Sopenharmony_cistatic void shutdown(struct slgt_info *info); 41262306a36Sopenharmony_cistatic void program_hw(struct slgt_info *info); 41362306a36Sopenharmony_cistatic void change_params(struct slgt_info *info); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int adapter_test(struct slgt_info *info); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void reset_port(struct slgt_info *info); 41862306a36Sopenharmony_cistatic void async_mode(struct slgt_info *info); 41962306a36Sopenharmony_cistatic void sync_mode(struct slgt_info *info); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void rx_stop(struct slgt_info *info); 42262306a36Sopenharmony_cistatic void rx_start(struct slgt_info *info); 42362306a36Sopenharmony_cistatic void reset_rbufs(struct slgt_info *info); 42462306a36Sopenharmony_cistatic void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last); 42562306a36Sopenharmony_cistatic bool rx_get_frame(struct slgt_info *info); 42662306a36Sopenharmony_cistatic bool rx_get_buf(struct slgt_info *info); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void tx_start(struct slgt_info *info); 42962306a36Sopenharmony_cistatic void tx_stop(struct slgt_info *info); 43062306a36Sopenharmony_cistatic void tx_set_idle(struct slgt_info *info); 43162306a36Sopenharmony_cistatic unsigned int tbuf_bytes(struct slgt_info *info); 43262306a36Sopenharmony_cistatic void reset_tbufs(struct slgt_info *info); 43362306a36Sopenharmony_cistatic void tdma_reset(struct slgt_info *info); 43462306a36Sopenharmony_cistatic bool tx_load(struct slgt_info *info, const u8 *buf, unsigned int count); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void get_gtsignals(struct slgt_info *info); 43762306a36Sopenharmony_cistatic void set_gtsignals(struct slgt_info *info); 43862306a36Sopenharmony_cistatic void set_rate(struct slgt_info *info, u32 data_rate); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void bh_transmit(struct slgt_info *info); 44162306a36Sopenharmony_cistatic void isr_txeom(struct slgt_info *info, unsigned short status); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic void tx_timeout(struct timer_list *t); 44462306a36Sopenharmony_cistatic void rx_timeout(struct timer_list *t); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* 44762306a36Sopenharmony_ci * ioctl handlers 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_cistatic int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount); 45062306a36Sopenharmony_cistatic int get_params(struct slgt_info *info, MGSL_PARAMS __user *params); 45162306a36Sopenharmony_cistatic int set_params(struct slgt_info *info, MGSL_PARAMS __user *params); 45262306a36Sopenharmony_cistatic int get_txidle(struct slgt_info *info, int __user *idle_mode); 45362306a36Sopenharmony_cistatic int set_txidle(struct slgt_info *info, int idle_mode); 45462306a36Sopenharmony_cistatic int tx_enable(struct slgt_info *info, int enable); 45562306a36Sopenharmony_cistatic int tx_abort(struct slgt_info *info); 45662306a36Sopenharmony_cistatic int rx_enable(struct slgt_info *info, int enable); 45762306a36Sopenharmony_cistatic int modem_input_wait(struct slgt_info *info,int arg); 45862306a36Sopenharmony_cistatic int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); 45962306a36Sopenharmony_cistatic int get_interface(struct slgt_info *info, int __user *if_mode); 46062306a36Sopenharmony_cistatic int set_interface(struct slgt_info *info, int if_mode); 46162306a36Sopenharmony_cistatic int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); 46262306a36Sopenharmony_cistatic int get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); 46362306a36Sopenharmony_cistatic int wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); 46462306a36Sopenharmony_cistatic int get_xsync(struct slgt_info *info, int __user *if_mode); 46562306a36Sopenharmony_cistatic int set_xsync(struct slgt_info *info, int if_mode); 46662306a36Sopenharmony_cistatic int get_xctrl(struct slgt_info *info, int __user *if_mode); 46762306a36Sopenharmony_cistatic int set_xctrl(struct slgt_info *info, int if_mode); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci/* 47062306a36Sopenharmony_ci * driver functions 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_cistatic void release_resources(struct slgt_info *info); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/* 47562306a36Sopenharmony_ci * DEBUG OUTPUT CODE 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci#ifndef DBGINFO 47862306a36Sopenharmony_ci#define DBGINFO(fmt) 47962306a36Sopenharmony_ci#endif 48062306a36Sopenharmony_ci#ifndef DBGERR 48162306a36Sopenharmony_ci#define DBGERR(fmt) 48262306a36Sopenharmony_ci#endif 48362306a36Sopenharmony_ci#ifndef DBGBH 48462306a36Sopenharmony_ci#define DBGBH(fmt) 48562306a36Sopenharmony_ci#endif 48662306a36Sopenharmony_ci#ifndef DBGISR 48762306a36Sopenharmony_ci#define DBGISR(fmt) 48862306a36Sopenharmony_ci#endif 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci#ifdef DBGDATA 49162306a36Sopenharmony_cistatic void trace_block(struct slgt_info *info, const char *data, int count, const char *label) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci int i; 49462306a36Sopenharmony_ci int linecount; 49562306a36Sopenharmony_ci printk("%s %s data:\n",info->device_name, label); 49662306a36Sopenharmony_ci while(count) { 49762306a36Sopenharmony_ci linecount = (count > 16) ? 16 : count; 49862306a36Sopenharmony_ci for(i=0; i < linecount; i++) 49962306a36Sopenharmony_ci printk("%02X ",(unsigned char)data[i]); 50062306a36Sopenharmony_ci for(;i<17;i++) 50162306a36Sopenharmony_ci printk(" "); 50262306a36Sopenharmony_ci for(i=0;i<linecount;i++) { 50362306a36Sopenharmony_ci if (data[i]>=040 && data[i]<=0176) 50462306a36Sopenharmony_ci printk("%c",data[i]); 50562306a36Sopenharmony_ci else 50662306a36Sopenharmony_ci printk("."); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci printk("\n"); 50962306a36Sopenharmony_ci data += linecount; 51062306a36Sopenharmony_ci count -= linecount; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci#else 51462306a36Sopenharmony_ci#define DBGDATA(info, buf, size, label) 51562306a36Sopenharmony_ci#endif 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci#ifdef DBGTBUF 51862306a36Sopenharmony_cistatic void dump_tbufs(struct slgt_info *info) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci int i; 52162306a36Sopenharmony_ci printk("tbuf_current=%d\n", info->tbuf_current); 52262306a36Sopenharmony_ci for (i=0 ; i < info->tbuf_count ; i++) { 52362306a36Sopenharmony_ci printk("%d: count=%04X status=%04X\n", 52462306a36Sopenharmony_ci i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status)); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci#else 52862306a36Sopenharmony_ci#define DBGTBUF(info) 52962306a36Sopenharmony_ci#endif 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci#ifdef DBGRBUF 53262306a36Sopenharmony_cistatic void dump_rbufs(struct slgt_info *info) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci int i; 53562306a36Sopenharmony_ci printk("rbuf_current=%d\n", info->rbuf_current); 53662306a36Sopenharmony_ci for (i=0 ; i < info->rbuf_count ; i++) { 53762306a36Sopenharmony_ci printk("%d: count=%04X status=%04X\n", 53862306a36Sopenharmony_ci i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status)); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci#else 54262306a36Sopenharmony_ci#define DBGRBUF(info) 54362306a36Sopenharmony_ci#endif 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic inline int sanity_check(struct slgt_info *info, char *devname, const char *name) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci#ifdef SANITY_CHECK 54862306a36Sopenharmony_ci if (!info) { 54962306a36Sopenharmony_ci printk("null struct slgt_info for (%s) in %s\n", devname, name); 55062306a36Sopenharmony_ci return 1; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci#else 55362306a36Sopenharmony_ci if (!info) 55462306a36Sopenharmony_ci return 1; 55562306a36Sopenharmony_ci#endif 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci/* 56062306a36Sopenharmony_ci * line discipline callback wrappers 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * The wrappers maintain line discipline references 56362306a36Sopenharmony_ci * while calling into the line discipline. 56462306a36Sopenharmony_ci * 56562306a36Sopenharmony_ci * ldisc_receive_buf - pass receive data to line discipline 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_cistatic void ldisc_receive_buf(struct tty_struct *tty, 56862306a36Sopenharmony_ci const __u8 *data, char *flags, int count) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct tty_ldisc *ld; 57162306a36Sopenharmony_ci if (!tty) 57262306a36Sopenharmony_ci return; 57362306a36Sopenharmony_ci ld = tty_ldisc_ref(tty); 57462306a36Sopenharmony_ci if (ld) { 57562306a36Sopenharmony_ci if (ld->ops->receive_buf) 57662306a36Sopenharmony_ci ld->ops->receive_buf(tty, data, flags, count); 57762306a36Sopenharmony_ci tty_ldisc_deref(ld); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci/* tty callbacks */ 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic int open(struct tty_struct *tty, struct file *filp) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct slgt_info *info; 58662306a36Sopenharmony_ci int retval, line; 58762306a36Sopenharmony_ci unsigned long flags; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci line = tty->index; 59062306a36Sopenharmony_ci if (line >= slgt_device_count) { 59162306a36Sopenharmony_ci DBGERR(("%s: open with invalid line #%d.\n", driver_name, line)); 59262306a36Sopenharmony_ci return -ENODEV; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci info = slgt_device_list; 59662306a36Sopenharmony_ci while(info && info->line != line) 59762306a36Sopenharmony_ci info = info->next_device; 59862306a36Sopenharmony_ci if (sanity_check(info, tty->name, "open")) 59962306a36Sopenharmony_ci return -ENODEV; 60062306a36Sopenharmony_ci if (info->init_error) { 60162306a36Sopenharmony_ci DBGERR(("%s init error=%d\n", info->device_name, info->init_error)); 60262306a36Sopenharmony_ci return -ENODEV; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci tty->driver_data = info; 60662306a36Sopenharmony_ci info->port.tty = tty; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count)); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci mutex_lock(&info->port.mutex); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 61362306a36Sopenharmony_ci if (info->netcount) { 61462306a36Sopenharmony_ci retval = -EBUSY; 61562306a36Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 61662306a36Sopenharmony_ci mutex_unlock(&info->port.mutex); 61762306a36Sopenharmony_ci goto cleanup; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci info->port.count++; 62062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (info->port.count == 1) { 62362306a36Sopenharmony_ci /* 1st open on this device, init hardware */ 62462306a36Sopenharmony_ci retval = startup(info); 62562306a36Sopenharmony_ci if (retval < 0) { 62662306a36Sopenharmony_ci mutex_unlock(&info->port.mutex); 62762306a36Sopenharmony_ci goto cleanup; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci mutex_unlock(&info->port.mutex); 63162306a36Sopenharmony_ci retval = block_til_ready(tty, filp, info); 63262306a36Sopenharmony_ci if (retval) { 63362306a36Sopenharmony_ci DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval)); 63462306a36Sopenharmony_ci goto cleanup; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci retval = 0; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cicleanup: 64062306a36Sopenharmony_ci if (retval) { 64162306a36Sopenharmony_ci if (tty->count == 1) 64262306a36Sopenharmony_ci info->port.tty = NULL; /* tty layer will release tty struct */ 64362306a36Sopenharmony_ci if(info->port.count) 64462306a36Sopenharmony_ci info->port.count--; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci DBGINFO(("%s open rc=%d\n", info->device_name, retval)); 64862306a36Sopenharmony_ci return retval; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic void close(struct tty_struct *tty, struct file *filp) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (sanity_check(info, tty->name, "close")) 65662306a36Sopenharmony_ci return; 65762306a36Sopenharmony_ci DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count)); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (tty_port_close_start(&info->port, tty, filp) == 0) 66062306a36Sopenharmony_ci goto cleanup; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci mutex_lock(&info->port.mutex); 66362306a36Sopenharmony_ci if (tty_port_initialized(&info->port)) 66462306a36Sopenharmony_ci wait_until_sent(tty, info->timeout); 66562306a36Sopenharmony_ci flush_buffer(tty); 66662306a36Sopenharmony_ci tty_ldisc_flush(tty); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci shutdown(info); 66962306a36Sopenharmony_ci mutex_unlock(&info->port.mutex); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci tty_port_close_end(&info->port, tty); 67262306a36Sopenharmony_ci info->port.tty = NULL; 67362306a36Sopenharmony_cicleanup: 67462306a36Sopenharmony_ci DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count)); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic void hangup(struct tty_struct *tty) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 68062306a36Sopenharmony_ci unsigned long flags; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (sanity_check(info, tty->name, "hangup")) 68362306a36Sopenharmony_ci return; 68462306a36Sopenharmony_ci DBGINFO(("%s hangup\n", info->device_name)); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci flush_buffer(tty); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci mutex_lock(&info->port.mutex); 68962306a36Sopenharmony_ci shutdown(info); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci spin_lock_irqsave(&info->port.lock, flags); 69262306a36Sopenharmony_ci info->port.count = 0; 69362306a36Sopenharmony_ci info->port.tty = NULL; 69462306a36Sopenharmony_ci spin_unlock_irqrestore(&info->port.lock, flags); 69562306a36Sopenharmony_ci tty_port_set_active(&info->port, false); 69662306a36Sopenharmony_ci mutex_unlock(&info->port.mutex); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci wake_up_interruptible(&info->port.open_wait); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic void set_termios(struct tty_struct *tty, 70262306a36Sopenharmony_ci const struct ktermios *old_termios) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 70562306a36Sopenharmony_ci unsigned long flags; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci DBGINFO(("%s set_termios\n", tty->driver->name)); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci change_params(info); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Handle transition to B0 status */ 71262306a36Sopenharmony_ci if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { 71362306a36Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 71462306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 71562306a36Sopenharmony_ci set_gtsignals(info); 71662306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* Handle transition away from B0 status */ 72062306a36Sopenharmony_ci if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { 72162306a36Sopenharmony_ci info->signals |= SerialSignal_DTR; 72262306a36Sopenharmony_ci if (!C_CRTSCTS(tty) || !tty_throttled(tty)) 72362306a36Sopenharmony_ci info->signals |= SerialSignal_RTS; 72462306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 72562306a36Sopenharmony_ci set_gtsignals(info); 72662306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Handle turning off CRTSCTS */ 73062306a36Sopenharmony_ci if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { 73162306a36Sopenharmony_ci tty->hw_stopped = false; 73262306a36Sopenharmony_ci tx_release(tty); 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic void update_tx_timer(struct slgt_info *info) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci /* 73962306a36Sopenharmony_ci * use worst case speed of 1200bps to calculate transmit timeout 74062306a36Sopenharmony_ci * based on data in buffers (tbuf_bytes) and FIFO (128 bytes) 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci if (info->params.mode == MGSL_MODE_HDLC) { 74362306a36Sopenharmony_ci int timeout = (tbuf_bytes(info) * 7) + 1000; 74462306a36Sopenharmony_ci mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout)); 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic ssize_t write(struct tty_struct *tty, const u8 *buf, size_t count) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci int ret = 0; 75162306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 75262306a36Sopenharmony_ci unsigned long flags; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (sanity_check(info, tty->name, "write")) 75562306a36Sopenharmony_ci return -EIO; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci DBGINFO(("%s write count=%zu\n", info->device_name, count)); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (!info->tx_buf || (count > info->max_frame_size)) 76062306a36Sopenharmony_ci return -EIO; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (!count || tty->flow.stopped || tty->hw_stopped) 76362306a36Sopenharmony_ci return 0; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (info->tx_count) { 76862306a36Sopenharmony_ci /* send accumulated data from send_char() */ 76962306a36Sopenharmony_ci if (!tx_load(info, info->tx_buf, info->tx_count)) 77062306a36Sopenharmony_ci goto cleanup; 77162306a36Sopenharmony_ci info->tx_count = 0; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (tx_load(info, buf, count)) 77562306a36Sopenharmony_ci ret = count; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cicleanup: 77862306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 77962306a36Sopenharmony_ci DBGINFO(("%s write rc=%d\n", info->device_name, ret)); 78062306a36Sopenharmony_ci return ret; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int put_char(struct tty_struct *tty, u8 ch) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 78662306a36Sopenharmony_ci unsigned long flags; 78762306a36Sopenharmony_ci int ret = 0; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (sanity_check(info, tty->name, "put_char")) 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci DBGINFO(("%s put_char(%u)\n", info->device_name, ch)); 79262306a36Sopenharmony_ci if (!info->tx_buf) 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 79562306a36Sopenharmony_ci if (info->tx_count < info->max_frame_size) { 79662306a36Sopenharmony_ci info->tx_buf[info->tx_count++] = ch; 79762306a36Sopenharmony_ci ret = 1; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 80062306a36Sopenharmony_ci return ret; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void send_xchar(struct tty_struct *tty, char ch) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 80662306a36Sopenharmony_ci unsigned long flags; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (sanity_check(info, tty->name, "send_xchar")) 80962306a36Sopenharmony_ci return; 81062306a36Sopenharmony_ci DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch)); 81162306a36Sopenharmony_ci info->x_char = ch; 81262306a36Sopenharmony_ci if (ch) { 81362306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 81462306a36Sopenharmony_ci if (!info->tx_enabled) 81562306a36Sopenharmony_ci tx_start(info); 81662306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic void wait_until_sent(struct tty_struct *tty, int timeout) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 82362306a36Sopenharmony_ci unsigned long orig_jiffies, char_time; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (!info ) 82662306a36Sopenharmony_ci return; 82762306a36Sopenharmony_ci if (sanity_check(info, tty->name, "wait_until_sent")) 82862306a36Sopenharmony_ci return; 82962306a36Sopenharmony_ci DBGINFO(("%s wait_until_sent entry\n", info->device_name)); 83062306a36Sopenharmony_ci if (!tty_port_initialized(&info->port)) 83162306a36Sopenharmony_ci goto exit; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci orig_jiffies = jiffies; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Set check interval to 1/5 of estimated time to 83662306a36Sopenharmony_ci * send a character, and make it at least 1. The check 83762306a36Sopenharmony_ci * interval should also be less than the timeout. 83862306a36Sopenharmony_ci * Note: use tight timings here to satisfy the NIST-PCTS. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (info->params.data_rate) { 84262306a36Sopenharmony_ci char_time = info->timeout/(32 * 5); 84362306a36Sopenharmony_ci if (!char_time) 84462306a36Sopenharmony_ci char_time++; 84562306a36Sopenharmony_ci } else 84662306a36Sopenharmony_ci char_time = 1; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (timeout) 84962306a36Sopenharmony_ci char_time = min_t(unsigned long, char_time, timeout); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci while (info->tx_active) { 85262306a36Sopenharmony_ci msleep_interruptible(jiffies_to_msecs(char_time)); 85362306a36Sopenharmony_ci if (signal_pending(current)) 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci if (timeout && time_after(jiffies, orig_jiffies + timeout)) 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ciexit: 85962306a36Sopenharmony_ci DBGINFO(("%s wait_until_sent exit\n", info->device_name)); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic unsigned int write_room(struct tty_struct *tty) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 86562306a36Sopenharmony_ci unsigned int ret; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (sanity_check(info, tty->name, "write_room")) 86862306a36Sopenharmony_ci return 0; 86962306a36Sopenharmony_ci ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE; 87062306a36Sopenharmony_ci DBGINFO(("%s write_room=%u\n", info->device_name, ret)); 87162306a36Sopenharmony_ci return ret; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic void flush_chars(struct tty_struct *tty) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 87762306a36Sopenharmony_ci unsigned long flags; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (sanity_check(info, tty->name, "flush_chars")) 88062306a36Sopenharmony_ci return; 88162306a36Sopenharmony_ci DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count)); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (info->tx_count <= 0 || tty->flow.stopped || 88462306a36Sopenharmony_ci tty->hw_stopped || !info->tx_buf) 88562306a36Sopenharmony_ci return; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci DBGINFO(("%s flush_chars start transmit\n", info->device_name)); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 89062306a36Sopenharmony_ci if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) 89162306a36Sopenharmony_ci info->tx_count = 0; 89262306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void flush_buffer(struct tty_struct *tty) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 89862306a36Sopenharmony_ci unsigned long flags; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (sanity_check(info, tty->name, "flush_buffer")) 90162306a36Sopenharmony_ci return; 90262306a36Sopenharmony_ci DBGINFO(("%s flush_buffer\n", info->device_name)); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 90562306a36Sopenharmony_ci info->tx_count = 0; 90662306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci tty_wakeup(tty); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci/* 91262306a36Sopenharmony_ci * throttle (stop) transmitter 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_cistatic void tx_hold(struct tty_struct *tty) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 91762306a36Sopenharmony_ci unsigned long flags; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (sanity_check(info, tty->name, "tx_hold")) 92062306a36Sopenharmony_ci return; 92162306a36Sopenharmony_ci DBGINFO(("%s tx_hold\n", info->device_name)); 92262306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 92362306a36Sopenharmony_ci if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC) 92462306a36Sopenharmony_ci tx_stop(info); 92562306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/* 92962306a36Sopenharmony_ci * release (start) transmitter 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_cistatic void tx_release(struct tty_struct *tty) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 93462306a36Sopenharmony_ci unsigned long flags; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (sanity_check(info, tty->name, "tx_release")) 93762306a36Sopenharmony_ci return; 93862306a36Sopenharmony_ci DBGINFO(("%s tx_release\n", info->device_name)); 93962306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 94062306a36Sopenharmony_ci if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) 94162306a36Sopenharmony_ci info->tx_count = 0; 94262306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci/* 94662306a36Sopenharmony_ci * Service an IOCTL request 94762306a36Sopenharmony_ci * 94862306a36Sopenharmony_ci * Arguments 94962306a36Sopenharmony_ci * 95062306a36Sopenharmony_ci * tty pointer to tty instance data 95162306a36Sopenharmony_ci * cmd IOCTL command code 95262306a36Sopenharmony_ci * arg command argument/context 95362306a36Sopenharmony_ci * 95462306a36Sopenharmony_ci * Return 0 if success, otherwise error code 95562306a36Sopenharmony_ci */ 95662306a36Sopenharmony_cistatic int ioctl(struct tty_struct *tty, 95762306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 96062306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 96162306a36Sopenharmony_ci int ret; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (sanity_check(info, tty->name, "ioctl")) 96462306a36Sopenharmony_ci return -ENODEV; 96562306a36Sopenharmony_ci DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd)); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (cmd != TIOCMIWAIT) { 96862306a36Sopenharmony_ci if (tty_io_error(tty)) 96962306a36Sopenharmony_ci return -EIO; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci switch (cmd) { 97362306a36Sopenharmony_ci case MGSL_IOCWAITEVENT: 97462306a36Sopenharmony_ci return wait_mgsl_event(info, argp); 97562306a36Sopenharmony_ci case TIOCMIWAIT: 97662306a36Sopenharmony_ci return modem_input_wait(info,(int)arg); 97762306a36Sopenharmony_ci case MGSL_IOCSGPIO: 97862306a36Sopenharmony_ci return set_gpio(info, argp); 97962306a36Sopenharmony_ci case MGSL_IOCGGPIO: 98062306a36Sopenharmony_ci return get_gpio(info, argp); 98162306a36Sopenharmony_ci case MGSL_IOCWAITGPIO: 98262306a36Sopenharmony_ci return wait_gpio(info, argp); 98362306a36Sopenharmony_ci case MGSL_IOCGXSYNC: 98462306a36Sopenharmony_ci return get_xsync(info, argp); 98562306a36Sopenharmony_ci case MGSL_IOCSXSYNC: 98662306a36Sopenharmony_ci return set_xsync(info, (int)arg); 98762306a36Sopenharmony_ci case MGSL_IOCGXCTRL: 98862306a36Sopenharmony_ci return get_xctrl(info, argp); 98962306a36Sopenharmony_ci case MGSL_IOCSXCTRL: 99062306a36Sopenharmony_ci return set_xctrl(info, (int)arg); 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci mutex_lock(&info->port.mutex); 99362306a36Sopenharmony_ci switch (cmd) { 99462306a36Sopenharmony_ci case MGSL_IOCGPARAMS: 99562306a36Sopenharmony_ci ret = get_params(info, argp); 99662306a36Sopenharmony_ci break; 99762306a36Sopenharmony_ci case MGSL_IOCSPARAMS: 99862306a36Sopenharmony_ci ret = set_params(info, argp); 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci case MGSL_IOCGTXIDLE: 100162306a36Sopenharmony_ci ret = get_txidle(info, argp); 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci case MGSL_IOCSTXIDLE: 100462306a36Sopenharmony_ci ret = set_txidle(info, (int)arg); 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci case MGSL_IOCTXENABLE: 100762306a36Sopenharmony_ci ret = tx_enable(info, (int)arg); 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci case MGSL_IOCRXENABLE: 101062306a36Sopenharmony_ci ret = rx_enable(info, (int)arg); 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci case MGSL_IOCTXABORT: 101362306a36Sopenharmony_ci ret = tx_abort(info); 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci case MGSL_IOCGSTATS: 101662306a36Sopenharmony_ci ret = get_stats(info, argp); 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci case MGSL_IOCGIF: 101962306a36Sopenharmony_ci ret = get_interface(info, argp); 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci case MGSL_IOCSIF: 102262306a36Sopenharmony_ci ret = set_interface(info,(int)arg); 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci default: 102562306a36Sopenharmony_ci ret = -ENOIOCTLCMD; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci mutex_unlock(&info->port.mutex); 102862306a36Sopenharmony_ci return ret; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cistatic int get_icount(struct tty_struct *tty, 103262306a36Sopenharmony_ci struct serial_icounter_struct *icount) 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 103662306a36Sopenharmony_ci struct mgsl_icount cnow; /* kernel counter temps */ 103762306a36Sopenharmony_ci unsigned long flags; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 104062306a36Sopenharmony_ci cnow = info->icount; 104162306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci icount->cts = cnow.cts; 104462306a36Sopenharmony_ci icount->dsr = cnow.dsr; 104562306a36Sopenharmony_ci icount->rng = cnow.rng; 104662306a36Sopenharmony_ci icount->dcd = cnow.dcd; 104762306a36Sopenharmony_ci icount->rx = cnow.rx; 104862306a36Sopenharmony_ci icount->tx = cnow.tx; 104962306a36Sopenharmony_ci icount->frame = cnow.frame; 105062306a36Sopenharmony_ci icount->overrun = cnow.overrun; 105162306a36Sopenharmony_ci icount->parity = cnow.parity; 105262306a36Sopenharmony_ci icount->brk = cnow.brk; 105362306a36Sopenharmony_ci icount->buf_overrun = cnow.buf_overrun; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci return 0; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci/* 105962306a36Sopenharmony_ci * support for 32 bit ioctl calls on 64 bit systems 106062306a36Sopenharmony_ci */ 106162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 106262306a36Sopenharmony_cistatic long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct MGSL_PARAMS32 tmp_params; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci DBGINFO(("%s get_params32\n", info->device_name)); 106762306a36Sopenharmony_ci memset(&tmp_params, 0, sizeof(tmp_params)); 106862306a36Sopenharmony_ci tmp_params.mode = (compat_ulong_t)info->params.mode; 106962306a36Sopenharmony_ci tmp_params.loopback = info->params.loopback; 107062306a36Sopenharmony_ci tmp_params.flags = info->params.flags; 107162306a36Sopenharmony_ci tmp_params.encoding = info->params.encoding; 107262306a36Sopenharmony_ci tmp_params.clock_speed = (compat_ulong_t)info->params.clock_speed; 107362306a36Sopenharmony_ci tmp_params.addr_filter = info->params.addr_filter; 107462306a36Sopenharmony_ci tmp_params.crc_type = info->params.crc_type; 107562306a36Sopenharmony_ci tmp_params.preamble_length = info->params.preamble_length; 107662306a36Sopenharmony_ci tmp_params.preamble = info->params.preamble; 107762306a36Sopenharmony_ci tmp_params.data_rate = (compat_ulong_t)info->params.data_rate; 107862306a36Sopenharmony_ci tmp_params.data_bits = info->params.data_bits; 107962306a36Sopenharmony_ci tmp_params.stop_bits = info->params.stop_bits; 108062306a36Sopenharmony_ci tmp_params.parity = info->params.parity; 108162306a36Sopenharmony_ci if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32))) 108262306a36Sopenharmony_ci return -EFAULT; 108362306a36Sopenharmony_ci return 0; 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci struct MGSL_PARAMS32 tmp_params; 108962306a36Sopenharmony_ci unsigned long flags; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci DBGINFO(("%s set_params32\n", info->device_name)); 109262306a36Sopenharmony_ci if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32))) 109362306a36Sopenharmony_ci return -EFAULT; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 109662306a36Sopenharmony_ci if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) { 109762306a36Sopenharmony_ci info->base_clock = tmp_params.clock_speed; 109862306a36Sopenharmony_ci } else { 109962306a36Sopenharmony_ci info->params.mode = tmp_params.mode; 110062306a36Sopenharmony_ci info->params.loopback = tmp_params.loopback; 110162306a36Sopenharmony_ci info->params.flags = tmp_params.flags; 110262306a36Sopenharmony_ci info->params.encoding = tmp_params.encoding; 110362306a36Sopenharmony_ci info->params.clock_speed = tmp_params.clock_speed; 110462306a36Sopenharmony_ci info->params.addr_filter = tmp_params.addr_filter; 110562306a36Sopenharmony_ci info->params.crc_type = tmp_params.crc_type; 110662306a36Sopenharmony_ci info->params.preamble_length = tmp_params.preamble_length; 110762306a36Sopenharmony_ci info->params.preamble = tmp_params.preamble; 110862306a36Sopenharmony_ci info->params.data_rate = tmp_params.data_rate; 110962306a36Sopenharmony_ci info->params.data_bits = tmp_params.data_bits; 111062306a36Sopenharmony_ci info->params.stop_bits = tmp_params.stop_bits; 111162306a36Sopenharmony_ci info->params.parity = tmp_params.parity; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci program_hw(info); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci return 0; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic long slgt_compat_ioctl(struct tty_struct *tty, 112162306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 112462306a36Sopenharmony_ci int rc; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (sanity_check(info, tty->name, "compat_ioctl")) 112762306a36Sopenharmony_ci return -ENODEV; 112862306a36Sopenharmony_ci DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd)); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci switch (cmd) { 113162306a36Sopenharmony_ci case MGSL_IOCSPARAMS32: 113262306a36Sopenharmony_ci rc = set_params32(info, compat_ptr(arg)); 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci case MGSL_IOCGPARAMS32: 113662306a36Sopenharmony_ci rc = get_params32(info, compat_ptr(arg)); 113762306a36Sopenharmony_ci break; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci case MGSL_IOCGPARAMS: 114062306a36Sopenharmony_ci case MGSL_IOCSPARAMS: 114162306a36Sopenharmony_ci case MGSL_IOCGTXIDLE: 114262306a36Sopenharmony_ci case MGSL_IOCGSTATS: 114362306a36Sopenharmony_ci case MGSL_IOCWAITEVENT: 114462306a36Sopenharmony_ci case MGSL_IOCGIF: 114562306a36Sopenharmony_ci case MGSL_IOCSGPIO: 114662306a36Sopenharmony_ci case MGSL_IOCGGPIO: 114762306a36Sopenharmony_ci case MGSL_IOCWAITGPIO: 114862306a36Sopenharmony_ci case MGSL_IOCGXSYNC: 114962306a36Sopenharmony_ci case MGSL_IOCGXCTRL: 115062306a36Sopenharmony_ci rc = ioctl(tty, cmd, (unsigned long)compat_ptr(arg)); 115162306a36Sopenharmony_ci break; 115262306a36Sopenharmony_ci default: 115362306a36Sopenharmony_ci rc = ioctl(tty, cmd, arg); 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc)); 115662306a36Sopenharmony_ci return rc; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci#else 115962306a36Sopenharmony_ci#define slgt_compat_ioctl NULL 116062306a36Sopenharmony_ci#endif /* ifdef CONFIG_COMPAT */ 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci/* 116362306a36Sopenharmony_ci * proc fs support 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_cistatic inline void line_info(struct seq_file *m, struct slgt_info *info) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci char stat_buf[30]; 116862306a36Sopenharmony_ci unsigned long flags; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n", 117162306a36Sopenharmony_ci info->device_name, info->phys_reg_addr, 117262306a36Sopenharmony_ci info->irq_level, info->max_frame_size); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci /* output current serial signal states */ 117562306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 117662306a36Sopenharmony_ci get_gtsignals(info); 117762306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci stat_buf[0] = 0; 118062306a36Sopenharmony_ci stat_buf[1] = 0; 118162306a36Sopenharmony_ci if (info->signals & SerialSignal_RTS) 118262306a36Sopenharmony_ci strcat(stat_buf, "|RTS"); 118362306a36Sopenharmony_ci if (info->signals & SerialSignal_CTS) 118462306a36Sopenharmony_ci strcat(stat_buf, "|CTS"); 118562306a36Sopenharmony_ci if (info->signals & SerialSignal_DTR) 118662306a36Sopenharmony_ci strcat(stat_buf, "|DTR"); 118762306a36Sopenharmony_ci if (info->signals & SerialSignal_DSR) 118862306a36Sopenharmony_ci strcat(stat_buf, "|DSR"); 118962306a36Sopenharmony_ci if (info->signals & SerialSignal_DCD) 119062306a36Sopenharmony_ci strcat(stat_buf, "|CD"); 119162306a36Sopenharmony_ci if (info->signals & SerialSignal_RI) 119262306a36Sopenharmony_ci strcat(stat_buf, "|RI"); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 119562306a36Sopenharmony_ci seq_printf(m, "\tHDLC txok:%d rxok:%d", 119662306a36Sopenharmony_ci info->icount.txok, info->icount.rxok); 119762306a36Sopenharmony_ci if (info->icount.txunder) 119862306a36Sopenharmony_ci seq_printf(m, " txunder:%d", info->icount.txunder); 119962306a36Sopenharmony_ci if (info->icount.txabort) 120062306a36Sopenharmony_ci seq_printf(m, " txabort:%d", info->icount.txabort); 120162306a36Sopenharmony_ci if (info->icount.rxshort) 120262306a36Sopenharmony_ci seq_printf(m, " rxshort:%d", info->icount.rxshort); 120362306a36Sopenharmony_ci if (info->icount.rxlong) 120462306a36Sopenharmony_ci seq_printf(m, " rxlong:%d", info->icount.rxlong); 120562306a36Sopenharmony_ci if (info->icount.rxover) 120662306a36Sopenharmony_ci seq_printf(m, " rxover:%d", info->icount.rxover); 120762306a36Sopenharmony_ci if (info->icount.rxcrc) 120862306a36Sopenharmony_ci seq_printf(m, " rxcrc:%d", info->icount.rxcrc); 120962306a36Sopenharmony_ci } else { 121062306a36Sopenharmony_ci seq_printf(m, "\tASYNC tx:%d rx:%d", 121162306a36Sopenharmony_ci info->icount.tx, info->icount.rx); 121262306a36Sopenharmony_ci if (info->icount.frame) 121362306a36Sopenharmony_ci seq_printf(m, " fe:%d", info->icount.frame); 121462306a36Sopenharmony_ci if (info->icount.parity) 121562306a36Sopenharmony_ci seq_printf(m, " pe:%d", info->icount.parity); 121662306a36Sopenharmony_ci if (info->icount.brk) 121762306a36Sopenharmony_ci seq_printf(m, " brk:%d", info->icount.brk); 121862306a36Sopenharmony_ci if (info->icount.overrun) 121962306a36Sopenharmony_ci seq_printf(m, " oe:%d", info->icount.overrun); 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci /* Append serial signal status to end */ 122362306a36Sopenharmony_ci seq_printf(m, " %s\n", stat_buf+1); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", 122662306a36Sopenharmony_ci info->tx_active,info->bh_requested,info->bh_running, 122762306a36Sopenharmony_ci info->pending_bh); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci/* Called to print information about devices 123162306a36Sopenharmony_ci */ 123262306a36Sopenharmony_cistatic int synclink_gt_proc_show(struct seq_file *m, void *v) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct slgt_info *info; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci seq_puts(m, "synclink_gt driver\n"); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci info = slgt_device_list; 123962306a36Sopenharmony_ci while( info ) { 124062306a36Sopenharmony_ci line_info(m, info); 124162306a36Sopenharmony_ci info = info->next_device; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci return 0; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci/* 124762306a36Sopenharmony_ci * return count of bytes in transmit buffer 124862306a36Sopenharmony_ci */ 124962306a36Sopenharmony_cistatic unsigned int chars_in_buffer(struct tty_struct *tty) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 125262306a36Sopenharmony_ci unsigned int count; 125362306a36Sopenharmony_ci if (sanity_check(info, tty->name, "chars_in_buffer")) 125462306a36Sopenharmony_ci return 0; 125562306a36Sopenharmony_ci count = tbuf_bytes(info); 125662306a36Sopenharmony_ci DBGINFO(("%s chars_in_buffer()=%u\n", info->device_name, count)); 125762306a36Sopenharmony_ci return count; 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci/* 126162306a36Sopenharmony_ci * signal remote device to throttle send data (our receive data) 126262306a36Sopenharmony_ci */ 126362306a36Sopenharmony_cistatic void throttle(struct tty_struct * tty) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 126662306a36Sopenharmony_ci unsigned long flags; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (sanity_check(info, tty->name, "throttle")) 126962306a36Sopenharmony_ci return; 127062306a36Sopenharmony_ci DBGINFO(("%s throttle\n", info->device_name)); 127162306a36Sopenharmony_ci if (I_IXOFF(tty)) 127262306a36Sopenharmony_ci send_xchar(tty, STOP_CHAR(tty)); 127362306a36Sopenharmony_ci if (C_CRTSCTS(tty)) { 127462306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 127562306a36Sopenharmony_ci info->signals &= ~SerialSignal_RTS; 127662306a36Sopenharmony_ci set_gtsignals(info); 127762306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci/* 128262306a36Sopenharmony_ci * signal remote device to stop throttling send data (our receive data) 128362306a36Sopenharmony_ci */ 128462306a36Sopenharmony_cistatic void unthrottle(struct tty_struct * tty) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 128762306a36Sopenharmony_ci unsigned long flags; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (sanity_check(info, tty->name, "unthrottle")) 129062306a36Sopenharmony_ci return; 129162306a36Sopenharmony_ci DBGINFO(("%s unthrottle\n", info->device_name)); 129262306a36Sopenharmony_ci if (I_IXOFF(tty)) { 129362306a36Sopenharmony_ci if (info->x_char) 129462306a36Sopenharmony_ci info->x_char = 0; 129562306a36Sopenharmony_ci else 129662306a36Sopenharmony_ci send_xchar(tty, START_CHAR(tty)); 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci if (C_CRTSCTS(tty)) { 129962306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 130062306a36Sopenharmony_ci info->signals |= SerialSignal_RTS; 130162306a36Sopenharmony_ci set_gtsignals(info); 130262306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci/* 130762306a36Sopenharmony_ci * set or clear transmit break condition 130862306a36Sopenharmony_ci * break_state -1=set break condition, 0=clear 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_cistatic int set_break(struct tty_struct *tty, int break_state) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 131362306a36Sopenharmony_ci unsigned short value; 131462306a36Sopenharmony_ci unsigned long flags; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (sanity_check(info, tty->name, "set_break")) 131762306a36Sopenharmony_ci return -EINVAL; 131862306a36Sopenharmony_ci DBGINFO(("%s set_break(%d)\n", info->device_name, break_state)); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 132162306a36Sopenharmony_ci value = rd_reg16(info, TCR); 132262306a36Sopenharmony_ci if (break_state == -1) 132362306a36Sopenharmony_ci value |= BIT6; 132462306a36Sopenharmony_ci else 132562306a36Sopenharmony_ci value &= ~BIT6; 132662306a36Sopenharmony_ci wr_reg16(info, TCR, value); 132762306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 132862306a36Sopenharmony_ci return 0; 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci/** 133462306a36Sopenharmony_ci * hdlcdev_attach - called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) 133562306a36Sopenharmony_ci * @dev: pointer to network device structure 133662306a36Sopenharmony_ci * @encoding: serial encoding setting 133762306a36Sopenharmony_ci * @parity: FCS setting 133862306a36Sopenharmony_ci * 133962306a36Sopenharmony_ci * Set encoding and frame check sequence (FCS) options. 134062306a36Sopenharmony_ci * 134162306a36Sopenharmony_ci * Return: 0 if success, otherwise error code 134262306a36Sopenharmony_ci */ 134362306a36Sopenharmony_cistatic int hdlcdev_attach(struct net_device *dev, unsigned short encoding, 134462306a36Sopenharmony_ci unsigned short parity) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 134762306a36Sopenharmony_ci unsigned char new_encoding; 134862306a36Sopenharmony_ci unsigned short new_crctype; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci /* return error if TTY interface open */ 135162306a36Sopenharmony_ci if (info->port.count) 135262306a36Sopenharmony_ci return -EBUSY; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci DBGINFO(("%s hdlcdev_attach\n", info->device_name)); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci switch (encoding) 135762306a36Sopenharmony_ci { 135862306a36Sopenharmony_ci case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; 135962306a36Sopenharmony_ci case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; 136062306a36Sopenharmony_ci case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; 136162306a36Sopenharmony_ci case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; 136262306a36Sopenharmony_ci case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; 136362306a36Sopenharmony_ci default: return -EINVAL; 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci switch (parity) 136762306a36Sopenharmony_ci { 136862306a36Sopenharmony_ci case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; 136962306a36Sopenharmony_ci case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; 137062306a36Sopenharmony_ci case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; 137162306a36Sopenharmony_ci default: return -EINVAL; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci info->params.encoding = new_encoding; 137562306a36Sopenharmony_ci info->params.crc_type = new_crctype; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* if network interface up, reprogram hardware */ 137862306a36Sopenharmony_ci if (info->netcount) 137962306a36Sopenharmony_ci program_hw(info); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci return 0; 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci/** 138562306a36Sopenharmony_ci * hdlcdev_xmit - called by generic HDLC layer to send a frame 138662306a36Sopenharmony_ci * @skb: socket buffer containing HDLC frame 138762306a36Sopenharmony_ci * @dev: pointer to network device structure 138862306a36Sopenharmony_ci */ 138962306a36Sopenharmony_cistatic netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, 139062306a36Sopenharmony_ci struct net_device *dev) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 139362306a36Sopenharmony_ci unsigned long flags; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci DBGINFO(("%s hdlc_xmit\n", dev->name)); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci if (!skb->len) 139862306a36Sopenharmony_ci return NETDEV_TX_OK; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci /* stop sending until this frame completes */ 140162306a36Sopenharmony_ci netif_stop_queue(dev); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* update network statistics */ 140462306a36Sopenharmony_ci dev->stats.tx_packets++; 140562306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci /* save start time for transmit timeout detection */ 140862306a36Sopenharmony_ci netif_trans_update(dev); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 141162306a36Sopenharmony_ci tx_load(info, skb->data, skb->len); 141262306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* done with socket buffer, so free it */ 141562306a36Sopenharmony_ci dev_kfree_skb(skb); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci return NETDEV_TX_OK; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci/** 142162306a36Sopenharmony_ci * hdlcdev_open - called by network layer when interface enabled 142262306a36Sopenharmony_ci * @dev: pointer to network device structure 142362306a36Sopenharmony_ci * 142462306a36Sopenharmony_ci * Claim resources and initialize hardware. 142562306a36Sopenharmony_ci * 142662306a36Sopenharmony_ci * Return: 0 if success, otherwise error code 142762306a36Sopenharmony_ci */ 142862306a36Sopenharmony_cistatic int hdlcdev_open(struct net_device *dev) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 143162306a36Sopenharmony_ci int rc; 143262306a36Sopenharmony_ci unsigned long flags; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci DBGINFO(("%s hdlcdev_open\n", dev->name)); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci /* arbitrate between network and tty opens */ 143762306a36Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 143862306a36Sopenharmony_ci if (info->port.count != 0 || info->netcount != 0) { 143962306a36Sopenharmony_ci DBGINFO(("%s hdlc_open busy\n", dev->name)); 144062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 144162306a36Sopenharmony_ci return -EBUSY; 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci info->netcount=1; 144462306a36Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci /* claim resources and init adapter */ 144762306a36Sopenharmony_ci if ((rc = startup(info)) != 0) { 144862306a36Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 144962306a36Sopenharmony_ci info->netcount=0; 145062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 145162306a36Sopenharmony_ci return rc; 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci /* generic HDLC layer open processing */ 145562306a36Sopenharmony_ci rc = hdlc_open(dev); 145662306a36Sopenharmony_ci if (rc) { 145762306a36Sopenharmony_ci shutdown(info); 145862306a36Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 145962306a36Sopenharmony_ci info->netcount = 0; 146062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 146162306a36Sopenharmony_ci return rc; 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci /* assert RTS and DTR, apply hardware settings */ 146562306a36Sopenharmony_ci info->signals |= SerialSignal_RTS | SerialSignal_DTR; 146662306a36Sopenharmony_ci program_hw(info); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci /* enable network layer transmit */ 146962306a36Sopenharmony_ci netif_trans_update(dev); 147062306a36Sopenharmony_ci netif_start_queue(dev); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci /* inform generic HDLC layer of current DCD status */ 147362306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 147462306a36Sopenharmony_ci get_gtsignals(info); 147562306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 147662306a36Sopenharmony_ci if (info->signals & SerialSignal_DCD) 147762306a36Sopenharmony_ci netif_carrier_on(dev); 147862306a36Sopenharmony_ci else 147962306a36Sopenharmony_ci netif_carrier_off(dev); 148062306a36Sopenharmony_ci return 0; 148162306a36Sopenharmony_ci} 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci/** 148462306a36Sopenharmony_ci * hdlcdev_close - called by network layer when interface is disabled 148562306a36Sopenharmony_ci * @dev: pointer to network device structure 148662306a36Sopenharmony_ci * 148762306a36Sopenharmony_ci * Shutdown hardware and release resources. 148862306a36Sopenharmony_ci * 148962306a36Sopenharmony_ci * Return: 0 if success, otherwise error code 149062306a36Sopenharmony_ci */ 149162306a36Sopenharmony_cistatic int hdlcdev_close(struct net_device *dev) 149262306a36Sopenharmony_ci{ 149362306a36Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 149462306a36Sopenharmony_ci unsigned long flags; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci DBGINFO(("%s hdlcdev_close\n", dev->name)); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci netif_stop_queue(dev); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci /* shutdown adapter and release resources */ 150162306a36Sopenharmony_ci shutdown(info); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci hdlc_close(dev); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci spin_lock_irqsave(&info->netlock, flags); 150662306a36Sopenharmony_ci info->netcount=0; 150762306a36Sopenharmony_ci spin_unlock_irqrestore(&info->netlock, flags); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci return 0; 151062306a36Sopenharmony_ci} 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci/** 151362306a36Sopenharmony_ci * hdlcdev_ioctl - called by network layer to process IOCTL call to network device 151462306a36Sopenharmony_ci * @dev: pointer to network device structure 151562306a36Sopenharmony_ci * @ifr: pointer to network interface request structure 151662306a36Sopenharmony_ci * @cmd: IOCTL command code 151762306a36Sopenharmony_ci * 151862306a36Sopenharmony_ci * Return: 0 if success, otherwise error code 151962306a36Sopenharmony_ci */ 152062306a36Sopenharmony_cistatic int hdlcdev_ioctl(struct net_device *dev, struct if_settings *ifs) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci const size_t size = sizeof(sync_serial_settings); 152362306a36Sopenharmony_ci sync_serial_settings new_line; 152462306a36Sopenharmony_ci sync_serial_settings __user *line = ifs->ifs_ifsu.sync; 152562306a36Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 152662306a36Sopenharmony_ci unsigned int flags; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci DBGINFO(("%s hdlcdev_ioctl\n", dev->name)); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci /* return error if TTY interface open */ 153162306a36Sopenharmony_ci if (info->port.count) 153262306a36Sopenharmony_ci return -EBUSY; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci memset(&new_line, 0, sizeof(new_line)); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci switch (ifs->type) { 153762306a36Sopenharmony_ci case IF_GET_IFACE: /* return current sync_serial_settings */ 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci ifs->type = IF_IFACE_SYNC_SERIAL; 154062306a36Sopenharmony_ci if (ifs->size < size) { 154162306a36Sopenharmony_ci ifs->size = size; /* data size wanted */ 154262306a36Sopenharmony_ci return -ENOBUFS; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | 154662306a36Sopenharmony_ci HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | 154762306a36Sopenharmony_ci HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | 154862306a36Sopenharmony_ci HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci switch (flags){ 155162306a36Sopenharmony_ci case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; 155262306a36Sopenharmony_ci case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; 155362306a36Sopenharmony_ci case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; 155462306a36Sopenharmony_ci case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; 155562306a36Sopenharmony_ci default: new_line.clock_type = CLOCK_DEFAULT; 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci new_line.clock_rate = info->params.clock_speed; 155962306a36Sopenharmony_ci new_line.loopback = info->params.loopback ? 1:0; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci if (copy_to_user(line, &new_line, size)) 156262306a36Sopenharmony_ci return -EFAULT; 156362306a36Sopenharmony_ci return 0; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci if(!capable(CAP_NET_ADMIN)) 156862306a36Sopenharmony_ci return -EPERM; 156962306a36Sopenharmony_ci if (copy_from_user(&new_line, line, size)) 157062306a36Sopenharmony_ci return -EFAULT; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci switch (new_line.clock_type) 157362306a36Sopenharmony_ci { 157462306a36Sopenharmony_ci case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; 157562306a36Sopenharmony_ci case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; 157662306a36Sopenharmony_ci case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; 157762306a36Sopenharmony_ci case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; 157862306a36Sopenharmony_ci case CLOCK_DEFAULT: flags = info->params.flags & 157962306a36Sopenharmony_ci (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | 158062306a36Sopenharmony_ci HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | 158162306a36Sopenharmony_ci HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | 158262306a36Sopenharmony_ci HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; 158362306a36Sopenharmony_ci default: return -EINVAL; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci if (new_line.loopback != 0 && new_line.loopback != 1) 158762306a36Sopenharmony_ci return -EINVAL; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | 159062306a36Sopenharmony_ci HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | 159162306a36Sopenharmony_ci HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | 159262306a36Sopenharmony_ci HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); 159362306a36Sopenharmony_ci info->params.flags |= flags; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci info->params.loopback = new_line.loopback; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) 159862306a36Sopenharmony_ci info->params.clock_speed = new_line.clock_rate; 159962306a36Sopenharmony_ci else 160062306a36Sopenharmony_ci info->params.clock_speed = 0; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci /* if network interface up, reprogram hardware */ 160362306a36Sopenharmony_ci if (info->netcount) 160462306a36Sopenharmony_ci program_hw(info); 160562306a36Sopenharmony_ci return 0; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci default: 160862306a36Sopenharmony_ci return hdlc_ioctl(dev, ifs); 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci/** 161362306a36Sopenharmony_ci * hdlcdev_tx_timeout - called by network layer when transmit timeout is detected 161462306a36Sopenharmony_ci * @dev: pointer to network device structure 161562306a36Sopenharmony_ci * @txqueue: unused 161662306a36Sopenharmony_ci */ 161762306a36Sopenharmony_cistatic void hdlcdev_tx_timeout(struct net_device *dev, unsigned int txqueue) 161862306a36Sopenharmony_ci{ 161962306a36Sopenharmony_ci struct slgt_info *info = dev_to_port(dev); 162062306a36Sopenharmony_ci unsigned long flags; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name)); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci dev->stats.tx_errors++; 162562306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 162862306a36Sopenharmony_ci tx_stop(info); 162962306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci netif_wake_queue(dev); 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci/** 163562306a36Sopenharmony_ci * hdlcdev_tx_done - called by device driver when transmit completes 163662306a36Sopenharmony_ci * @info: pointer to device instance information 163762306a36Sopenharmony_ci * 163862306a36Sopenharmony_ci * Reenable network layer transmit if stopped. 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_cistatic void hdlcdev_tx_done(struct slgt_info *info) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci if (netif_queue_stopped(info->netdev)) 164362306a36Sopenharmony_ci netif_wake_queue(info->netdev); 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci/** 164762306a36Sopenharmony_ci * hdlcdev_rx - called by device driver when frame received 164862306a36Sopenharmony_ci * @info: pointer to device instance information 164962306a36Sopenharmony_ci * @buf: pointer to buffer contianing frame data 165062306a36Sopenharmony_ci * @size: count of data bytes in buf 165162306a36Sopenharmony_ci * 165262306a36Sopenharmony_ci * Pass frame to network layer. 165362306a36Sopenharmony_ci */ 165462306a36Sopenharmony_cistatic void hdlcdev_rx(struct slgt_info *info, char *buf, int size) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci struct sk_buff *skb = dev_alloc_skb(size); 165762306a36Sopenharmony_ci struct net_device *dev = info->netdev; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci DBGINFO(("%s hdlcdev_rx\n", dev->name)); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci if (skb == NULL) { 166262306a36Sopenharmony_ci DBGERR(("%s: can't alloc skb, drop packet\n", dev->name)); 166362306a36Sopenharmony_ci dev->stats.rx_dropped++; 166462306a36Sopenharmony_ci return; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci skb_put_data(skb, buf, size); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci skb->protocol = hdlc_type_trans(skb, dev); 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci dev->stats.rx_packets++; 167262306a36Sopenharmony_ci dev->stats.rx_bytes += size; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci netif_rx(skb); 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic const struct net_device_ops hdlcdev_ops = { 167862306a36Sopenharmony_ci .ndo_open = hdlcdev_open, 167962306a36Sopenharmony_ci .ndo_stop = hdlcdev_close, 168062306a36Sopenharmony_ci .ndo_start_xmit = hdlc_start_xmit, 168162306a36Sopenharmony_ci .ndo_siocwandev = hdlcdev_ioctl, 168262306a36Sopenharmony_ci .ndo_tx_timeout = hdlcdev_tx_timeout, 168362306a36Sopenharmony_ci}; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci/** 168662306a36Sopenharmony_ci * hdlcdev_init - called by device driver when adding device instance 168762306a36Sopenharmony_ci * @info: pointer to device instance information 168862306a36Sopenharmony_ci * 168962306a36Sopenharmony_ci * Do generic HDLC initialization. 169062306a36Sopenharmony_ci * 169162306a36Sopenharmony_ci * Return: 0 if success, otherwise error code 169262306a36Sopenharmony_ci */ 169362306a36Sopenharmony_cistatic int hdlcdev_init(struct slgt_info *info) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci int rc; 169662306a36Sopenharmony_ci struct net_device *dev; 169762306a36Sopenharmony_ci hdlc_device *hdlc; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci /* allocate and initialize network and HDLC layer objects */ 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci dev = alloc_hdlcdev(info); 170262306a36Sopenharmony_ci if (!dev) { 170362306a36Sopenharmony_ci printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name); 170462306a36Sopenharmony_ci return -ENOMEM; 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci /* for network layer reporting purposes only */ 170862306a36Sopenharmony_ci dev->mem_start = info->phys_reg_addr; 170962306a36Sopenharmony_ci dev->mem_end = info->phys_reg_addr + SLGT_REG_SIZE - 1; 171062306a36Sopenharmony_ci dev->irq = info->irq_level; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* network layer callbacks and settings */ 171362306a36Sopenharmony_ci dev->netdev_ops = &hdlcdev_ops; 171462306a36Sopenharmony_ci dev->watchdog_timeo = 10 * HZ; 171562306a36Sopenharmony_ci dev->tx_queue_len = 50; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci /* generic HDLC layer callbacks and settings */ 171862306a36Sopenharmony_ci hdlc = dev_to_hdlc(dev); 171962306a36Sopenharmony_ci hdlc->attach = hdlcdev_attach; 172062306a36Sopenharmony_ci hdlc->xmit = hdlcdev_xmit; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci /* register objects with HDLC layer */ 172362306a36Sopenharmony_ci rc = register_hdlc_device(dev); 172462306a36Sopenharmony_ci if (rc) { 172562306a36Sopenharmony_ci printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); 172662306a36Sopenharmony_ci free_netdev(dev); 172762306a36Sopenharmony_ci return rc; 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci info->netdev = dev; 173162306a36Sopenharmony_ci return 0; 173262306a36Sopenharmony_ci} 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci/** 173562306a36Sopenharmony_ci * hdlcdev_exit - called by device driver when removing device instance 173662306a36Sopenharmony_ci * @info: pointer to device instance information 173762306a36Sopenharmony_ci * 173862306a36Sopenharmony_ci * Do generic HDLC cleanup. 173962306a36Sopenharmony_ci */ 174062306a36Sopenharmony_cistatic void hdlcdev_exit(struct slgt_info *info) 174162306a36Sopenharmony_ci{ 174262306a36Sopenharmony_ci if (!info->netdev) 174362306a36Sopenharmony_ci return; 174462306a36Sopenharmony_ci unregister_hdlc_device(info->netdev); 174562306a36Sopenharmony_ci free_netdev(info->netdev); 174662306a36Sopenharmony_ci info->netdev = NULL; 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci#endif /* ifdef CONFIG_HDLC */ 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci/* 175262306a36Sopenharmony_ci * get async data from rx DMA buffers 175362306a36Sopenharmony_ci */ 175462306a36Sopenharmony_cistatic void rx_async(struct slgt_info *info) 175562306a36Sopenharmony_ci{ 175662306a36Sopenharmony_ci struct mgsl_icount *icount = &info->icount; 175762306a36Sopenharmony_ci unsigned int start, end; 175862306a36Sopenharmony_ci unsigned char *p; 175962306a36Sopenharmony_ci unsigned char status; 176062306a36Sopenharmony_ci struct slgt_desc *bufs = info->rbufs; 176162306a36Sopenharmony_ci int i, count; 176262306a36Sopenharmony_ci int chars = 0; 176362306a36Sopenharmony_ci int stat; 176462306a36Sopenharmony_ci unsigned char ch; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci start = end = info->rbuf_current; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci while(desc_complete(bufs[end])) { 176962306a36Sopenharmony_ci count = desc_count(bufs[end]) - info->rbuf_index; 177062306a36Sopenharmony_ci p = bufs[end].buf + info->rbuf_index; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci DBGISR(("%s rx_async count=%d\n", info->device_name, count)); 177362306a36Sopenharmony_ci DBGDATA(info, p, count, "rx"); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci for(i=0 ; i < count; i+=2, p+=2) { 177662306a36Sopenharmony_ci ch = *p; 177762306a36Sopenharmony_ci icount->rx++; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci stat = 0; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci status = *(p + 1) & (BIT1 + BIT0); 178262306a36Sopenharmony_ci if (status) { 178362306a36Sopenharmony_ci if (status & BIT1) 178462306a36Sopenharmony_ci icount->parity++; 178562306a36Sopenharmony_ci else if (status & BIT0) 178662306a36Sopenharmony_ci icount->frame++; 178762306a36Sopenharmony_ci /* discard char if tty control flags say so */ 178862306a36Sopenharmony_ci if (status & info->ignore_status_mask) 178962306a36Sopenharmony_ci continue; 179062306a36Sopenharmony_ci if (status & BIT1) 179162306a36Sopenharmony_ci stat = TTY_PARITY; 179262306a36Sopenharmony_ci else if (status & BIT0) 179362306a36Sopenharmony_ci stat = TTY_FRAME; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci tty_insert_flip_char(&info->port, ch, stat); 179662306a36Sopenharmony_ci chars++; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci if (i < count) { 180062306a36Sopenharmony_ci /* receive buffer not completed */ 180162306a36Sopenharmony_ci info->rbuf_index += i; 180262306a36Sopenharmony_ci mod_timer(&info->rx_timer, jiffies + 1); 180362306a36Sopenharmony_ci break; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci info->rbuf_index = 0; 180762306a36Sopenharmony_ci free_rbufs(info, end, end); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (++end == info->rbuf_count) 181062306a36Sopenharmony_ci end = 0; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci /* if entire list searched then no frame available */ 181362306a36Sopenharmony_ci if (end == start) 181462306a36Sopenharmony_ci break; 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci if (chars) 181862306a36Sopenharmony_ci tty_flip_buffer_push(&info->port); 181962306a36Sopenharmony_ci} 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci/* 182262306a36Sopenharmony_ci * return next bottom half action to perform 182362306a36Sopenharmony_ci */ 182462306a36Sopenharmony_cistatic int bh_action(struct slgt_info *info) 182562306a36Sopenharmony_ci{ 182662306a36Sopenharmony_ci unsigned long flags; 182762306a36Sopenharmony_ci int rc; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci if (info->pending_bh & BH_RECEIVE) { 183262306a36Sopenharmony_ci info->pending_bh &= ~BH_RECEIVE; 183362306a36Sopenharmony_ci rc = BH_RECEIVE; 183462306a36Sopenharmony_ci } else if (info->pending_bh & BH_TRANSMIT) { 183562306a36Sopenharmony_ci info->pending_bh &= ~BH_TRANSMIT; 183662306a36Sopenharmony_ci rc = BH_TRANSMIT; 183762306a36Sopenharmony_ci } else if (info->pending_bh & BH_STATUS) { 183862306a36Sopenharmony_ci info->pending_bh &= ~BH_STATUS; 183962306a36Sopenharmony_ci rc = BH_STATUS; 184062306a36Sopenharmony_ci } else { 184162306a36Sopenharmony_ci /* Mark BH routine as complete */ 184262306a36Sopenharmony_ci info->bh_running = false; 184362306a36Sopenharmony_ci info->bh_requested = false; 184462306a36Sopenharmony_ci rc = 0; 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci return rc; 185062306a36Sopenharmony_ci} 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci/* 185362306a36Sopenharmony_ci * perform bottom half processing 185462306a36Sopenharmony_ci */ 185562306a36Sopenharmony_cistatic void bh_handler(struct work_struct *work) 185662306a36Sopenharmony_ci{ 185762306a36Sopenharmony_ci struct slgt_info *info = container_of(work, struct slgt_info, task); 185862306a36Sopenharmony_ci int action; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci info->bh_running = true; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci while((action = bh_action(info))) { 186362306a36Sopenharmony_ci switch (action) { 186462306a36Sopenharmony_ci case BH_RECEIVE: 186562306a36Sopenharmony_ci DBGBH(("%s bh receive\n", info->device_name)); 186662306a36Sopenharmony_ci switch(info->params.mode) { 186762306a36Sopenharmony_ci case MGSL_MODE_ASYNC: 186862306a36Sopenharmony_ci rx_async(info); 186962306a36Sopenharmony_ci break; 187062306a36Sopenharmony_ci case MGSL_MODE_HDLC: 187162306a36Sopenharmony_ci while(rx_get_frame(info)); 187262306a36Sopenharmony_ci break; 187362306a36Sopenharmony_ci case MGSL_MODE_RAW: 187462306a36Sopenharmony_ci case MGSL_MODE_MONOSYNC: 187562306a36Sopenharmony_ci case MGSL_MODE_BISYNC: 187662306a36Sopenharmony_ci case MGSL_MODE_XSYNC: 187762306a36Sopenharmony_ci while(rx_get_buf(info)); 187862306a36Sopenharmony_ci break; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci /* restart receiver if rx DMA buffers exhausted */ 188162306a36Sopenharmony_ci if (info->rx_restart) 188262306a36Sopenharmony_ci rx_start(info); 188362306a36Sopenharmony_ci break; 188462306a36Sopenharmony_ci case BH_TRANSMIT: 188562306a36Sopenharmony_ci bh_transmit(info); 188662306a36Sopenharmony_ci break; 188762306a36Sopenharmony_ci case BH_STATUS: 188862306a36Sopenharmony_ci DBGBH(("%s bh status\n", info->device_name)); 188962306a36Sopenharmony_ci info->ri_chkcount = 0; 189062306a36Sopenharmony_ci info->dsr_chkcount = 0; 189162306a36Sopenharmony_ci info->dcd_chkcount = 0; 189262306a36Sopenharmony_ci info->cts_chkcount = 0; 189362306a36Sopenharmony_ci break; 189462306a36Sopenharmony_ci default: 189562306a36Sopenharmony_ci DBGBH(("%s unknown action\n", info->device_name)); 189662306a36Sopenharmony_ci break; 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci } 189962306a36Sopenharmony_ci DBGBH(("%s bh_handler exit\n", info->device_name)); 190062306a36Sopenharmony_ci} 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_cistatic void bh_transmit(struct slgt_info *info) 190362306a36Sopenharmony_ci{ 190462306a36Sopenharmony_ci struct tty_struct *tty = info->port.tty; 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci DBGBH(("%s bh_transmit\n", info->device_name)); 190762306a36Sopenharmony_ci if (tty) 190862306a36Sopenharmony_ci tty_wakeup(tty); 190962306a36Sopenharmony_ci} 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_cistatic void dsr_change(struct slgt_info *info, unsigned short status) 191262306a36Sopenharmony_ci{ 191362306a36Sopenharmony_ci if (status & BIT3) { 191462306a36Sopenharmony_ci info->signals |= SerialSignal_DSR; 191562306a36Sopenharmony_ci info->input_signal_events.dsr_up++; 191662306a36Sopenharmony_ci } else { 191762306a36Sopenharmony_ci info->signals &= ~SerialSignal_DSR; 191862306a36Sopenharmony_ci info->input_signal_events.dsr_down++; 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals)); 192162306a36Sopenharmony_ci if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { 192262306a36Sopenharmony_ci slgt_irq_off(info, IRQ_DSR); 192362306a36Sopenharmony_ci return; 192462306a36Sopenharmony_ci } 192562306a36Sopenharmony_ci info->icount.dsr++; 192662306a36Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 192762306a36Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 192862306a36Sopenharmony_ci info->pending_bh |= BH_STATUS; 192962306a36Sopenharmony_ci} 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_cistatic void cts_change(struct slgt_info *info, unsigned short status) 193262306a36Sopenharmony_ci{ 193362306a36Sopenharmony_ci if (status & BIT2) { 193462306a36Sopenharmony_ci info->signals |= SerialSignal_CTS; 193562306a36Sopenharmony_ci info->input_signal_events.cts_up++; 193662306a36Sopenharmony_ci } else { 193762306a36Sopenharmony_ci info->signals &= ~SerialSignal_CTS; 193862306a36Sopenharmony_ci info->input_signal_events.cts_down++; 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals)); 194162306a36Sopenharmony_ci if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { 194262306a36Sopenharmony_ci slgt_irq_off(info, IRQ_CTS); 194362306a36Sopenharmony_ci return; 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci info->icount.cts++; 194662306a36Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 194762306a36Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 194862306a36Sopenharmony_ci info->pending_bh |= BH_STATUS; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci if (tty_port_cts_enabled(&info->port)) { 195162306a36Sopenharmony_ci if (info->port.tty) { 195262306a36Sopenharmony_ci if (info->port.tty->hw_stopped) { 195362306a36Sopenharmony_ci if (info->signals & SerialSignal_CTS) { 195462306a36Sopenharmony_ci info->port.tty->hw_stopped = false; 195562306a36Sopenharmony_ci info->pending_bh |= BH_TRANSMIT; 195662306a36Sopenharmony_ci return; 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci } else { 195962306a36Sopenharmony_ci if (!(info->signals & SerialSignal_CTS)) 196062306a36Sopenharmony_ci info->port.tty->hw_stopped = true; 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci } 196462306a36Sopenharmony_ci} 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_cistatic void dcd_change(struct slgt_info *info, unsigned short status) 196762306a36Sopenharmony_ci{ 196862306a36Sopenharmony_ci if (status & BIT1) { 196962306a36Sopenharmony_ci info->signals |= SerialSignal_DCD; 197062306a36Sopenharmony_ci info->input_signal_events.dcd_up++; 197162306a36Sopenharmony_ci } else { 197262306a36Sopenharmony_ci info->signals &= ~SerialSignal_DCD; 197362306a36Sopenharmony_ci info->input_signal_events.dcd_down++; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals)); 197662306a36Sopenharmony_ci if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { 197762306a36Sopenharmony_ci slgt_irq_off(info, IRQ_DCD); 197862306a36Sopenharmony_ci return; 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci info->icount.dcd++; 198162306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 198262306a36Sopenharmony_ci if (info->netcount) { 198362306a36Sopenharmony_ci if (info->signals & SerialSignal_DCD) 198462306a36Sopenharmony_ci netif_carrier_on(info->netdev); 198562306a36Sopenharmony_ci else 198662306a36Sopenharmony_ci netif_carrier_off(info->netdev); 198762306a36Sopenharmony_ci } 198862306a36Sopenharmony_ci#endif 198962306a36Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 199062306a36Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 199162306a36Sopenharmony_ci info->pending_bh |= BH_STATUS; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci if (tty_port_check_carrier(&info->port)) { 199462306a36Sopenharmony_ci if (info->signals & SerialSignal_DCD) 199562306a36Sopenharmony_ci wake_up_interruptible(&info->port.open_wait); 199662306a36Sopenharmony_ci else { 199762306a36Sopenharmony_ci if (info->port.tty) 199862306a36Sopenharmony_ci tty_hangup(info->port.tty); 199962306a36Sopenharmony_ci } 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci} 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_cistatic void ri_change(struct slgt_info *info, unsigned short status) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci if (status & BIT0) { 200662306a36Sopenharmony_ci info->signals |= SerialSignal_RI; 200762306a36Sopenharmony_ci info->input_signal_events.ri_up++; 200862306a36Sopenharmony_ci } else { 200962306a36Sopenharmony_ci info->signals &= ~SerialSignal_RI; 201062306a36Sopenharmony_ci info->input_signal_events.ri_down++; 201162306a36Sopenharmony_ci } 201262306a36Sopenharmony_ci DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals)); 201362306a36Sopenharmony_ci if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { 201462306a36Sopenharmony_ci slgt_irq_off(info, IRQ_RI); 201562306a36Sopenharmony_ci return; 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci info->icount.rng++; 201862306a36Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 201962306a36Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 202062306a36Sopenharmony_ci info->pending_bh |= BH_STATUS; 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_cistatic void isr_rxdata(struct slgt_info *info) 202462306a36Sopenharmony_ci{ 202562306a36Sopenharmony_ci unsigned int count = info->rbuf_fill_count; 202662306a36Sopenharmony_ci unsigned int i = info->rbuf_fill_index; 202762306a36Sopenharmony_ci unsigned short reg; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci while (rd_reg16(info, SSR) & IRQ_RXDATA) { 203062306a36Sopenharmony_ci reg = rd_reg16(info, RDR); 203162306a36Sopenharmony_ci DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg)); 203262306a36Sopenharmony_ci if (desc_complete(info->rbufs[i])) { 203362306a36Sopenharmony_ci /* all buffers full */ 203462306a36Sopenharmony_ci rx_stop(info); 203562306a36Sopenharmony_ci info->rx_restart = true; 203662306a36Sopenharmony_ci continue; 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci info->rbufs[i].buf[count++] = (unsigned char)reg; 203962306a36Sopenharmony_ci /* async mode saves status byte to buffer for each data byte */ 204062306a36Sopenharmony_ci if (info->params.mode == MGSL_MODE_ASYNC) 204162306a36Sopenharmony_ci info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8); 204262306a36Sopenharmony_ci if (count == info->rbuf_fill_level || (reg & BIT10)) { 204362306a36Sopenharmony_ci /* buffer full or end of frame */ 204462306a36Sopenharmony_ci set_desc_count(info->rbufs[i], count); 204562306a36Sopenharmony_ci set_desc_status(info->rbufs[i], BIT15 | (reg >> 8)); 204662306a36Sopenharmony_ci info->rbuf_fill_count = count = 0; 204762306a36Sopenharmony_ci if (++i == info->rbuf_count) 204862306a36Sopenharmony_ci i = 0; 204962306a36Sopenharmony_ci info->pending_bh |= BH_RECEIVE; 205062306a36Sopenharmony_ci } 205162306a36Sopenharmony_ci } 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci info->rbuf_fill_index = i; 205462306a36Sopenharmony_ci info->rbuf_fill_count = count; 205562306a36Sopenharmony_ci} 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_cistatic void isr_serial(struct slgt_info *info) 205862306a36Sopenharmony_ci{ 205962306a36Sopenharmony_ci unsigned short status = rd_reg16(info, SSR); 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci DBGISR(("%s isr_serial status=%04X\n", info->device_name, status)); 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci wr_reg16(info, SSR, status); /* clear pending */ 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci info->irq_occurred = true; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (info->params.mode == MGSL_MODE_ASYNC) { 206862306a36Sopenharmony_ci if (status & IRQ_TXIDLE) { 206962306a36Sopenharmony_ci if (info->tx_active) 207062306a36Sopenharmony_ci isr_txeom(info, status); 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci if (info->rx_pio && (status & IRQ_RXDATA)) 207362306a36Sopenharmony_ci isr_rxdata(info); 207462306a36Sopenharmony_ci if ((status & IRQ_RXBREAK) && (status & RXBREAK)) { 207562306a36Sopenharmony_ci info->icount.brk++; 207662306a36Sopenharmony_ci /* process break detection if tty control allows */ 207762306a36Sopenharmony_ci if (info->port.tty) { 207862306a36Sopenharmony_ci if (!(status & info->ignore_status_mask)) { 207962306a36Sopenharmony_ci if (info->read_status_mask & MASK_BREAK) { 208062306a36Sopenharmony_ci tty_insert_flip_char(&info->port, 0, TTY_BREAK); 208162306a36Sopenharmony_ci if (info->port.flags & ASYNC_SAK) 208262306a36Sopenharmony_ci do_SAK(info->port.tty); 208362306a36Sopenharmony_ci } 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci } 208662306a36Sopenharmony_ci } 208762306a36Sopenharmony_ci } else { 208862306a36Sopenharmony_ci if (status & (IRQ_TXIDLE + IRQ_TXUNDER)) 208962306a36Sopenharmony_ci isr_txeom(info, status); 209062306a36Sopenharmony_ci if (info->rx_pio && (status & IRQ_RXDATA)) 209162306a36Sopenharmony_ci isr_rxdata(info); 209262306a36Sopenharmony_ci if (status & IRQ_RXIDLE) { 209362306a36Sopenharmony_ci if (status & RXIDLE) 209462306a36Sopenharmony_ci info->icount.rxidle++; 209562306a36Sopenharmony_ci else 209662306a36Sopenharmony_ci info->icount.exithunt++; 209762306a36Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci if (status & IRQ_RXOVER) 210162306a36Sopenharmony_ci rx_start(info); 210262306a36Sopenharmony_ci } 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci if (status & IRQ_DSR) 210562306a36Sopenharmony_ci dsr_change(info, status); 210662306a36Sopenharmony_ci if (status & IRQ_CTS) 210762306a36Sopenharmony_ci cts_change(info, status); 210862306a36Sopenharmony_ci if (status & IRQ_DCD) 210962306a36Sopenharmony_ci dcd_change(info, status); 211062306a36Sopenharmony_ci if (status & IRQ_RI) 211162306a36Sopenharmony_ci ri_change(info, status); 211262306a36Sopenharmony_ci} 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_cistatic void isr_rdma(struct slgt_info *info) 211562306a36Sopenharmony_ci{ 211662306a36Sopenharmony_ci unsigned int status = rd_reg32(info, RDCSR); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status)); 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci /* RDCSR (rx DMA control/status) 212162306a36Sopenharmony_ci * 212262306a36Sopenharmony_ci * 31..07 reserved 212362306a36Sopenharmony_ci * 06 save status byte to DMA buffer 212462306a36Sopenharmony_ci * 05 error 212562306a36Sopenharmony_ci * 04 eol (end of list) 212662306a36Sopenharmony_ci * 03 eob (end of buffer) 212762306a36Sopenharmony_ci * 02 IRQ enable 212862306a36Sopenharmony_ci * 01 reset 212962306a36Sopenharmony_ci * 00 enable 213062306a36Sopenharmony_ci */ 213162306a36Sopenharmony_ci wr_reg32(info, RDCSR, status); /* clear pending */ 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci if (status & (BIT5 + BIT4)) { 213462306a36Sopenharmony_ci DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name)); 213562306a36Sopenharmony_ci info->rx_restart = true; 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci info->pending_bh |= BH_RECEIVE; 213862306a36Sopenharmony_ci} 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_cistatic void isr_tdma(struct slgt_info *info) 214162306a36Sopenharmony_ci{ 214262306a36Sopenharmony_ci unsigned int status = rd_reg32(info, TDCSR); 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status)); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci /* TDCSR (tx DMA control/status) 214762306a36Sopenharmony_ci * 214862306a36Sopenharmony_ci * 31..06 reserved 214962306a36Sopenharmony_ci * 05 error 215062306a36Sopenharmony_ci * 04 eol (end of list) 215162306a36Sopenharmony_ci * 03 eob (end of buffer) 215262306a36Sopenharmony_ci * 02 IRQ enable 215362306a36Sopenharmony_ci * 01 reset 215462306a36Sopenharmony_ci * 00 enable 215562306a36Sopenharmony_ci */ 215662306a36Sopenharmony_ci wr_reg32(info, TDCSR, status); /* clear pending */ 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci if (status & (BIT5 + BIT4 + BIT3)) { 215962306a36Sopenharmony_ci // another transmit buffer has completed 216062306a36Sopenharmony_ci // run bottom half to get more send data from user 216162306a36Sopenharmony_ci info->pending_bh |= BH_TRANSMIT; 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci} 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci/* 216662306a36Sopenharmony_ci * return true if there are unsent tx DMA buffers, otherwise false 216762306a36Sopenharmony_ci * 216862306a36Sopenharmony_ci * if there are unsent buffers then info->tbuf_start 216962306a36Sopenharmony_ci * is set to index of first unsent buffer 217062306a36Sopenharmony_ci */ 217162306a36Sopenharmony_cistatic bool unsent_tbufs(struct slgt_info *info) 217262306a36Sopenharmony_ci{ 217362306a36Sopenharmony_ci unsigned int i = info->tbuf_current; 217462306a36Sopenharmony_ci bool rc = false; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci /* 217762306a36Sopenharmony_ci * search backwards from last loaded buffer (precedes tbuf_current) 217862306a36Sopenharmony_ci * for first unsent buffer (desc_count > 0) 217962306a36Sopenharmony_ci */ 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci do { 218262306a36Sopenharmony_ci if (i) 218362306a36Sopenharmony_ci i--; 218462306a36Sopenharmony_ci else 218562306a36Sopenharmony_ci i = info->tbuf_count - 1; 218662306a36Sopenharmony_ci if (!desc_count(info->tbufs[i])) 218762306a36Sopenharmony_ci break; 218862306a36Sopenharmony_ci info->tbuf_start = i; 218962306a36Sopenharmony_ci rc = true; 219062306a36Sopenharmony_ci } while (i != info->tbuf_current); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci return rc; 219362306a36Sopenharmony_ci} 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_cistatic void isr_txeom(struct slgt_info *info, unsigned short status) 219662306a36Sopenharmony_ci{ 219762306a36Sopenharmony_ci DBGISR(("%s txeom status=%04x\n", info->device_name, status)); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); 220062306a36Sopenharmony_ci tdma_reset(info); 220162306a36Sopenharmony_ci if (status & IRQ_TXUNDER) { 220262306a36Sopenharmony_ci unsigned short val = rd_reg16(info, TCR); 220362306a36Sopenharmony_ci wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ 220462306a36Sopenharmony_ci wr_reg16(info, TCR, val); /* clear reset bit */ 220562306a36Sopenharmony_ci } 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci if (info->tx_active) { 220862306a36Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 220962306a36Sopenharmony_ci if (status & IRQ_TXUNDER) 221062306a36Sopenharmony_ci info->icount.txunder++; 221162306a36Sopenharmony_ci else if (status & IRQ_TXIDLE) 221262306a36Sopenharmony_ci info->icount.txok++; 221362306a36Sopenharmony_ci } 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci if (unsent_tbufs(info)) { 221662306a36Sopenharmony_ci tx_start(info); 221762306a36Sopenharmony_ci update_tx_timer(info); 221862306a36Sopenharmony_ci return; 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci info->tx_active = false; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci del_timer(&info->tx_timer); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) { 222562306a36Sopenharmony_ci info->signals &= ~SerialSignal_RTS; 222662306a36Sopenharmony_ci info->drop_rts_on_tx_done = false; 222762306a36Sopenharmony_ci set_gtsignals(info); 222862306a36Sopenharmony_ci } 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 223162306a36Sopenharmony_ci if (info->netcount) 223262306a36Sopenharmony_ci hdlcdev_tx_done(info); 223362306a36Sopenharmony_ci else 223462306a36Sopenharmony_ci#endif 223562306a36Sopenharmony_ci { 223662306a36Sopenharmony_ci if (info->port.tty && (info->port.tty->flow.stopped || info->port.tty->hw_stopped)) { 223762306a36Sopenharmony_ci tx_stop(info); 223862306a36Sopenharmony_ci return; 223962306a36Sopenharmony_ci } 224062306a36Sopenharmony_ci info->pending_bh |= BH_TRANSMIT; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci} 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_cistatic void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state) 224662306a36Sopenharmony_ci{ 224762306a36Sopenharmony_ci struct cond_wait *w, *prev; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci /* wake processes waiting for specific transitions */ 225062306a36Sopenharmony_ci for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) { 225162306a36Sopenharmony_ci if (w->data & changed) { 225262306a36Sopenharmony_ci w->data = state; 225362306a36Sopenharmony_ci wake_up_interruptible(&w->q); 225462306a36Sopenharmony_ci if (prev != NULL) 225562306a36Sopenharmony_ci prev->next = w->next; 225662306a36Sopenharmony_ci else 225762306a36Sopenharmony_ci info->gpio_wait_q = w->next; 225862306a36Sopenharmony_ci } else 225962306a36Sopenharmony_ci prev = w; 226062306a36Sopenharmony_ci } 226162306a36Sopenharmony_ci} 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci/* interrupt service routine 226462306a36Sopenharmony_ci * 226562306a36Sopenharmony_ci * irq interrupt number 226662306a36Sopenharmony_ci * dev_id device ID supplied during interrupt registration 226762306a36Sopenharmony_ci */ 226862306a36Sopenharmony_cistatic irqreturn_t slgt_interrupt(int dummy, void *dev_id) 226962306a36Sopenharmony_ci{ 227062306a36Sopenharmony_ci struct slgt_info *info = dev_id; 227162306a36Sopenharmony_ci unsigned int gsr; 227262306a36Sopenharmony_ci unsigned int i; 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level)); 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci while((gsr = rd_reg32(info, GSR) & 0xffffff00)) { 227762306a36Sopenharmony_ci DBGISR(("%s gsr=%08x\n", info->device_name, gsr)); 227862306a36Sopenharmony_ci info->irq_occurred = true; 227962306a36Sopenharmony_ci for(i=0; i < info->port_count ; i++) { 228062306a36Sopenharmony_ci if (info->port_array[i] == NULL) 228162306a36Sopenharmony_ci continue; 228262306a36Sopenharmony_ci spin_lock(&info->port_array[i]->lock); 228362306a36Sopenharmony_ci if (gsr & (BIT8 << i)) 228462306a36Sopenharmony_ci isr_serial(info->port_array[i]); 228562306a36Sopenharmony_ci if (gsr & (BIT16 << (i*2))) 228662306a36Sopenharmony_ci isr_rdma(info->port_array[i]); 228762306a36Sopenharmony_ci if (gsr & (BIT17 << (i*2))) 228862306a36Sopenharmony_ci isr_tdma(info->port_array[i]); 228962306a36Sopenharmony_ci spin_unlock(&info->port_array[i]->lock); 229062306a36Sopenharmony_ci } 229162306a36Sopenharmony_ci } 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci if (info->gpio_present) { 229462306a36Sopenharmony_ci unsigned int state; 229562306a36Sopenharmony_ci unsigned int changed; 229662306a36Sopenharmony_ci spin_lock(&info->lock); 229762306a36Sopenharmony_ci while ((changed = rd_reg32(info, IOSR)) != 0) { 229862306a36Sopenharmony_ci DBGISR(("%s iosr=%08x\n", info->device_name, changed)); 229962306a36Sopenharmony_ci /* read latched state of GPIO signals */ 230062306a36Sopenharmony_ci state = rd_reg32(info, IOVR); 230162306a36Sopenharmony_ci /* clear pending GPIO interrupt bits */ 230262306a36Sopenharmony_ci wr_reg32(info, IOSR, changed); 230362306a36Sopenharmony_ci for (i=0 ; i < info->port_count ; i++) { 230462306a36Sopenharmony_ci if (info->port_array[i] != NULL) 230562306a36Sopenharmony_ci isr_gpio(info->port_array[i], changed, state); 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci } 230862306a36Sopenharmony_ci spin_unlock(&info->lock); 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci for(i=0; i < info->port_count ; i++) { 231262306a36Sopenharmony_ci struct slgt_info *port = info->port_array[i]; 231362306a36Sopenharmony_ci if (port == NULL) 231462306a36Sopenharmony_ci continue; 231562306a36Sopenharmony_ci spin_lock(&port->lock); 231662306a36Sopenharmony_ci if ((port->port.count || port->netcount) && 231762306a36Sopenharmony_ci port->pending_bh && !port->bh_running && 231862306a36Sopenharmony_ci !port->bh_requested) { 231962306a36Sopenharmony_ci DBGISR(("%s bh queued\n", port->device_name)); 232062306a36Sopenharmony_ci schedule_work(&port->task); 232162306a36Sopenharmony_ci port->bh_requested = true; 232262306a36Sopenharmony_ci } 232362306a36Sopenharmony_ci spin_unlock(&port->lock); 232462306a36Sopenharmony_ci } 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level)); 232762306a36Sopenharmony_ci return IRQ_HANDLED; 232862306a36Sopenharmony_ci} 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_cistatic int startup(struct slgt_info *info) 233162306a36Sopenharmony_ci{ 233262306a36Sopenharmony_ci DBGINFO(("%s startup\n", info->device_name)); 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci if (tty_port_initialized(&info->port)) 233562306a36Sopenharmony_ci return 0; 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci if (!info->tx_buf) { 233862306a36Sopenharmony_ci info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); 233962306a36Sopenharmony_ci if (!info->tx_buf) { 234062306a36Sopenharmony_ci DBGERR(("%s can't allocate tx buffer\n", info->device_name)); 234162306a36Sopenharmony_ci return -ENOMEM; 234262306a36Sopenharmony_ci } 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci info->pending_bh = 0; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci memset(&info->icount, 0, sizeof(info->icount)); 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci /* program hardware for current parameters */ 235062306a36Sopenharmony_ci change_params(info); 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci if (info->port.tty) 235362306a36Sopenharmony_ci clear_bit(TTY_IO_ERROR, &info->port.tty->flags); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci tty_port_set_initialized(&info->port, true); 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci return 0; 235862306a36Sopenharmony_ci} 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci/* 236162306a36Sopenharmony_ci * called by close() and hangup() to shutdown hardware 236262306a36Sopenharmony_ci */ 236362306a36Sopenharmony_cistatic void shutdown(struct slgt_info *info) 236462306a36Sopenharmony_ci{ 236562306a36Sopenharmony_ci unsigned long flags; 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci if (!tty_port_initialized(&info->port)) 236862306a36Sopenharmony_ci return; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci DBGINFO(("%s shutdown\n", info->device_name)); 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci /* clear status wait queue because status changes */ 237362306a36Sopenharmony_ci /* can't happen after shutting down the hardware */ 237462306a36Sopenharmony_ci wake_up_interruptible(&info->status_event_wait_q); 237562306a36Sopenharmony_ci wake_up_interruptible(&info->event_wait_q); 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci del_timer_sync(&info->tx_timer); 237862306a36Sopenharmony_ci del_timer_sync(&info->rx_timer); 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci kfree(info->tx_buf); 238162306a36Sopenharmony_ci info->tx_buf = NULL; 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci tx_stop(info); 238662306a36Sopenharmony_ci rx_stop(info); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { 239162306a36Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 239262306a36Sopenharmony_ci set_gtsignals(info); 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci flush_cond_wait(&info->gpio_wait_q); 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci if (info->port.tty) 240062306a36Sopenharmony_ci set_bit(TTY_IO_ERROR, &info->port.tty->flags); 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci tty_port_set_initialized(&info->port, false); 240362306a36Sopenharmony_ci} 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_cistatic void program_hw(struct slgt_info *info) 240662306a36Sopenharmony_ci{ 240762306a36Sopenharmony_ci unsigned long flags; 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci rx_stop(info); 241262306a36Sopenharmony_ci tx_stop(info); 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC || 241562306a36Sopenharmony_ci info->netcount) 241662306a36Sopenharmony_ci sync_mode(info); 241762306a36Sopenharmony_ci else 241862306a36Sopenharmony_ci async_mode(info); 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci set_gtsignals(info); 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci info->dcd_chkcount = 0; 242362306a36Sopenharmony_ci info->cts_chkcount = 0; 242462306a36Sopenharmony_ci info->ri_chkcount = 0; 242562306a36Sopenharmony_ci info->dsr_chkcount = 0; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI); 242862306a36Sopenharmony_ci get_gtsignals(info); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci if (info->netcount || 243162306a36Sopenharmony_ci (info->port.tty && info->port.tty->termios.c_cflag & CREAD)) 243262306a36Sopenharmony_ci rx_start(info); 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 243562306a36Sopenharmony_ci} 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci/* 243862306a36Sopenharmony_ci * reconfigure adapter based on new parameters 243962306a36Sopenharmony_ci */ 244062306a36Sopenharmony_cistatic void change_params(struct slgt_info *info) 244162306a36Sopenharmony_ci{ 244262306a36Sopenharmony_ci unsigned cflag; 244362306a36Sopenharmony_ci int bits_per_char; 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci if (!info->port.tty) 244662306a36Sopenharmony_ci return; 244762306a36Sopenharmony_ci DBGINFO(("%s change_params\n", info->device_name)); 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci cflag = info->port.tty->termios.c_cflag; 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci /* if B0 rate (hangup) specified then negate RTS and DTR */ 245262306a36Sopenharmony_ci /* otherwise assert RTS and DTR */ 245362306a36Sopenharmony_ci if (cflag & CBAUD) 245462306a36Sopenharmony_ci info->signals |= SerialSignal_RTS | SerialSignal_DTR; 245562306a36Sopenharmony_ci else 245662306a36Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci /* byte size and parity */ 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci info->params.data_bits = tty_get_char_size(cflag); 246162306a36Sopenharmony_ci info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci if (cflag & PARENB) 246462306a36Sopenharmony_ci info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN; 246562306a36Sopenharmony_ci else 246662306a36Sopenharmony_ci info->params.parity = ASYNC_PARITY_NONE; 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci /* calculate number of jiffies to transmit a full 246962306a36Sopenharmony_ci * FIFO (32 bytes) at specified data rate 247062306a36Sopenharmony_ci */ 247162306a36Sopenharmony_ci bits_per_char = info->params.data_bits + 247262306a36Sopenharmony_ci info->params.stop_bits + 1; 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci info->params.data_rate = tty_get_baud_rate(info->port.tty); 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci if (info->params.data_rate) { 247762306a36Sopenharmony_ci info->timeout = (32*HZ*bits_per_char) / 247862306a36Sopenharmony_ci info->params.data_rate; 247962306a36Sopenharmony_ci } 248062306a36Sopenharmony_ci info->timeout += HZ/50; /* Add .02 seconds of slop */ 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); 248362306a36Sopenharmony_ci tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci /* process tty input control flags */ 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci info->read_status_mask = IRQ_RXOVER; 248862306a36Sopenharmony_ci if (I_INPCK(info->port.tty)) 248962306a36Sopenharmony_ci info->read_status_mask |= MASK_PARITY | MASK_FRAMING; 249062306a36Sopenharmony_ci if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) 249162306a36Sopenharmony_ci info->read_status_mask |= MASK_BREAK; 249262306a36Sopenharmony_ci if (I_IGNPAR(info->port.tty)) 249362306a36Sopenharmony_ci info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING; 249462306a36Sopenharmony_ci if (I_IGNBRK(info->port.tty)) { 249562306a36Sopenharmony_ci info->ignore_status_mask |= MASK_BREAK; 249662306a36Sopenharmony_ci /* If ignoring parity and break indicators, ignore 249762306a36Sopenharmony_ci * overruns too. (For real raw support). 249862306a36Sopenharmony_ci */ 249962306a36Sopenharmony_ci if (I_IGNPAR(info->port.tty)) 250062306a36Sopenharmony_ci info->ignore_status_mask |= MASK_OVERRUN; 250162306a36Sopenharmony_ci } 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci program_hw(info); 250462306a36Sopenharmony_ci} 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_cistatic int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount) 250762306a36Sopenharmony_ci{ 250862306a36Sopenharmony_ci DBGINFO(("%s get_stats\n", info->device_name)); 250962306a36Sopenharmony_ci if (!user_icount) { 251062306a36Sopenharmony_ci memset(&info->icount, 0, sizeof(info->icount)); 251162306a36Sopenharmony_ci } else { 251262306a36Sopenharmony_ci if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount))) 251362306a36Sopenharmony_ci return -EFAULT; 251462306a36Sopenharmony_ci } 251562306a36Sopenharmony_ci return 0; 251662306a36Sopenharmony_ci} 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_cistatic int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params) 251962306a36Sopenharmony_ci{ 252062306a36Sopenharmony_ci DBGINFO(("%s get_params\n", info->device_name)); 252162306a36Sopenharmony_ci if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS))) 252262306a36Sopenharmony_ci return -EFAULT; 252362306a36Sopenharmony_ci return 0; 252462306a36Sopenharmony_ci} 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_cistatic int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params) 252762306a36Sopenharmony_ci{ 252862306a36Sopenharmony_ci unsigned long flags; 252962306a36Sopenharmony_ci MGSL_PARAMS tmp_params; 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci DBGINFO(("%s set_params\n", info->device_name)); 253262306a36Sopenharmony_ci if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS))) 253362306a36Sopenharmony_ci return -EFAULT; 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 253662306a36Sopenharmony_ci if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) 253762306a36Sopenharmony_ci info->base_clock = tmp_params.clock_speed; 253862306a36Sopenharmony_ci else 253962306a36Sopenharmony_ci memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS)); 254062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci program_hw(info); 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci return 0; 254562306a36Sopenharmony_ci} 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_cistatic int get_txidle(struct slgt_info *info, int __user *idle_mode) 254862306a36Sopenharmony_ci{ 254962306a36Sopenharmony_ci DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode)); 255062306a36Sopenharmony_ci if (put_user(info->idle_mode, idle_mode)) 255162306a36Sopenharmony_ci return -EFAULT; 255262306a36Sopenharmony_ci return 0; 255362306a36Sopenharmony_ci} 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_cistatic int set_txidle(struct slgt_info *info, int idle_mode) 255662306a36Sopenharmony_ci{ 255762306a36Sopenharmony_ci unsigned long flags; 255862306a36Sopenharmony_ci DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode)); 255962306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 256062306a36Sopenharmony_ci info->idle_mode = idle_mode; 256162306a36Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) 256262306a36Sopenharmony_ci tx_set_idle(info); 256362306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 256462306a36Sopenharmony_ci return 0; 256562306a36Sopenharmony_ci} 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_cistatic int tx_enable(struct slgt_info *info, int enable) 256862306a36Sopenharmony_ci{ 256962306a36Sopenharmony_ci unsigned long flags; 257062306a36Sopenharmony_ci DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable)); 257162306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 257262306a36Sopenharmony_ci if (enable) { 257362306a36Sopenharmony_ci if (!info->tx_enabled) 257462306a36Sopenharmony_ci tx_start(info); 257562306a36Sopenharmony_ci } else { 257662306a36Sopenharmony_ci if (info->tx_enabled) 257762306a36Sopenharmony_ci tx_stop(info); 257862306a36Sopenharmony_ci } 257962306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 258062306a36Sopenharmony_ci return 0; 258162306a36Sopenharmony_ci} 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci/* 258462306a36Sopenharmony_ci * abort transmit HDLC frame 258562306a36Sopenharmony_ci */ 258662306a36Sopenharmony_cistatic int tx_abort(struct slgt_info *info) 258762306a36Sopenharmony_ci{ 258862306a36Sopenharmony_ci unsigned long flags; 258962306a36Sopenharmony_ci DBGINFO(("%s tx_abort\n", info->device_name)); 259062306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 259162306a36Sopenharmony_ci tdma_reset(info); 259262306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 259362306a36Sopenharmony_ci return 0; 259462306a36Sopenharmony_ci} 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_cistatic int rx_enable(struct slgt_info *info, int enable) 259762306a36Sopenharmony_ci{ 259862306a36Sopenharmony_ci unsigned long flags; 259962306a36Sopenharmony_ci unsigned int rbuf_fill_level; 260062306a36Sopenharmony_ci DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable)); 260162306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 260262306a36Sopenharmony_ci /* 260362306a36Sopenharmony_ci * enable[31..16] = receive DMA buffer fill level 260462306a36Sopenharmony_ci * 0 = noop (leave fill level unchanged) 260562306a36Sopenharmony_ci * fill level must be multiple of 4 and <= buffer size 260662306a36Sopenharmony_ci */ 260762306a36Sopenharmony_ci rbuf_fill_level = ((unsigned int)enable) >> 16; 260862306a36Sopenharmony_ci if (rbuf_fill_level) { 260962306a36Sopenharmony_ci if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) { 261062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 261162306a36Sopenharmony_ci return -EINVAL; 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci info->rbuf_fill_level = rbuf_fill_level; 261462306a36Sopenharmony_ci if (rbuf_fill_level < 128) 261562306a36Sopenharmony_ci info->rx_pio = 1; /* PIO mode */ 261662306a36Sopenharmony_ci else 261762306a36Sopenharmony_ci info->rx_pio = 0; /* DMA mode */ 261862306a36Sopenharmony_ci rx_stop(info); /* restart receiver to use new fill level */ 261962306a36Sopenharmony_ci } 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci /* 262262306a36Sopenharmony_ci * enable[1..0] = receiver enable command 262362306a36Sopenharmony_ci * 0 = disable 262462306a36Sopenharmony_ci * 1 = enable 262562306a36Sopenharmony_ci * 2 = enable or force hunt mode if already enabled 262662306a36Sopenharmony_ci */ 262762306a36Sopenharmony_ci enable &= 3; 262862306a36Sopenharmony_ci if (enable) { 262962306a36Sopenharmony_ci if (!info->rx_enabled) 263062306a36Sopenharmony_ci rx_start(info); 263162306a36Sopenharmony_ci else if (enable == 2) { 263262306a36Sopenharmony_ci /* force hunt mode (write 1 to RCR[3]) */ 263362306a36Sopenharmony_ci wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3); 263462306a36Sopenharmony_ci } 263562306a36Sopenharmony_ci } else { 263662306a36Sopenharmony_ci if (info->rx_enabled) 263762306a36Sopenharmony_ci rx_stop(info); 263862306a36Sopenharmony_ci } 263962306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 264062306a36Sopenharmony_ci return 0; 264162306a36Sopenharmony_ci} 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci/* 264462306a36Sopenharmony_ci * wait for specified event to occur 264562306a36Sopenharmony_ci */ 264662306a36Sopenharmony_cistatic int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr) 264762306a36Sopenharmony_ci{ 264862306a36Sopenharmony_ci unsigned long flags; 264962306a36Sopenharmony_ci int s; 265062306a36Sopenharmony_ci int rc=0; 265162306a36Sopenharmony_ci struct mgsl_icount cprev, cnow; 265262306a36Sopenharmony_ci int events; 265362306a36Sopenharmony_ci int mask; 265462306a36Sopenharmony_ci struct _input_signal_events oldsigs, newsigs; 265562306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci if (get_user(mask, mask_ptr)) 265862306a36Sopenharmony_ci return -EFAULT; 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask)); 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci /* return immediately if state matches requested events */ 266562306a36Sopenharmony_ci get_gtsignals(info); 266662306a36Sopenharmony_ci s = info->signals; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci events = mask & 266962306a36Sopenharmony_ci ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + 267062306a36Sopenharmony_ci ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + 267162306a36Sopenharmony_ci ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + 267262306a36Sopenharmony_ci ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); 267362306a36Sopenharmony_ci if (events) { 267462306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 267562306a36Sopenharmony_ci goto exit; 267662306a36Sopenharmony_ci } 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci /* save current irq counts */ 267962306a36Sopenharmony_ci cprev = info->icount; 268062306a36Sopenharmony_ci oldsigs = info->input_signal_events; 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci /* enable hunt and idle irqs if needed */ 268362306a36Sopenharmony_ci if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) { 268462306a36Sopenharmony_ci unsigned short val = rd_reg16(info, SCR); 268562306a36Sopenharmony_ci if (!(val & IRQ_RXIDLE)) 268662306a36Sopenharmony_ci wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE)); 268762306a36Sopenharmony_ci } 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 269062306a36Sopenharmony_ci add_wait_queue(&info->event_wait_q, &wait); 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci for(;;) { 269562306a36Sopenharmony_ci schedule(); 269662306a36Sopenharmony_ci if (signal_pending(current)) { 269762306a36Sopenharmony_ci rc = -ERESTARTSYS; 269862306a36Sopenharmony_ci break; 269962306a36Sopenharmony_ci } 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci /* get current irq counts */ 270262306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 270362306a36Sopenharmony_ci cnow = info->icount; 270462306a36Sopenharmony_ci newsigs = info->input_signal_events; 270562306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 270662306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci /* if no change, wait aborted for some reason */ 270962306a36Sopenharmony_ci if (newsigs.dsr_up == oldsigs.dsr_up && 271062306a36Sopenharmony_ci newsigs.dsr_down == oldsigs.dsr_down && 271162306a36Sopenharmony_ci newsigs.dcd_up == oldsigs.dcd_up && 271262306a36Sopenharmony_ci newsigs.dcd_down == oldsigs.dcd_down && 271362306a36Sopenharmony_ci newsigs.cts_up == oldsigs.cts_up && 271462306a36Sopenharmony_ci newsigs.cts_down == oldsigs.cts_down && 271562306a36Sopenharmony_ci newsigs.ri_up == oldsigs.ri_up && 271662306a36Sopenharmony_ci newsigs.ri_down == oldsigs.ri_down && 271762306a36Sopenharmony_ci cnow.exithunt == cprev.exithunt && 271862306a36Sopenharmony_ci cnow.rxidle == cprev.rxidle) { 271962306a36Sopenharmony_ci rc = -EIO; 272062306a36Sopenharmony_ci break; 272162306a36Sopenharmony_ci } 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci events = mask & 272462306a36Sopenharmony_ci ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + 272562306a36Sopenharmony_ci (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + 272662306a36Sopenharmony_ci (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + 272762306a36Sopenharmony_ci (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + 272862306a36Sopenharmony_ci (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + 272962306a36Sopenharmony_ci (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + 273062306a36Sopenharmony_ci (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + 273162306a36Sopenharmony_ci (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + 273262306a36Sopenharmony_ci (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + 273362306a36Sopenharmony_ci (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); 273462306a36Sopenharmony_ci if (events) 273562306a36Sopenharmony_ci break; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci cprev = cnow; 273862306a36Sopenharmony_ci oldsigs = newsigs; 273962306a36Sopenharmony_ci } 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci remove_wait_queue(&info->event_wait_q, &wait); 274262306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { 274662306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 274762306a36Sopenharmony_ci if (!waitqueue_active(&info->event_wait_q)) { 274862306a36Sopenharmony_ci /* disable enable exit hunt mode/idle rcvd IRQs */ 274962306a36Sopenharmony_ci wr_reg16(info, SCR, 275062306a36Sopenharmony_ci (unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE)); 275162306a36Sopenharmony_ci } 275262306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 275362306a36Sopenharmony_ci } 275462306a36Sopenharmony_ciexit: 275562306a36Sopenharmony_ci if (rc == 0) 275662306a36Sopenharmony_ci rc = put_user(events, mask_ptr); 275762306a36Sopenharmony_ci return rc; 275862306a36Sopenharmony_ci} 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_cistatic int get_interface(struct slgt_info *info, int __user *if_mode) 276162306a36Sopenharmony_ci{ 276262306a36Sopenharmony_ci DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode)); 276362306a36Sopenharmony_ci if (put_user(info->if_mode, if_mode)) 276462306a36Sopenharmony_ci return -EFAULT; 276562306a36Sopenharmony_ci return 0; 276662306a36Sopenharmony_ci} 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_cistatic int set_interface(struct slgt_info *info, int if_mode) 276962306a36Sopenharmony_ci{ 277062306a36Sopenharmony_ci unsigned long flags; 277162306a36Sopenharmony_ci unsigned short val; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode)); 277462306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 277562306a36Sopenharmony_ci info->if_mode = if_mode; 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci msc_set_vcr(info); 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci /* TCR (tx control) 07 1=RTS driver control */ 278062306a36Sopenharmony_ci val = rd_reg16(info, TCR); 278162306a36Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_RTS_EN) 278262306a36Sopenharmony_ci val |= BIT7; 278362306a36Sopenharmony_ci else 278462306a36Sopenharmony_ci val &= ~BIT7; 278562306a36Sopenharmony_ci wr_reg16(info, TCR, val); 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 278862306a36Sopenharmony_ci return 0; 278962306a36Sopenharmony_ci} 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_cistatic int get_xsync(struct slgt_info *info, int __user *xsync) 279262306a36Sopenharmony_ci{ 279362306a36Sopenharmony_ci DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync)); 279462306a36Sopenharmony_ci if (put_user(info->xsync, xsync)) 279562306a36Sopenharmony_ci return -EFAULT; 279662306a36Sopenharmony_ci return 0; 279762306a36Sopenharmony_ci} 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci/* 280062306a36Sopenharmony_ci * set extended sync pattern (1 to 4 bytes) for extended sync mode 280162306a36Sopenharmony_ci * 280262306a36Sopenharmony_ci * sync pattern is contained in least significant bytes of value 280362306a36Sopenharmony_ci * most significant byte of sync pattern is oldest (1st sent/detected) 280462306a36Sopenharmony_ci */ 280562306a36Sopenharmony_cistatic int set_xsync(struct slgt_info *info, int xsync) 280662306a36Sopenharmony_ci{ 280762306a36Sopenharmony_ci unsigned long flags; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync)); 281062306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 281162306a36Sopenharmony_ci info->xsync = xsync; 281262306a36Sopenharmony_ci wr_reg32(info, XSR, xsync); 281362306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 281462306a36Sopenharmony_ci return 0; 281562306a36Sopenharmony_ci} 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_cistatic int get_xctrl(struct slgt_info *info, int __user *xctrl) 281862306a36Sopenharmony_ci{ 281962306a36Sopenharmony_ci DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl)); 282062306a36Sopenharmony_ci if (put_user(info->xctrl, xctrl)) 282162306a36Sopenharmony_ci return -EFAULT; 282262306a36Sopenharmony_ci return 0; 282362306a36Sopenharmony_ci} 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci/* 282662306a36Sopenharmony_ci * set extended control options 282762306a36Sopenharmony_ci * 282862306a36Sopenharmony_ci * xctrl[31:19] reserved, must be zero 282962306a36Sopenharmony_ci * xctrl[18:17] extended sync pattern length in bytes 283062306a36Sopenharmony_ci * 00 = 1 byte in xsr[7:0] 283162306a36Sopenharmony_ci * 01 = 2 bytes in xsr[15:0] 283262306a36Sopenharmony_ci * 10 = 3 bytes in xsr[23:0] 283362306a36Sopenharmony_ci * 11 = 4 bytes in xsr[31:0] 283462306a36Sopenharmony_ci * xctrl[16] 1 = enable terminal count, 0=disabled 283562306a36Sopenharmony_ci * xctrl[15:0] receive terminal count for fixed length packets 283662306a36Sopenharmony_ci * value is count minus one (0 = 1 byte packet) 283762306a36Sopenharmony_ci * when terminal count is reached, receiver 283862306a36Sopenharmony_ci * automatically returns to hunt mode and receive 283962306a36Sopenharmony_ci * FIFO contents are flushed to DMA buffers with 284062306a36Sopenharmony_ci * end of frame (EOF) status 284162306a36Sopenharmony_ci */ 284262306a36Sopenharmony_cistatic int set_xctrl(struct slgt_info *info, int xctrl) 284362306a36Sopenharmony_ci{ 284462306a36Sopenharmony_ci unsigned long flags; 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl)); 284762306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 284862306a36Sopenharmony_ci info->xctrl = xctrl; 284962306a36Sopenharmony_ci wr_reg32(info, XCR, xctrl); 285062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 285162306a36Sopenharmony_ci return 0; 285262306a36Sopenharmony_ci} 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci/* 285562306a36Sopenharmony_ci * set general purpose IO pin state and direction 285662306a36Sopenharmony_ci * 285762306a36Sopenharmony_ci * user_gpio fields: 285862306a36Sopenharmony_ci * state each bit indicates a pin state 285962306a36Sopenharmony_ci * smask set bit indicates pin state to set 286062306a36Sopenharmony_ci * dir each bit indicates a pin direction (0=input, 1=output) 286162306a36Sopenharmony_ci * dmask set bit indicates pin direction to set 286262306a36Sopenharmony_ci */ 286362306a36Sopenharmony_cistatic int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) 286462306a36Sopenharmony_ci{ 286562306a36Sopenharmony_ci unsigned long flags; 286662306a36Sopenharmony_ci struct gpio_desc gpio; 286762306a36Sopenharmony_ci __u32 data; 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci if (!info->gpio_present) 287062306a36Sopenharmony_ci return -EINVAL; 287162306a36Sopenharmony_ci if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) 287262306a36Sopenharmony_ci return -EFAULT; 287362306a36Sopenharmony_ci DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n", 287462306a36Sopenharmony_ci info->device_name, gpio.state, gpio.smask, 287562306a36Sopenharmony_ci gpio.dir, gpio.dmask)); 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci spin_lock_irqsave(&info->port_array[0]->lock, flags); 287862306a36Sopenharmony_ci if (gpio.dmask) { 287962306a36Sopenharmony_ci data = rd_reg32(info, IODR); 288062306a36Sopenharmony_ci data |= gpio.dmask & gpio.dir; 288162306a36Sopenharmony_ci data &= ~(gpio.dmask & ~gpio.dir); 288262306a36Sopenharmony_ci wr_reg32(info, IODR, data); 288362306a36Sopenharmony_ci } 288462306a36Sopenharmony_ci if (gpio.smask) { 288562306a36Sopenharmony_ci data = rd_reg32(info, IOVR); 288662306a36Sopenharmony_ci data |= gpio.smask & gpio.state; 288762306a36Sopenharmony_ci data &= ~(gpio.smask & ~gpio.state); 288862306a36Sopenharmony_ci wr_reg32(info, IOVR, data); 288962306a36Sopenharmony_ci } 289062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->port_array[0]->lock, flags); 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci return 0; 289362306a36Sopenharmony_ci} 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_ci/* 289662306a36Sopenharmony_ci * get general purpose IO pin state and direction 289762306a36Sopenharmony_ci */ 289862306a36Sopenharmony_cistatic int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) 289962306a36Sopenharmony_ci{ 290062306a36Sopenharmony_ci struct gpio_desc gpio; 290162306a36Sopenharmony_ci if (!info->gpio_present) 290262306a36Sopenharmony_ci return -EINVAL; 290362306a36Sopenharmony_ci gpio.state = rd_reg32(info, IOVR); 290462306a36Sopenharmony_ci gpio.smask = 0xffffffff; 290562306a36Sopenharmony_ci gpio.dir = rd_reg32(info, IODR); 290662306a36Sopenharmony_ci gpio.dmask = 0xffffffff; 290762306a36Sopenharmony_ci if (copy_to_user(user_gpio, &gpio, sizeof(gpio))) 290862306a36Sopenharmony_ci return -EFAULT; 290962306a36Sopenharmony_ci DBGINFO(("%s get_gpio state=%08x dir=%08x\n", 291062306a36Sopenharmony_ci info->device_name, gpio.state, gpio.dir)); 291162306a36Sopenharmony_ci return 0; 291262306a36Sopenharmony_ci} 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci/* 291562306a36Sopenharmony_ci * conditional wait facility 291662306a36Sopenharmony_ci */ 291762306a36Sopenharmony_cistatic void init_cond_wait(struct cond_wait *w, unsigned int data) 291862306a36Sopenharmony_ci{ 291962306a36Sopenharmony_ci init_waitqueue_head(&w->q); 292062306a36Sopenharmony_ci init_waitqueue_entry(&w->wait, current); 292162306a36Sopenharmony_ci w->data = data; 292262306a36Sopenharmony_ci} 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_cistatic void add_cond_wait(struct cond_wait **head, struct cond_wait *w) 292562306a36Sopenharmony_ci{ 292662306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 292762306a36Sopenharmony_ci add_wait_queue(&w->q, &w->wait); 292862306a36Sopenharmony_ci w->next = *head; 292962306a36Sopenharmony_ci *head = w; 293062306a36Sopenharmony_ci} 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_cistatic void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw) 293362306a36Sopenharmony_ci{ 293462306a36Sopenharmony_ci struct cond_wait *w, *prev; 293562306a36Sopenharmony_ci remove_wait_queue(&cw->q, &cw->wait); 293662306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 293762306a36Sopenharmony_ci for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) { 293862306a36Sopenharmony_ci if (w == cw) { 293962306a36Sopenharmony_ci if (prev != NULL) 294062306a36Sopenharmony_ci prev->next = w->next; 294162306a36Sopenharmony_ci else 294262306a36Sopenharmony_ci *head = w->next; 294362306a36Sopenharmony_ci break; 294462306a36Sopenharmony_ci } 294562306a36Sopenharmony_ci } 294662306a36Sopenharmony_ci} 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_cistatic void flush_cond_wait(struct cond_wait **head) 294962306a36Sopenharmony_ci{ 295062306a36Sopenharmony_ci while (*head != NULL) { 295162306a36Sopenharmony_ci wake_up_interruptible(&(*head)->q); 295262306a36Sopenharmony_ci *head = (*head)->next; 295362306a36Sopenharmony_ci } 295462306a36Sopenharmony_ci} 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci/* 295762306a36Sopenharmony_ci * wait for general purpose I/O pin(s) to enter specified state 295862306a36Sopenharmony_ci * 295962306a36Sopenharmony_ci * user_gpio fields: 296062306a36Sopenharmony_ci * state - bit indicates target pin state 296162306a36Sopenharmony_ci * smask - set bit indicates watched pin 296262306a36Sopenharmony_ci * 296362306a36Sopenharmony_ci * The wait ends when at least one watched pin enters the specified 296462306a36Sopenharmony_ci * state. When 0 (no error) is returned, user_gpio->state is set to the 296562306a36Sopenharmony_ci * state of all GPIO pins when the wait ends. 296662306a36Sopenharmony_ci * 296762306a36Sopenharmony_ci * Note: Each pin may be a dedicated input, dedicated output, or 296862306a36Sopenharmony_ci * configurable input/output. The number and configuration of pins 296962306a36Sopenharmony_ci * varies with the specific adapter model. Only input pins (dedicated 297062306a36Sopenharmony_ci * or configured) can be monitored with this function. 297162306a36Sopenharmony_ci */ 297262306a36Sopenharmony_cistatic int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) 297362306a36Sopenharmony_ci{ 297462306a36Sopenharmony_ci unsigned long flags; 297562306a36Sopenharmony_ci int rc = 0; 297662306a36Sopenharmony_ci struct gpio_desc gpio; 297762306a36Sopenharmony_ci struct cond_wait wait; 297862306a36Sopenharmony_ci u32 state; 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci if (!info->gpio_present) 298162306a36Sopenharmony_ci return -EINVAL; 298262306a36Sopenharmony_ci if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) 298362306a36Sopenharmony_ci return -EFAULT; 298462306a36Sopenharmony_ci DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n", 298562306a36Sopenharmony_ci info->device_name, gpio.state, gpio.smask)); 298662306a36Sopenharmony_ci /* ignore output pins identified by set IODR bit */ 298762306a36Sopenharmony_ci if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0) 298862306a36Sopenharmony_ci return -EINVAL; 298962306a36Sopenharmony_ci init_cond_wait(&wait, gpio.smask); 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci spin_lock_irqsave(&info->port_array[0]->lock, flags); 299262306a36Sopenharmony_ci /* enable interrupts for watched pins */ 299362306a36Sopenharmony_ci wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask); 299462306a36Sopenharmony_ci /* get current pin states */ 299562306a36Sopenharmony_ci state = rd_reg32(info, IOVR); 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci if (gpio.smask & ~(state ^ gpio.state)) { 299862306a36Sopenharmony_ci /* already in target state */ 299962306a36Sopenharmony_ci gpio.state = state; 300062306a36Sopenharmony_ci } else { 300162306a36Sopenharmony_ci /* wait for target state */ 300262306a36Sopenharmony_ci add_cond_wait(&info->gpio_wait_q, &wait); 300362306a36Sopenharmony_ci spin_unlock_irqrestore(&info->port_array[0]->lock, flags); 300462306a36Sopenharmony_ci schedule(); 300562306a36Sopenharmony_ci if (signal_pending(current)) 300662306a36Sopenharmony_ci rc = -ERESTARTSYS; 300762306a36Sopenharmony_ci else 300862306a36Sopenharmony_ci gpio.state = wait.data; 300962306a36Sopenharmony_ci spin_lock_irqsave(&info->port_array[0]->lock, flags); 301062306a36Sopenharmony_ci remove_cond_wait(&info->gpio_wait_q, &wait); 301162306a36Sopenharmony_ci } 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci /* disable all GPIO interrupts if no waiting processes */ 301462306a36Sopenharmony_ci if (info->gpio_wait_q == NULL) 301562306a36Sopenharmony_ci wr_reg32(info, IOER, 0); 301662306a36Sopenharmony_ci spin_unlock_irqrestore(&info->port_array[0]->lock, flags); 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio))) 301962306a36Sopenharmony_ci rc = -EFAULT; 302062306a36Sopenharmony_ci return rc; 302162306a36Sopenharmony_ci} 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_cistatic int modem_input_wait(struct slgt_info *info,int arg) 302462306a36Sopenharmony_ci{ 302562306a36Sopenharmony_ci unsigned long flags; 302662306a36Sopenharmony_ci int rc; 302762306a36Sopenharmony_ci struct mgsl_icount cprev, cnow; 302862306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci /* save current irq counts */ 303162306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 303262306a36Sopenharmony_ci cprev = info->icount; 303362306a36Sopenharmony_ci add_wait_queue(&info->status_event_wait_q, &wait); 303462306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 303562306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci for(;;) { 303862306a36Sopenharmony_ci schedule(); 303962306a36Sopenharmony_ci if (signal_pending(current)) { 304062306a36Sopenharmony_ci rc = -ERESTARTSYS; 304162306a36Sopenharmony_ci break; 304262306a36Sopenharmony_ci } 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci /* get new irq counts */ 304562306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 304662306a36Sopenharmony_ci cnow = info->icount; 304762306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 304862306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci /* if no change, wait aborted for some reason */ 305162306a36Sopenharmony_ci if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 305262306a36Sopenharmony_ci cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { 305362306a36Sopenharmony_ci rc = -EIO; 305462306a36Sopenharmony_ci break; 305562306a36Sopenharmony_ci } 305662306a36Sopenharmony_ci 305762306a36Sopenharmony_ci /* check for change in caller specified modem input */ 305862306a36Sopenharmony_ci if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || 305962306a36Sopenharmony_ci (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || 306062306a36Sopenharmony_ci (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || 306162306a36Sopenharmony_ci (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { 306262306a36Sopenharmony_ci rc = 0; 306362306a36Sopenharmony_ci break; 306462306a36Sopenharmony_ci } 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci cprev = cnow; 306762306a36Sopenharmony_ci } 306862306a36Sopenharmony_ci remove_wait_queue(&info->status_event_wait_q, &wait); 306962306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 307062306a36Sopenharmony_ci return rc; 307162306a36Sopenharmony_ci} 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci/* 307462306a36Sopenharmony_ci * return state of serial control and status signals 307562306a36Sopenharmony_ci */ 307662306a36Sopenharmony_cistatic int tiocmget(struct tty_struct *tty) 307762306a36Sopenharmony_ci{ 307862306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 307962306a36Sopenharmony_ci unsigned int result; 308062306a36Sopenharmony_ci unsigned long flags; 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 308362306a36Sopenharmony_ci get_gtsignals(info); 308462306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) + 308762306a36Sopenharmony_ci ((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) + 308862306a36Sopenharmony_ci ((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) + 308962306a36Sopenharmony_ci ((info->signals & SerialSignal_RI) ? TIOCM_RNG:0) + 309062306a36Sopenharmony_ci ((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) + 309162306a36Sopenharmony_ci ((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0); 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result)); 309462306a36Sopenharmony_ci return result; 309562306a36Sopenharmony_ci} 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci/* 309862306a36Sopenharmony_ci * set modem control signals (DTR/RTS) 309962306a36Sopenharmony_ci * 310062306a36Sopenharmony_ci * cmd signal command: TIOCMBIS = set bit TIOCMBIC = clear bit 310162306a36Sopenharmony_ci * TIOCMSET = set/clear signal values 310262306a36Sopenharmony_ci * value bit mask for command 310362306a36Sopenharmony_ci */ 310462306a36Sopenharmony_cistatic int tiocmset(struct tty_struct *tty, 310562306a36Sopenharmony_ci unsigned int set, unsigned int clear) 310662306a36Sopenharmony_ci{ 310762306a36Sopenharmony_ci struct slgt_info *info = tty->driver_data; 310862306a36Sopenharmony_ci unsigned long flags; 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear)); 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci if (set & TIOCM_RTS) 311362306a36Sopenharmony_ci info->signals |= SerialSignal_RTS; 311462306a36Sopenharmony_ci if (set & TIOCM_DTR) 311562306a36Sopenharmony_ci info->signals |= SerialSignal_DTR; 311662306a36Sopenharmony_ci if (clear & TIOCM_RTS) 311762306a36Sopenharmony_ci info->signals &= ~SerialSignal_RTS; 311862306a36Sopenharmony_ci if (clear & TIOCM_DTR) 311962306a36Sopenharmony_ci info->signals &= ~SerialSignal_DTR; 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 312262306a36Sopenharmony_ci set_gtsignals(info); 312362306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 312462306a36Sopenharmony_ci return 0; 312562306a36Sopenharmony_ci} 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_cistatic bool carrier_raised(struct tty_port *port) 312862306a36Sopenharmony_ci{ 312962306a36Sopenharmony_ci unsigned long flags; 313062306a36Sopenharmony_ci struct slgt_info *info = container_of(port, struct slgt_info, port); 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 313362306a36Sopenharmony_ci get_gtsignals(info); 313462306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci return info->signals & SerialSignal_DCD; 313762306a36Sopenharmony_ci} 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_cistatic void dtr_rts(struct tty_port *port, bool active) 314062306a36Sopenharmony_ci{ 314162306a36Sopenharmony_ci unsigned long flags; 314262306a36Sopenharmony_ci struct slgt_info *info = container_of(port, struct slgt_info, port); 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 314562306a36Sopenharmony_ci if (active) 314662306a36Sopenharmony_ci info->signals |= SerialSignal_RTS | SerialSignal_DTR; 314762306a36Sopenharmony_ci else 314862306a36Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 314962306a36Sopenharmony_ci set_gtsignals(info); 315062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 315162306a36Sopenharmony_ci} 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci/* 315562306a36Sopenharmony_ci * block current process until the device is ready to open 315662306a36Sopenharmony_ci */ 315762306a36Sopenharmony_cistatic int block_til_ready(struct tty_struct *tty, struct file *filp, 315862306a36Sopenharmony_ci struct slgt_info *info) 315962306a36Sopenharmony_ci{ 316062306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 316162306a36Sopenharmony_ci int retval; 316262306a36Sopenharmony_ci bool do_clocal = false; 316362306a36Sopenharmony_ci unsigned long flags; 316462306a36Sopenharmony_ci bool cd; 316562306a36Sopenharmony_ci struct tty_port *port = &info->port; 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci DBGINFO(("%s block_til_ready\n", tty->driver->name)); 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) { 317062306a36Sopenharmony_ci /* nonblock mode is set or port is not enabled */ 317162306a36Sopenharmony_ci tty_port_set_active(port, true); 317262306a36Sopenharmony_ci return 0; 317362306a36Sopenharmony_ci } 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_ci if (C_CLOCAL(tty)) 317662306a36Sopenharmony_ci do_clocal = true; 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci /* Wait for carrier detect and the line to become 317962306a36Sopenharmony_ci * free (i.e., not in use by the callout). While we are in 318062306a36Sopenharmony_ci * this loop, port->count is dropped by one, so that 318162306a36Sopenharmony_ci * close() knows when to free things. We restore it upon 318262306a36Sopenharmony_ci * exit, either normal or abnormal. 318362306a36Sopenharmony_ci */ 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci retval = 0; 318662306a36Sopenharmony_ci add_wait_queue(&port->open_wait, &wait); 318762306a36Sopenharmony_ci 318862306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 318962306a36Sopenharmony_ci port->count--; 319062306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 319162306a36Sopenharmony_ci port->blocked_open++; 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci while (1) { 319462306a36Sopenharmony_ci if (C_BAUD(tty) && tty_port_initialized(port)) 319562306a36Sopenharmony_ci tty_port_raise_dtr_rts(port); 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { 320062306a36Sopenharmony_ci retval = (port->flags & ASYNC_HUP_NOTIFY) ? 320162306a36Sopenharmony_ci -EAGAIN : -ERESTARTSYS; 320262306a36Sopenharmony_ci break; 320362306a36Sopenharmony_ci } 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci cd = tty_port_carrier_raised(port); 320662306a36Sopenharmony_ci if (do_clocal || cd) 320762306a36Sopenharmony_ci break; 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_ci if (signal_pending(current)) { 321062306a36Sopenharmony_ci retval = -ERESTARTSYS; 321162306a36Sopenharmony_ci break; 321262306a36Sopenharmony_ci } 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_ci DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); 321562306a36Sopenharmony_ci tty_unlock(tty); 321662306a36Sopenharmony_ci schedule(); 321762306a36Sopenharmony_ci tty_lock(tty); 321862306a36Sopenharmony_ci } 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 322162306a36Sopenharmony_ci remove_wait_queue(&port->open_wait, &wait); 322262306a36Sopenharmony_ci 322362306a36Sopenharmony_ci if (!tty_hung_up_p(filp)) 322462306a36Sopenharmony_ci port->count++; 322562306a36Sopenharmony_ci port->blocked_open--; 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci if (!retval) 322862306a36Sopenharmony_ci tty_port_set_active(port, true); 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_ci DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); 323162306a36Sopenharmony_ci return retval; 323262306a36Sopenharmony_ci} 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci/* 323562306a36Sopenharmony_ci * allocate buffers used for calling line discipline receive_buf 323662306a36Sopenharmony_ci * directly in synchronous mode 323762306a36Sopenharmony_ci * note: add 5 bytes to max frame size to allow appending 323862306a36Sopenharmony_ci * 32-bit CRC and status byte when configured to do so 323962306a36Sopenharmony_ci */ 324062306a36Sopenharmony_cistatic int alloc_tmp_rbuf(struct slgt_info *info) 324162306a36Sopenharmony_ci{ 324262306a36Sopenharmony_ci info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL); 324362306a36Sopenharmony_ci if (info->tmp_rbuf == NULL) 324462306a36Sopenharmony_ci return -ENOMEM; 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci return 0; 324762306a36Sopenharmony_ci} 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_cistatic void free_tmp_rbuf(struct slgt_info *info) 325062306a36Sopenharmony_ci{ 325162306a36Sopenharmony_ci kfree(info->tmp_rbuf); 325262306a36Sopenharmony_ci info->tmp_rbuf = NULL; 325362306a36Sopenharmony_ci} 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci/* 325662306a36Sopenharmony_ci * allocate DMA descriptor lists. 325762306a36Sopenharmony_ci */ 325862306a36Sopenharmony_cistatic int alloc_desc(struct slgt_info *info) 325962306a36Sopenharmony_ci{ 326062306a36Sopenharmony_ci unsigned int i; 326162306a36Sopenharmony_ci unsigned int pbufs; 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci /* allocate memory to hold descriptor lists */ 326462306a36Sopenharmony_ci info->bufs = dma_alloc_coherent(&info->pdev->dev, DESC_LIST_SIZE, 326562306a36Sopenharmony_ci &info->bufs_dma_addr, GFP_KERNEL); 326662306a36Sopenharmony_ci if (info->bufs == NULL) 326762306a36Sopenharmony_ci return -ENOMEM; 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci info->rbufs = (struct slgt_desc*)info->bufs; 327062306a36Sopenharmony_ci info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count; 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci pbufs = (unsigned int)info->bufs_dma_addr; 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci /* 327562306a36Sopenharmony_ci * Build circular lists of descriptors 327662306a36Sopenharmony_ci */ 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci for (i=0; i < info->rbuf_count; i++) { 327962306a36Sopenharmony_ci /* physical address of this descriptor */ 328062306a36Sopenharmony_ci info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc)); 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci /* physical address of next descriptor */ 328362306a36Sopenharmony_ci if (i == info->rbuf_count - 1) 328462306a36Sopenharmony_ci info->rbufs[i].next = cpu_to_le32(pbufs); 328562306a36Sopenharmony_ci else 328662306a36Sopenharmony_ci info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc))); 328762306a36Sopenharmony_ci set_desc_count(info->rbufs[i], DMABUFSIZE); 328862306a36Sopenharmony_ci } 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci for (i=0; i < info->tbuf_count; i++) { 329162306a36Sopenharmony_ci /* physical address of this descriptor */ 329262306a36Sopenharmony_ci info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc)); 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci /* physical address of next descriptor */ 329562306a36Sopenharmony_ci if (i == info->tbuf_count - 1) 329662306a36Sopenharmony_ci info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc)); 329762306a36Sopenharmony_ci else 329862306a36Sopenharmony_ci info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc))); 329962306a36Sopenharmony_ci } 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci return 0; 330262306a36Sopenharmony_ci} 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_cistatic void free_desc(struct slgt_info *info) 330562306a36Sopenharmony_ci{ 330662306a36Sopenharmony_ci if (info->bufs != NULL) { 330762306a36Sopenharmony_ci dma_free_coherent(&info->pdev->dev, DESC_LIST_SIZE, 330862306a36Sopenharmony_ci info->bufs, info->bufs_dma_addr); 330962306a36Sopenharmony_ci info->bufs = NULL; 331062306a36Sopenharmony_ci info->rbufs = NULL; 331162306a36Sopenharmony_ci info->tbufs = NULL; 331262306a36Sopenharmony_ci } 331362306a36Sopenharmony_ci} 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_cistatic int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) 331662306a36Sopenharmony_ci{ 331762306a36Sopenharmony_ci int i; 331862306a36Sopenharmony_ci for (i=0; i < count; i++) { 331962306a36Sopenharmony_ci bufs[i].buf = dma_alloc_coherent(&info->pdev->dev, DMABUFSIZE, 332062306a36Sopenharmony_ci &bufs[i].buf_dma_addr, GFP_KERNEL); 332162306a36Sopenharmony_ci if (!bufs[i].buf) 332262306a36Sopenharmony_ci return -ENOMEM; 332362306a36Sopenharmony_ci bufs[i].pbuf = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr); 332462306a36Sopenharmony_ci } 332562306a36Sopenharmony_ci return 0; 332662306a36Sopenharmony_ci} 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_cistatic void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) 332962306a36Sopenharmony_ci{ 333062306a36Sopenharmony_ci int i; 333162306a36Sopenharmony_ci for (i=0; i < count; i++) { 333262306a36Sopenharmony_ci if (bufs[i].buf == NULL) 333362306a36Sopenharmony_ci continue; 333462306a36Sopenharmony_ci dma_free_coherent(&info->pdev->dev, DMABUFSIZE, bufs[i].buf, 333562306a36Sopenharmony_ci bufs[i].buf_dma_addr); 333662306a36Sopenharmony_ci bufs[i].buf = NULL; 333762306a36Sopenharmony_ci } 333862306a36Sopenharmony_ci} 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_cistatic int alloc_dma_bufs(struct slgt_info *info) 334162306a36Sopenharmony_ci{ 334262306a36Sopenharmony_ci info->rbuf_count = 32; 334362306a36Sopenharmony_ci info->tbuf_count = 32; 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci if (alloc_desc(info) < 0 || 334662306a36Sopenharmony_ci alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 || 334762306a36Sopenharmony_ci alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 || 334862306a36Sopenharmony_ci alloc_tmp_rbuf(info) < 0) { 334962306a36Sopenharmony_ci DBGERR(("%s DMA buffer alloc fail\n", info->device_name)); 335062306a36Sopenharmony_ci return -ENOMEM; 335162306a36Sopenharmony_ci } 335262306a36Sopenharmony_ci reset_rbufs(info); 335362306a36Sopenharmony_ci return 0; 335462306a36Sopenharmony_ci} 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_cistatic void free_dma_bufs(struct slgt_info *info) 335762306a36Sopenharmony_ci{ 335862306a36Sopenharmony_ci if (info->bufs) { 335962306a36Sopenharmony_ci free_bufs(info, info->rbufs, info->rbuf_count); 336062306a36Sopenharmony_ci free_bufs(info, info->tbufs, info->tbuf_count); 336162306a36Sopenharmony_ci free_desc(info); 336262306a36Sopenharmony_ci } 336362306a36Sopenharmony_ci free_tmp_rbuf(info); 336462306a36Sopenharmony_ci} 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_cistatic int claim_resources(struct slgt_info *info) 336762306a36Sopenharmony_ci{ 336862306a36Sopenharmony_ci if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) { 336962306a36Sopenharmony_ci DBGERR(("%s reg addr conflict, addr=%08X\n", 337062306a36Sopenharmony_ci info->device_name, info->phys_reg_addr)); 337162306a36Sopenharmony_ci info->init_error = DiagStatus_AddressConflict; 337262306a36Sopenharmony_ci goto errout; 337362306a36Sopenharmony_ci } 337462306a36Sopenharmony_ci else 337562306a36Sopenharmony_ci info->reg_addr_requested = true; 337662306a36Sopenharmony_ci 337762306a36Sopenharmony_ci info->reg_addr = ioremap(info->phys_reg_addr, SLGT_REG_SIZE); 337862306a36Sopenharmony_ci if (!info->reg_addr) { 337962306a36Sopenharmony_ci DBGERR(("%s can't map device registers, addr=%08X\n", 338062306a36Sopenharmony_ci info->device_name, info->phys_reg_addr)); 338162306a36Sopenharmony_ci info->init_error = DiagStatus_CantAssignPciResources; 338262306a36Sopenharmony_ci goto errout; 338362306a36Sopenharmony_ci } 338462306a36Sopenharmony_ci return 0; 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_cierrout: 338762306a36Sopenharmony_ci release_resources(info); 338862306a36Sopenharmony_ci return -ENODEV; 338962306a36Sopenharmony_ci} 339062306a36Sopenharmony_ci 339162306a36Sopenharmony_cistatic void release_resources(struct slgt_info *info) 339262306a36Sopenharmony_ci{ 339362306a36Sopenharmony_ci if (info->irq_requested) { 339462306a36Sopenharmony_ci free_irq(info->irq_level, info); 339562306a36Sopenharmony_ci info->irq_requested = false; 339662306a36Sopenharmony_ci } 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_ci if (info->reg_addr_requested) { 339962306a36Sopenharmony_ci release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE); 340062306a36Sopenharmony_ci info->reg_addr_requested = false; 340162306a36Sopenharmony_ci } 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ci if (info->reg_addr) { 340462306a36Sopenharmony_ci iounmap(info->reg_addr); 340562306a36Sopenharmony_ci info->reg_addr = NULL; 340662306a36Sopenharmony_ci } 340762306a36Sopenharmony_ci} 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci/* Add the specified device instance data structure to the 341062306a36Sopenharmony_ci * global linked list of devices and increment the device count. 341162306a36Sopenharmony_ci */ 341262306a36Sopenharmony_cistatic void add_device(struct slgt_info *info) 341362306a36Sopenharmony_ci{ 341462306a36Sopenharmony_ci char *devstr; 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci info->next_device = NULL; 341762306a36Sopenharmony_ci info->line = slgt_device_count; 341862306a36Sopenharmony_ci sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line); 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci if (info->line < MAX_DEVICES) { 342162306a36Sopenharmony_ci if (maxframe[info->line]) 342262306a36Sopenharmony_ci info->max_frame_size = maxframe[info->line]; 342362306a36Sopenharmony_ci } 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci slgt_device_count++; 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_ci if (!slgt_device_list) 342862306a36Sopenharmony_ci slgt_device_list = info; 342962306a36Sopenharmony_ci else { 343062306a36Sopenharmony_ci struct slgt_info *current_dev = slgt_device_list; 343162306a36Sopenharmony_ci while(current_dev->next_device) 343262306a36Sopenharmony_ci current_dev = current_dev->next_device; 343362306a36Sopenharmony_ci current_dev->next_device = info; 343462306a36Sopenharmony_ci } 343562306a36Sopenharmony_ci 343662306a36Sopenharmony_ci if (info->max_frame_size < 4096) 343762306a36Sopenharmony_ci info->max_frame_size = 4096; 343862306a36Sopenharmony_ci else if (info->max_frame_size > 65535) 343962306a36Sopenharmony_ci info->max_frame_size = 65535; 344062306a36Sopenharmony_ci 344162306a36Sopenharmony_ci switch(info->pdev->device) { 344262306a36Sopenharmony_ci case SYNCLINK_GT_DEVICE_ID: 344362306a36Sopenharmony_ci devstr = "GT"; 344462306a36Sopenharmony_ci break; 344562306a36Sopenharmony_ci case SYNCLINK_GT2_DEVICE_ID: 344662306a36Sopenharmony_ci devstr = "GT2"; 344762306a36Sopenharmony_ci break; 344862306a36Sopenharmony_ci case SYNCLINK_GT4_DEVICE_ID: 344962306a36Sopenharmony_ci devstr = "GT4"; 345062306a36Sopenharmony_ci break; 345162306a36Sopenharmony_ci case SYNCLINK_AC_DEVICE_ID: 345262306a36Sopenharmony_ci devstr = "AC"; 345362306a36Sopenharmony_ci info->params.mode = MGSL_MODE_ASYNC; 345462306a36Sopenharmony_ci break; 345562306a36Sopenharmony_ci default: 345662306a36Sopenharmony_ci devstr = "(unknown model)"; 345762306a36Sopenharmony_ci } 345862306a36Sopenharmony_ci printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n", 345962306a36Sopenharmony_ci devstr, info->device_name, info->phys_reg_addr, 346062306a36Sopenharmony_ci info->irq_level, info->max_frame_size); 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 346362306a36Sopenharmony_ci hdlcdev_init(info); 346462306a36Sopenharmony_ci#endif 346562306a36Sopenharmony_ci} 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_cistatic const struct tty_port_operations slgt_port_ops = { 346862306a36Sopenharmony_ci .carrier_raised = carrier_raised, 346962306a36Sopenharmony_ci .dtr_rts = dtr_rts, 347062306a36Sopenharmony_ci}; 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci/* 347362306a36Sopenharmony_ci * allocate device instance structure, return NULL on failure 347462306a36Sopenharmony_ci */ 347562306a36Sopenharmony_cistatic struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) 347662306a36Sopenharmony_ci{ 347762306a36Sopenharmony_ci struct slgt_info *info; 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL); 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ci if (!info) { 348262306a36Sopenharmony_ci DBGERR(("%s device alloc failed adapter=%d port=%d\n", 348362306a36Sopenharmony_ci driver_name, adapter_num, port_num)); 348462306a36Sopenharmony_ci } else { 348562306a36Sopenharmony_ci tty_port_init(&info->port); 348662306a36Sopenharmony_ci info->port.ops = &slgt_port_ops; 348762306a36Sopenharmony_ci INIT_WORK(&info->task, bh_handler); 348862306a36Sopenharmony_ci info->max_frame_size = 4096; 348962306a36Sopenharmony_ci info->base_clock = 14745600; 349062306a36Sopenharmony_ci info->rbuf_fill_level = DMABUFSIZE; 349162306a36Sopenharmony_ci init_waitqueue_head(&info->status_event_wait_q); 349262306a36Sopenharmony_ci init_waitqueue_head(&info->event_wait_q); 349362306a36Sopenharmony_ci spin_lock_init(&info->netlock); 349462306a36Sopenharmony_ci memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); 349562306a36Sopenharmony_ci info->idle_mode = HDLC_TXIDLE_FLAGS; 349662306a36Sopenharmony_ci info->adapter_num = adapter_num; 349762306a36Sopenharmony_ci info->port_num = port_num; 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci timer_setup(&info->tx_timer, tx_timeout, 0); 350062306a36Sopenharmony_ci timer_setup(&info->rx_timer, rx_timeout, 0); 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_ci /* Copy configuration info to device instance data */ 350362306a36Sopenharmony_ci info->pdev = pdev; 350462306a36Sopenharmony_ci info->irq_level = pdev->irq; 350562306a36Sopenharmony_ci info->phys_reg_addr = pci_resource_start(pdev,0); 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci info->bus_type = MGSL_BUS_TYPE_PCI; 350862306a36Sopenharmony_ci info->irq_flags = IRQF_SHARED; 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_ci info->init_error = -1; /* assume error, set to 0 on successful init */ 351162306a36Sopenharmony_ci } 351262306a36Sopenharmony_ci 351362306a36Sopenharmony_ci return info; 351462306a36Sopenharmony_ci} 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_cistatic void device_init(int adapter_num, struct pci_dev *pdev) 351762306a36Sopenharmony_ci{ 351862306a36Sopenharmony_ci struct slgt_info *port_array[SLGT_MAX_PORTS]; 351962306a36Sopenharmony_ci int i; 352062306a36Sopenharmony_ci int port_count = 1; 352162306a36Sopenharmony_ci 352262306a36Sopenharmony_ci if (pdev->device == SYNCLINK_GT2_DEVICE_ID) 352362306a36Sopenharmony_ci port_count = 2; 352462306a36Sopenharmony_ci else if (pdev->device == SYNCLINK_GT4_DEVICE_ID) 352562306a36Sopenharmony_ci port_count = 4; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci /* allocate device instances for all ports */ 352862306a36Sopenharmony_ci for (i=0; i < port_count; ++i) { 352962306a36Sopenharmony_ci port_array[i] = alloc_dev(adapter_num, i, pdev); 353062306a36Sopenharmony_ci if (port_array[i] == NULL) { 353162306a36Sopenharmony_ci for (--i; i >= 0; --i) { 353262306a36Sopenharmony_ci tty_port_destroy(&port_array[i]->port); 353362306a36Sopenharmony_ci kfree(port_array[i]); 353462306a36Sopenharmony_ci } 353562306a36Sopenharmony_ci return; 353662306a36Sopenharmony_ci } 353762306a36Sopenharmony_ci } 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_ci /* give copy of port_array to all ports and add to device list */ 354062306a36Sopenharmony_ci for (i=0; i < port_count; ++i) { 354162306a36Sopenharmony_ci memcpy(port_array[i]->port_array, port_array, sizeof(port_array)); 354262306a36Sopenharmony_ci add_device(port_array[i]); 354362306a36Sopenharmony_ci port_array[i]->port_count = port_count; 354462306a36Sopenharmony_ci spin_lock_init(&port_array[i]->lock); 354562306a36Sopenharmony_ci } 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci /* Allocate and claim adapter resources */ 354862306a36Sopenharmony_ci if (!claim_resources(port_array[0])) { 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ci alloc_dma_bufs(port_array[0]); 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci /* copy resource information from first port to others */ 355362306a36Sopenharmony_ci for (i = 1; i < port_count; ++i) { 355462306a36Sopenharmony_ci port_array[i]->irq_level = port_array[0]->irq_level; 355562306a36Sopenharmony_ci port_array[i]->reg_addr = port_array[0]->reg_addr; 355662306a36Sopenharmony_ci alloc_dma_bufs(port_array[i]); 355762306a36Sopenharmony_ci } 355862306a36Sopenharmony_ci 355962306a36Sopenharmony_ci if (request_irq(port_array[0]->irq_level, 356062306a36Sopenharmony_ci slgt_interrupt, 356162306a36Sopenharmony_ci port_array[0]->irq_flags, 356262306a36Sopenharmony_ci port_array[0]->device_name, 356362306a36Sopenharmony_ci port_array[0]) < 0) { 356462306a36Sopenharmony_ci DBGERR(("%s request_irq failed IRQ=%d\n", 356562306a36Sopenharmony_ci port_array[0]->device_name, 356662306a36Sopenharmony_ci port_array[0]->irq_level)); 356762306a36Sopenharmony_ci } else { 356862306a36Sopenharmony_ci port_array[0]->irq_requested = true; 356962306a36Sopenharmony_ci adapter_test(port_array[0]); 357062306a36Sopenharmony_ci for (i=1 ; i < port_count ; i++) { 357162306a36Sopenharmony_ci port_array[i]->init_error = port_array[0]->init_error; 357262306a36Sopenharmony_ci port_array[i]->gpio_present = port_array[0]->gpio_present; 357362306a36Sopenharmony_ci } 357462306a36Sopenharmony_ci } 357562306a36Sopenharmony_ci } 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci for (i = 0; i < port_count; ++i) { 357862306a36Sopenharmony_ci struct slgt_info *info = port_array[i]; 357962306a36Sopenharmony_ci tty_port_register_device(&info->port, serial_driver, info->line, 358062306a36Sopenharmony_ci &info->pdev->dev); 358162306a36Sopenharmony_ci } 358262306a36Sopenharmony_ci} 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_cistatic int init_one(struct pci_dev *dev, 358562306a36Sopenharmony_ci const struct pci_device_id *ent) 358662306a36Sopenharmony_ci{ 358762306a36Sopenharmony_ci if (pci_enable_device(dev)) { 358862306a36Sopenharmony_ci printk("error enabling pci device %p\n", dev); 358962306a36Sopenharmony_ci return -EIO; 359062306a36Sopenharmony_ci } 359162306a36Sopenharmony_ci pci_set_master(dev); 359262306a36Sopenharmony_ci device_init(slgt_device_count, dev); 359362306a36Sopenharmony_ci return 0; 359462306a36Sopenharmony_ci} 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_cistatic void remove_one(struct pci_dev *dev) 359762306a36Sopenharmony_ci{ 359862306a36Sopenharmony_ci} 359962306a36Sopenharmony_ci 360062306a36Sopenharmony_cistatic const struct tty_operations ops = { 360162306a36Sopenharmony_ci .open = open, 360262306a36Sopenharmony_ci .close = close, 360362306a36Sopenharmony_ci .write = write, 360462306a36Sopenharmony_ci .put_char = put_char, 360562306a36Sopenharmony_ci .flush_chars = flush_chars, 360662306a36Sopenharmony_ci .write_room = write_room, 360762306a36Sopenharmony_ci .chars_in_buffer = chars_in_buffer, 360862306a36Sopenharmony_ci .flush_buffer = flush_buffer, 360962306a36Sopenharmony_ci .ioctl = ioctl, 361062306a36Sopenharmony_ci .compat_ioctl = slgt_compat_ioctl, 361162306a36Sopenharmony_ci .throttle = throttle, 361262306a36Sopenharmony_ci .unthrottle = unthrottle, 361362306a36Sopenharmony_ci .send_xchar = send_xchar, 361462306a36Sopenharmony_ci .break_ctl = set_break, 361562306a36Sopenharmony_ci .wait_until_sent = wait_until_sent, 361662306a36Sopenharmony_ci .set_termios = set_termios, 361762306a36Sopenharmony_ci .stop = tx_hold, 361862306a36Sopenharmony_ci .start = tx_release, 361962306a36Sopenharmony_ci .hangup = hangup, 362062306a36Sopenharmony_ci .tiocmget = tiocmget, 362162306a36Sopenharmony_ci .tiocmset = tiocmset, 362262306a36Sopenharmony_ci .get_icount = get_icount, 362362306a36Sopenharmony_ci .proc_show = synclink_gt_proc_show, 362462306a36Sopenharmony_ci}; 362562306a36Sopenharmony_ci 362662306a36Sopenharmony_cistatic void slgt_cleanup(void) 362762306a36Sopenharmony_ci{ 362862306a36Sopenharmony_ci struct slgt_info *info; 362962306a36Sopenharmony_ci struct slgt_info *tmp; 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_ci if (serial_driver) { 363262306a36Sopenharmony_ci for (info=slgt_device_list ; info != NULL ; info=info->next_device) 363362306a36Sopenharmony_ci tty_unregister_device(serial_driver, info->line); 363462306a36Sopenharmony_ci tty_unregister_driver(serial_driver); 363562306a36Sopenharmony_ci tty_driver_kref_put(serial_driver); 363662306a36Sopenharmony_ci } 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_ci /* reset devices */ 363962306a36Sopenharmony_ci info = slgt_device_list; 364062306a36Sopenharmony_ci while(info) { 364162306a36Sopenharmony_ci reset_port(info); 364262306a36Sopenharmony_ci info = info->next_device; 364362306a36Sopenharmony_ci } 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci /* release devices */ 364662306a36Sopenharmony_ci info = slgt_device_list; 364762306a36Sopenharmony_ci while(info) { 364862306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 364962306a36Sopenharmony_ci hdlcdev_exit(info); 365062306a36Sopenharmony_ci#endif 365162306a36Sopenharmony_ci free_dma_bufs(info); 365262306a36Sopenharmony_ci free_tmp_rbuf(info); 365362306a36Sopenharmony_ci if (info->port_num == 0) 365462306a36Sopenharmony_ci release_resources(info); 365562306a36Sopenharmony_ci tmp = info; 365662306a36Sopenharmony_ci info = info->next_device; 365762306a36Sopenharmony_ci tty_port_destroy(&tmp->port); 365862306a36Sopenharmony_ci kfree(tmp); 365962306a36Sopenharmony_ci } 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci if (pci_registered) 366262306a36Sopenharmony_ci pci_unregister_driver(&pci_driver); 366362306a36Sopenharmony_ci} 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci/* 366662306a36Sopenharmony_ci * Driver initialization entry point. 366762306a36Sopenharmony_ci */ 366862306a36Sopenharmony_cistatic int __init slgt_init(void) 366962306a36Sopenharmony_ci{ 367062306a36Sopenharmony_ci int rc; 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ci serial_driver = tty_alloc_driver(MAX_DEVICES, TTY_DRIVER_REAL_RAW | 367362306a36Sopenharmony_ci TTY_DRIVER_DYNAMIC_DEV); 367462306a36Sopenharmony_ci if (IS_ERR(serial_driver)) { 367562306a36Sopenharmony_ci printk("%s can't allocate tty driver\n", driver_name); 367662306a36Sopenharmony_ci return PTR_ERR(serial_driver); 367762306a36Sopenharmony_ci } 367862306a36Sopenharmony_ci 367962306a36Sopenharmony_ci /* Initialize the tty_driver structure */ 368062306a36Sopenharmony_ci 368162306a36Sopenharmony_ci serial_driver->driver_name = "synclink_gt"; 368262306a36Sopenharmony_ci serial_driver->name = tty_dev_prefix; 368362306a36Sopenharmony_ci serial_driver->major = ttymajor; 368462306a36Sopenharmony_ci serial_driver->minor_start = 64; 368562306a36Sopenharmony_ci serial_driver->type = TTY_DRIVER_TYPE_SERIAL; 368662306a36Sopenharmony_ci serial_driver->subtype = SERIAL_TYPE_NORMAL; 368762306a36Sopenharmony_ci serial_driver->init_termios = tty_std_termios; 368862306a36Sopenharmony_ci serial_driver->init_termios.c_cflag = 368962306a36Sopenharmony_ci B9600 | CS8 | CREAD | HUPCL | CLOCAL; 369062306a36Sopenharmony_ci serial_driver->init_termios.c_ispeed = 9600; 369162306a36Sopenharmony_ci serial_driver->init_termios.c_ospeed = 9600; 369262306a36Sopenharmony_ci tty_set_operations(serial_driver, &ops); 369362306a36Sopenharmony_ci if ((rc = tty_register_driver(serial_driver)) < 0) { 369462306a36Sopenharmony_ci DBGERR(("%s can't register serial driver\n", driver_name)); 369562306a36Sopenharmony_ci tty_driver_kref_put(serial_driver); 369662306a36Sopenharmony_ci serial_driver = NULL; 369762306a36Sopenharmony_ci goto error; 369862306a36Sopenharmony_ci } 369962306a36Sopenharmony_ci 370062306a36Sopenharmony_ci slgt_device_count = 0; 370162306a36Sopenharmony_ci if ((rc = pci_register_driver(&pci_driver)) < 0) { 370262306a36Sopenharmony_ci printk("%s pci_register_driver error=%d\n", driver_name, rc); 370362306a36Sopenharmony_ci goto error; 370462306a36Sopenharmony_ci } 370562306a36Sopenharmony_ci pci_registered = true; 370662306a36Sopenharmony_ci 370762306a36Sopenharmony_ci return 0; 370862306a36Sopenharmony_ci 370962306a36Sopenharmony_cierror: 371062306a36Sopenharmony_ci slgt_cleanup(); 371162306a36Sopenharmony_ci return rc; 371262306a36Sopenharmony_ci} 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_cistatic void __exit slgt_exit(void) 371562306a36Sopenharmony_ci{ 371662306a36Sopenharmony_ci slgt_cleanup(); 371762306a36Sopenharmony_ci} 371862306a36Sopenharmony_ci 371962306a36Sopenharmony_cimodule_init(slgt_init); 372062306a36Sopenharmony_cimodule_exit(slgt_exit); 372162306a36Sopenharmony_ci 372262306a36Sopenharmony_ci/* 372362306a36Sopenharmony_ci * register access routines 372462306a36Sopenharmony_ci */ 372562306a36Sopenharmony_ci 372662306a36Sopenharmony_cistatic inline void __iomem *calc_regaddr(struct slgt_info *info, 372762306a36Sopenharmony_ci unsigned int addr) 372862306a36Sopenharmony_ci{ 372962306a36Sopenharmony_ci void __iomem *reg_addr = info->reg_addr + addr; 373062306a36Sopenharmony_ci 373162306a36Sopenharmony_ci if (addr >= 0x80) 373262306a36Sopenharmony_ci reg_addr += info->port_num * 32; 373362306a36Sopenharmony_ci else if (addr >= 0x40) 373462306a36Sopenharmony_ci reg_addr += info->port_num * 16; 373562306a36Sopenharmony_ci 373662306a36Sopenharmony_ci return reg_addr; 373762306a36Sopenharmony_ci} 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_cistatic __u8 rd_reg8(struct slgt_info *info, unsigned int addr) 374062306a36Sopenharmony_ci{ 374162306a36Sopenharmony_ci return readb(calc_regaddr(info, addr)); 374262306a36Sopenharmony_ci} 374362306a36Sopenharmony_ci 374462306a36Sopenharmony_cistatic void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value) 374562306a36Sopenharmony_ci{ 374662306a36Sopenharmony_ci writeb(value, calc_regaddr(info, addr)); 374762306a36Sopenharmony_ci} 374862306a36Sopenharmony_ci 374962306a36Sopenharmony_cistatic __u16 rd_reg16(struct slgt_info *info, unsigned int addr) 375062306a36Sopenharmony_ci{ 375162306a36Sopenharmony_ci return readw(calc_regaddr(info, addr)); 375262306a36Sopenharmony_ci} 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_cistatic void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value) 375562306a36Sopenharmony_ci{ 375662306a36Sopenharmony_ci writew(value, calc_regaddr(info, addr)); 375762306a36Sopenharmony_ci} 375862306a36Sopenharmony_ci 375962306a36Sopenharmony_cistatic __u32 rd_reg32(struct slgt_info *info, unsigned int addr) 376062306a36Sopenharmony_ci{ 376162306a36Sopenharmony_ci return readl(calc_regaddr(info, addr)); 376262306a36Sopenharmony_ci} 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_cistatic void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value) 376562306a36Sopenharmony_ci{ 376662306a36Sopenharmony_ci writel(value, calc_regaddr(info, addr)); 376762306a36Sopenharmony_ci} 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_cistatic void rdma_reset(struct slgt_info *info) 377062306a36Sopenharmony_ci{ 377162306a36Sopenharmony_ci unsigned int i; 377262306a36Sopenharmony_ci 377362306a36Sopenharmony_ci /* set reset bit */ 377462306a36Sopenharmony_ci wr_reg32(info, RDCSR, BIT1); 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_ci /* wait for enable bit cleared */ 377762306a36Sopenharmony_ci for(i=0 ; i < 1000 ; i++) 377862306a36Sopenharmony_ci if (!(rd_reg32(info, RDCSR) & BIT0)) 377962306a36Sopenharmony_ci break; 378062306a36Sopenharmony_ci} 378162306a36Sopenharmony_ci 378262306a36Sopenharmony_cistatic void tdma_reset(struct slgt_info *info) 378362306a36Sopenharmony_ci{ 378462306a36Sopenharmony_ci unsigned int i; 378562306a36Sopenharmony_ci 378662306a36Sopenharmony_ci /* set reset bit */ 378762306a36Sopenharmony_ci wr_reg32(info, TDCSR, BIT1); 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_ci /* wait for enable bit cleared */ 379062306a36Sopenharmony_ci for(i=0 ; i < 1000 ; i++) 379162306a36Sopenharmony_ci if (!(rd_reg32(info, TDCSR) & BIT0)) 379262306a36Sopenharmony_ci break; 379362306a36Sopenharmony_ci} 379462306a36Sopenharmony_ci 379562306a36Sopenharmony_ci/* 379662306a36Sopenharmony_ci * enable internal loopback 379762306a36Sopenharmony_ci * TxCLK and RxCLK are generated from BRG 379862306a36Sopenharmony_ci * and TxD is looped back to RxD internally. 379962306a36Sopenharmony_ci */ 380062306a36Sopenharmony_cistatic void enable_loopback(struct slgt_info *info) 380162306a36Sopenharmony_ci{ 380262306a36Sopenharmony_ci /* SCR (serial control) BIT2=loopback enable */ 380362306a36Sopenharmony_ci wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2)); 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 380662306a36Sopenharmony_ci /* CCR (clock control) 380762306a36Sopenharmony_ci * 07..05 tx clock source (010 = BRG) 380862306a36Sopenharmony_ci * 04..02 rx clock source (010 = BRG) 380962306a36Sopenharmony_ci * 01 auxclk enable (0 = disable) 381062306a36Sopenharmony_ci * 00 BRG enable (1 = enable) 381162306a36Sopenharmony_ci * 381262306a36Sopenharmony_ci * 0100 1001 381362306a36Sopenharmony_ci */ 381462306a36Sopenharmony_ci wr_reg8(info, CCR, 0x49); 381562306a36Sopenharmony_ci 381662306a36Sopenharmony_ci /* set speed if available, otherwise use default */ 381762306a36Sopenharmony_ci if (info->params.clock_speed) 381862306a36Sopenharmony_ci set_rate(info, info->params.clock_speed); 381962306a36Sopenharmony_ci else 382062306a36Sopenharmony_ci set_rate(info, 3686400); 382162306a36Sopenharmony_ci } 382262306a36Sopenharmony_ci} 382362306a36Sopenharmony_ci 382462306a36Sopenharmony_ci/* 382562306a36Sopenharmony_ci * set baud rate generator to specified rate 382662306a36Sopenharmony_ci */ 382762306a36Sopenharmony_cistatic void set_rate(struct slgt_info *info, u32 rate) 382862306a36Sopenharmony_ci{ 382962306a36Sopenharmony_ci unsigned int div; 383062306a36Sopenharmony_ci unsigned int osc = info->base_clock; 383162306a36Sopenharmony_ci 383262306a36Sopenharmony_ci /* div = osc/rate - 1 383362306a36Sopenharmony_ci * 383462306a36Sopenharmony_ci * Round div up if osc/rate is not integer to 383562306a36Sopenharmony_ci * force to next slowest rate. 383662306a36Sopenharmony_ci */ 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci if (rate) { 383962306a36Sopenharmony_ci div = osc/rate; 384062306a36Sopenharmony_ci if (!(osc % rate) && div) 384162306a36Sopenharmony_ci div--; 384262306a36Sopenharmony_ci wr_reg16(info, BDR, (unsigned short)div); 384362306a36Sopenharmony_ci } 384462306a36Sopenharmony_ci} 384562306a36Sopenharmony_ci 384662306a36Sopenharmony_cistatic void rx_stop(struct slgt_info *info) 384762306a36Sopenharmony_ci{ 384862306a36Sopenharmony_ci unsigned short val; 384962306a36Sopenharmony_ci 385062306a36Sopenharmony_ci /* disable and reset receiver */ 385162306a36Sopenharmony_ci val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ 385262306a36Sopenharmony_ci wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ 385362306a36Sopenharmony_ci wr_reg16(info, RCR, val); /* clear reset bit */ 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_ci slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE); 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci /* clear pending rx interrupts */ 385862306a36Sopenharmony_ci wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER); 385962306a36Sopenharmony_ci 386062306a36Sopenharmony_ci rdma_reset(info); 386162306a36Sopenharmony_ci 386262306a36Sopenharmony_ci info->rx_enabled = false; 386362306a36Sopenharmony_ci info->rx_restart = false; 386462306a36Sopenharmony_ci} 386562306a36Sopenharmony_ci 386662306a36Sopenharmony_cistatic void rx_start(struct slgt_info *info) 386762306a36Sopenharmony_ci{ 386862306a36Sopenharmony_ci unsigned short val; 386962306a36Sopenharmony_ci 387062306a36Sopenharmony_ci slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA); 387162306a36Sopenharmony_ci 387262306a36Sopenharmony_ci /* clear pending rx overrun IRQ */ 387362306a36Sopenharmony_ci wr_reg16(info, SSR, IRQ_RXOVER); 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci /* reset and disable receiver */ 387662306a36Sopenharmony_ci val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ 387762306a36Sopenharmony_ci wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ 387862306a36Sopenharmony_ci wr_reg16(info, RCR, val); /* clear reset bit */ 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ci rdma_reset(info); 388162306a36Sopenharmony_ci reset_rbufs(info); 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci if (info->rx_pio) { 388462306a36Sopenharmony_ci /* rx request when rx FIFO not empty */ 388562306a36Sopenharmony_ci wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14)); 388662306a36Sopenharmony_ci slgt_irq_on(info, IRQ_RXDATA); 388762306a36Sopenharmony_ci if (info->params.mode == MGSL_MODE_ASYNC) { 388862306a36Sopenharmony_ci /* enable saving of rx status */ 388962306a36Sopenharmony_ci wr_reg32(info, RDCSR, BIT6); 389062306a36Sopenharmony_ci } 389162306a36Sopenharmony_ci } else { 389262306a36Sopenharmony_ci /* rx request when rx FIFO half full */ 389362306a36Sopenharmony_ci wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14)); 389462306a36Sopenharmony_ci /* set 1st descriptor address */ 389562306a36Sopenharmony_ci wr_reg32(info, RDDAR, info->rbufs[0].pdesc); 389662306a36Sopenharmony_ci 389762306a36Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 389862306a36Sopenharmony_ci /* enable rx DMA and DMA interrupt */ 389962306a36Sopenharmony_ci wr_reg32(info, RDCSR, (BIT2 + BIT0)); 390062306a36Sopenharmony_ci } else { 390162306a36Sopenharmony_ci /* enable saving of rx status, rx DMA and DMA interrupt */ 390262306a36Sopenharmony_ci wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0)); 390362306a36Sopenharmony_ci } 390462306a36Sopenharmony_ci } 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci slgt_irq_on(info, IRQ_RXOVER); 390762306a36Sopenharmony_ci 390862306a36Sopenharmony_ci /* enable receiver */ 390962306a36Sopenharmony_ci wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1)); 391062306a36Sopenharmony_ci 391162306a36Sopenharmony_ci info->rx_restart = false; 391262306a36Sopenharmony_ci info->rx_enabled = true; 391362306a36Sopenharmony_ci} 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_cistatic void tx_start(struct slgt_info *info) 391662306a36Sopenharmony_ci{ 391762306a36Sopenharmony_ci if (!info->tx_enabled) { 391862306a36Sopenharmony_ci wr_reg16(info, TCR, 391962306a36Sopenharmony_ci (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2)); 392062306a36Sopenharmony_ci info->tx_enabled = true; 392162306a36Sopenharmony_ci } 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci if (desc_count(info->tbufs[info->tbuf_start])) { 392462306a36Sopenharmony_ci info->drop_rts_on_tx_done = false; 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci if (info->params.mode != MGSL_MODE_ASYNC) { 392762306a36Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_RTS) { 392862306a36Sopenharmony_ci get_gtsignals(info); 392962306a36Sopenharmony_ci if (!(info->signals & SerialSignal_RTS)) { 393062306a36Sopenharmony_ci info->signals |= SerialSignal_RTS; 393162306a36Sopenharmony_ci set_gtsignals(info); 393262306a36Sopenharmony_ci info->drop_rts_on_tx_done = true; 393362306a36Sopenharmony_ci } 393462306a36Sopenharmony_ci } 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci slgt_irq_off(info, IRQ_TXDATA); 393762306a36Sopenharmony_ci slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE); 393862306a36Sopenharmony_ci /* clear tx idle and underrun status bits */ 393962306a36Sopenharmony_ci wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); 394062306a36Sopenharmony_ci } else { 394162306a36Sopenharmony_ci slgt_irq_off(info, IRQ_TXDATA); 394262306a36Sopenharmony_ci slgt_irq_on(info, IRQ_TXIDLE); 394362306a36Sopenharmony_ci /* clear tx idle status bit */ 394462306a36Sopenharmony_ci wr_reg16(info, SSR, IRQ_TXIDLE); 394562306a36Sopenharmony_ci } 394662306a36Sopenharmony_ci /* set 1st descriptor address and start DMA */ 394762306a36Sopenharmony_ci wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); 394862306a36Sopenharmony_ci wr_reg32(info, TDCSR, BIT2 + BIT0); 394962306a36Sopenharmony_ci info->tx_active = true; 395062306a36Sopenharmony_ci } 395162306a36Sopenharmony_ci} 395262306a36Sopenharmony_ci 395362306a36Sopenharmony_cistatic void tx_stop(struct slgt_info *info) 395462306a36Sopenharmony_ci{ 395562306a36Sopenharmony_ci unsigned short val; 395662306a36Sopenharmony_ci 395762306a36Sopenharmony_ci del_timer(&info->tx_timer); 395862306a36Sopenharmony_ci 395962306a36Sopenharmony_ci tdma_reset(info); 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_ci /* reset and disable transmitter */ 396262306a36Sopenharmony_ci val = rd_reg16(info, TCR) & ~BIT1; /* clear enable bit */ 396362306a36Sopenharmony_ci wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ 396462306a36Sopenharmony_ci 396562306a36Sopenharmony_ci slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_ci /* clear tx idle and underrun status bit */ 396862306a36Sopenharmony_ci wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); 396962306a36Sopenharmony_ci 397062306a36Sopenharmony_ci reset_tbufs(info); 397162306a36Sopenharmony_ci 397262306a36Sopenharmony_ci info->tx_enabled = false; 397362306a36Sopenharmony_ci info->tx_active = false; 397462306a36Sopenharmony_ci} 397562306a36Sopenharmony_ci 397662306a36Sopenharmony_cistatic void reset_port(struct slgt_info *info) 397762306a36Sopenharmony_ci{ 397862306a36Sopenharmony_ci if (!info->reg_addr) 397962306a36Sopenharmony_ci return; 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_ci tx_stop(info); 398262306a36Sopenharmony_ci rx_stop(info); 398362306a36Sopenharmony_ci 398462306a36Sopenharmony_ci info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); 398562306a36Sopenharmony_ci set_gtsignals(info); 398662306a36Sopenharmony_ci 398762306a36Sopenharmony_ci slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); 398862306a36Sopenharmony_ci} 398962306a36Sopenharmony_ci 399062306a36Sopenharmony_cistatic void reset_adapter(struct slgt_info *info) 399162306a36Sopenharmony_ci{ 399262306a36Sopenharmony_ci int i; 399362306a36Sopenharmony_ci for (i=0; i < info->port_count; ++i) { 399462306a36Sopenharmony_ci if (info->port_array[i]) 399562306a36Sopenharmony_ci reset_port(info->port_array[i]); 399662306a36Sopenharmony_ci } 399762306a36Sopenharmony_ci} 399862306a36Sopenharmony_ci 399962306a36Sopenharmony_cistatic void async_mode(struct slgt_info *info) 400062306a36Sopenharmony_ci{ 400162306a36Sopenharmony_ci unsigned short val; 400262306a36Sopenharmony_ci 400362306a36Sopenharmony_ci slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); 400462306a36Sopenharmony_ci tx_stop(info); 400562306a36Sopenharmony_ci rx_stop(info); 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_ci /* TCR (tx control) 400862306a36Sopenharmony_ci * 400962306a36Sopenharmony_ci * 15..13 mode, 010=async 401062306a36Sopenharmony_ci * 12..10 encoding, 000=NRZ 401162306a36Sopenharmony_ci * 09 parity enable 401262306a36Sopenharmony_ci * 08 1=odd parity, 0=even parity 401362306a36Sopenharmony_ci * 07 1=RTS driver control 401462306a36Sopenharmony_ci * 06 1=break enable 401562306a36Sopenharmony_ci * 05..04 character length 401662306a36Sopenharmony_ci * 00=5 bits 401762306a36Sopenharmony_ci * 01=6 bits 401862306a36Sopenharmony_ci * 10=7 bits 401962306a36Sopenharmony_ci * 11=8 bits 402062306a36Sopenharmony_ci * 03 0=1 stop bit, 1=2 stop bits 402162306a36Sopenharmony_ci * 02 reset 402262306a36Sopenharmony_ci * 01 enable 402362306a36Sopenharmony_ci * 00 auto-CTS enable 402462306a36Sopenharmony_ci */ 402562306a36Sopenharmony_ci val = 0x4000; 402662306a36Sopenharmony_ci 402762306a36Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_RTS_EN) 402862306a36Sopenharmony_ci val |= BIT7; 402962306a36Sopenharmony_ci 403062306a36Sopenharmony_ci if (info->params.parity != ASYNC_PARITY_NONE) { 403162306a36Sopenharmony_ci val |= BIT9; 403262306a36Sopenharmony_ci if (info->params.parity == ASYNC_PARITY_ODD) 403362306a36Sopenharmony_ci val |= BIT8; 403462306a36Sopenharmony_ci } 403562306a36Sopenharmony_ci 403662306a36Sopenharmony_ci switch (info->params.data_bits) 403762306a36Sopenharmony_ci { 403862306a36Sopenharmony_ci case 6: val |= BIT4; break; 403962306a36Sopenharmony_ci case 7: val |= BIT5; break; 404062306a36Sopenharmony_ci case 8: val |= BIT5 + BIT4; break; 404162306a36Sopenharmony_ci } 404262306a36Sopenharmony_ci 404362306a36Sopenharmony_ci if (info->params.stop_bits != 1) 404462306a36Sopenharmony_ci val |= BIT3; 404562306a36Sopenharmony_ci 404662306a36Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_CTS) 404762306a36Sopenharmony_ci val |= BIT0; 404862306a36Sopenharmony_ci 404962306a36Sopenharmony_ci wr_reg16(info, TCR, val); 405062306a36Sopenharmony_ci 405162306a36Sopenharmony_ci /* RCR (rx control) 405262306a36Sopenharmony_ci * 405362306a36Sopenharmony_ci * 15..13 mode, 010=async 405462306a36Sopenharmony_ci * 12..10 encoding, 000=NRZ 405562306a36Sopenharmony_ci * 09 parity enable 405662306a36Sopenharmony_ci * 08 1=odd parity, 0=even parity 405762306a36Sopenharmony_ci * 07..06 reserved, must be 0 405862306a36Sopenharmony_ci * 05..04 character length 405962306a36Sopenharmony_ci * 00=5 bits 406062306a36Sopenharmony_ci * 01=6 bits 406162306a36Sopenharmony_ci * 10=7 bits 406262306a36Sopenharmony_ci * 11=8 bits 406362306a36Sopenharmony_ci * 03 reserved, must be zero 406462306a36Sopenharmony_ci * 02 reset 406562306a36Sopenharmony_ci * 01 enable 406662306a36Sopenharmony_ci * 00 auto-DCD enable 406762306a36Sopenharmony_ci */ 406862306a36Sopenharmony_ci val = 0x4000; 406962306a36Sopenharmony_ci 407062306a36Sopenharmony_ci if (info->params.parity != ASYNC_PARITY_NONE) { 407162306a36Sopenharmony_ci val |= BIT9; 407262306a36Sopenharmony_ci if (info->params.parity == ASYNC_PARITY_ODD) 407362306a36Sopenharmony_ci val |= BIT8; 407462306a36Sopenharmony_ci } 407562306a36Sopenharmony_ci 407662306a36Sopenharmony_ci switch (info->params.data_bits) 407762306a36Sopenharmony_ci { 407862306a36Sopenharmony_ci case 6: val |= BIT4; break; 407962306a36Sopenharmony_ci case 7: val |= BIT5; break; 408062306a36Sopenharmony_ci case 8: val |= BIT5 + BIT4; break; 408162306a36Sopenharmony_ci } 408262306a36Sopenharmony_ci 408362306a36Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_DCD) 408462306a36Sopenharmony_ci val |= BIT0; 408562306a36Sopenharmony_ci 408662306a36Sopenharmony_ci wr_reg16(info, RCR, val); 408762306a36Sopenharmony_ci 408862306a36Sopenharmony_ci /* CCR (clock control) 408962306a36Sopenharmony_ci * 409062306a36Sopenharmony_ci * 07..05 011 = tx clock source is BRG/16 409162306a36Sopenharmony_ci * 04..02 010 = rx clock source is BRG 409262306a36Sopenharmony_ci * 01 0 = auxclk disabled 409362306a36Sopenharmony_ci * 00 1 = BRG enabled 409462306a36Sopenharmony_ci * 409562306a36Sopenharmony_ci * 0110 1001 409662306a36Sopenharmony_ci */ 409762306a36Sopenharmony_ci wr_reg8(info, CCR, 0x69); 409862306a36Sopenharmony_ci 409962306a36Sopenharmony_ci msc_set_vcr(info); 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_ci /* SCR (serial control) 410262306a36Sopenharmony_ci * 410362306a36Sopenharmony_ci * 15 1=tx req on FIFO half empty 410462306a36Sopenharmony_ci * 14 1=rx req on FIFO half full 410562306a36Sopenharmony_ci * 13 tx data IRQ enable 410662306a36Sopenharmony_ci * 12 tx idle IRQ enable 410762306a36Sopenharmony_ci * 11 rx break on IRQ enable 410862306a36Sopenharmony_ci * 10 rx data IRQ enable 410962306a36Sopenharmony_ci * 09 rx break off IRQ enable 411062306a36Sopenharmony_ci * 08 overrun IRQ enable 411162306a36Sopenharmony_ci * 07 DSR IRQ enable 411262306a36Sopenharmony_ci * 06 CTS IRQ enable 411362306a36Sopenharmony_ci * 05 DCD IRQ enable 411462306a36Sopenharmony_ci * 04 RI IRQ enable 411562306a36Sopenharmony_ci * 03 0=16x sampling, 1=8x sampling 411662306a36Sopenharmony_ci * 02 1=txd->rxd internal loopback enable 411762306a36Sopenharmony_ci * 01 reserved, must be zero 411862306a36Sopenharmony_ci * 00 1=master IRQ enable 411962306a36Sopenharmony_ci */ 412062306a36Sopenharmony_ci val = BIT15 + BIT14 + BIT0; 412162306a36Sopenharmony_ci /* JCR[8] : 1 = x8 async mode feature available */ 412262306a36Sopenharmony_ci if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate && 412362306a36Sopenharmony_ci ((info->base_clock < (info->params.data_rate * 16)) || 412462306a36Sopenharmony_ci (info->base_clock % (info->params.data_rate * 16)))) { 412562306a36Sopenharmony_ci /* use 8x sampling */ 412662306a36Sopenharmony_ci val |= BIT3; 412762306a36Sopenharmony_ci set_rate(info, info->params.data_rate * 8); 412862306a36Sopenharmony_ci } else { 412962306a36Sopenharmony_ci /* use 16x sampling */ 413062306a36Sopenharmony_ci set_rate(info, info->params.data_rate * 16); 413162306a36Sopenharmony_ci } 413262306a36Sopenharmony_ci wr_reg16(info, SCR, val); 413362306a36Sopenharmony_ci 413462306a36Sopenharmony_ci slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER); 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci if (info->params.loopback) 413762306a36Sopenharmony_ci enable_loopback(info); 413862306a36Sopenharmony_ci} 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_cistatic void sync_mode(struct slgt_info *info) 414162306a36Sopenharmony_ci{ 414262306a36Sopenharmony_ci unsigned short val; 414362306a36Sopenharmony_ci 414462306a36Sopenharmony_ci slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); 414562306a36Sopenharmony_ci tx_stop(info); 414662306a36Sopenharmony_ci rx_stop(info); 414762306a36Sopenharmony_ci 414862306a36Sopenharmony_ci /* TCR (tx control) 414962306a36Sopenharmony_ci * 415062306a36Sopenharmony_ci * 15..13 mode 415162306a36Sopenharmony_ci * 000=HDLC/SDLC 415262306a36Sopenharmony_ci * 001=raw bit synchronous 415362306a36Sopenharmony_ci * 010=asynchronous/isochronous 415462306a36Sopenharmony_ci * 011=monosync byte synchronous 415562306a36Sopenharmony_ci * 100=bisync byte synchronous 415662306a36Sopenharmony_ci * 101=xsync byte synchronous 415762306a36Sopenharmony_ci * 12..10 encoding 415862306a36Sopenharmony_ci * 09 CRC enable 415962306a36Sopenharmony_ci * 08 CRC32 416062306a36Sopenharmony_ci * 07 1=RTS driver control 416162306a36Sopenharmony_ci * 06 preamble enable 416262306a36Sopenharmony_ci * 05..04 preamble length 416362306a36Sopenharmony_ci * 03 share open/close flag 416462306a36Sopenharmony_ci * 02 reset 416562306a36Sopenharmony_ci * 01 enable 416662306a36Sopenharmony_ci * 00 auto-CTS enable 416762306a36Sopenharmony_ci */ 416862306a36Sopenharmony_ci val = BIT2; 416962306a36Sopenharmony_ci 417062306a36Sopenharmony_ci switch(info->params.mode) { 417162306a36Sopenharmony_ci case MGSL_MODE_XSYNC: 417262306a36Sopenharmony_ci val |= BIT15 + BIT13; 417362306a36Sopenharmony_ci break; 417462306a36Sopenharmony_ci case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; 417562306a36Sopenharmony_ci case MGSL_MODE_BISYNC: val |= BIT15; break; 417662306a36Sopenharmony_ci case MGSL_MODE_RAW: val |= BIT13; break; 417762306a36Sopenharmony_ci } 417862306a36Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_RTS_EN) 417962306a36Sopenharmony_ci val |= BIT7; 418062306a36Sopenharmony_ci 418162306a36Sopenharmony_ci switch(info->params.encoding) 418262306a36Sopenharmony_ci { 418362306a36Sopenharmony_ci case HDLC_ENCODING_NRZB: val |= BIT10; break; 418462306a36Sopenharmony_ci case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; 418562306a36Sopenharmony_ci case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; 418662306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; 418762306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; 418862306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; 418962306a36Sopenharmony_ci case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; 419062306a36Sopenharmony_ci } 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_ci switch (info->params.crc_type & HDLC_CRC_MASK) 419362306a36Sopenharmony_ci { 419462306a36Sopenharmony_ci case HDLC_CRC_16_CCITT: val |= BIT9; break; 419562306a36Sopenharmony_ci case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; 419662306a36Sopenharmony_ci } 419762306a36Sopenharmony_ci 419862306a36Sopenharmony_ci if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE) 419962306a36Sopenharmony_ci val |= BIT6; 420062306a36Sopenharmony_ci 420162306a36Sopenharmony_ci switch (info->params.preamble_length) 420262306a36Sopenharmony_ci { 420362306a36Sopenharmony_ci case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break; 420462306a36Sopenharmony_ci case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break; 420562306a36Sopenharmony_ci case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break; 420662306a36Sopenharmony_ci } 420762306a36Sopenharmony_ci 420862306a36Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_CTS) 420962306a36Sopenharmony_ci val |= BIT0; 421062306a36Sopenharmony_ci 421162306a36Sopenharmony_ci wr_reg16(info, TCR, val); 421262306a36Sopenharmony_ci 421362306a36Sopenharmony_ci /* TPR (transmit preamble) */ 421462306a36Sopenharmony_ci 421562306a36Sopenharmony_ci switch (info->params.preamble) 421662306a36Sopenharmony_ci { 421762306a36Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break; 421862306a36Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_ONES: val = 0xff; break; 421962306a36Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break; 422062306a36Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_10: val = 0x55; break; 422162306a36Sopenharmony_ci case HDLC_PREAMBLE_PATTERN_01: val = 0xaa; break; 422262306a36Sopenharmony_ci default: val = 0x7e; break; 422362306a36Sopenharmony_ci } 422462306a36Sopenharmony_ci wr_reg8(info, TPR, (unsigned char)val); 422562306a36Sopenharmony_ci 422662306a36Sopenharmony_ci /* RCR (rx control) 422762306a36Sopenharmony_ci * 422862306a36Sopenharmony_ci * 15..13 mode 422962306a36Sopenharmony_ci * 000=HDLC/SDLC 423062306a36Sopenharmony_ci * 001=raw bit synchronous 423162306a36Sopenharmony_ci * 010=asynchronous/isochronous 423262306a36Sopenharmony_ci * 011=monosync byte synchronous 423362306a36Sopenharmony_ci * 100=bisync byte synchronous 423462306a36Sopenharmony_ci * 101=xsync byte synchronous 423562306a36Sopenharmony_ci * 12..10 encoding 423662306a36Sopenharmony_ci * 09 CRC enable 423762306a36Sopenharmony_ci * 08 CRC32 423862306a36Sopenharmony_ci * 07..03 reserved, must be 0 423962306a36Sopenharmony_ci * 02 reset 424062306a36Sopenharmony_ci * 01 enable 424162306a36Sopenharmony_ci * 00 auto-DCD enable 424262306a36Sopenharmony_ci */ 424362306a36Sopenharmony_ci val = 0; 424462306a36Sopenharmony_ci 424562306a36Sopenharmony_ci switch(info->params.mode) { 424662306a36Sopenharmony_ci case MGSL_MODE_XSYNC: 424762306a36Sopenharmony_ci val |= BIT15 + BIT13; 424862306a36Sopenharmony_ci break; 424962306a36Sopenharmony_ci case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; 425062306a36Sopenharmony_ci case MGSL_MODE_BISYNC: val |= BIT15; break; 425162306a36Sopenharmony_ci case MGSL_MODE_RAW: val |= BIT13; break; 425262306a36Sopenharmony_ci } 425362306a36Sopenharmony_ci 425462306a36Sopenharmony_ci switch(info->params.encoding) 425562306a36Sopenharmony_ci { 425662306a36Sopenharmony_ci case HDLC_ENCODING_NRZB: val |= BIT10; break; 425762306a36Sopenharmony_ci case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; 425862306a36Sopenharmony_ci case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; 425962306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; 426062306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; 426162306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; 426262306a36Sopenharmony_ci case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; 426362306a36Sopenharmony_ci } 426462306a36Sopenharmony_ci 426562306a36Sopenharmony_ci switch (info->params.crc_type & HDLC_CRC_MASK) 426662306a36Sopenharmony_ci { 426762306a36Sopenharmony_ci case HDLC_CRC_16_CCITT: val |= BIT9; break; 426862306a36Sopenharmony_ci case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; 426962306a36Sopenharmony_ci } 427062306a36Sopenharmony_ci 427162306a36Sopenharmony_ci if (info->params.flags & HDLC_FLAG_AUTO_DCD) 427262306a36Sopenharmony_ci val |= BIT0; 427362306a36Sopenharmony_ci 427462306a36Sopenharmony_ci wr_reg16(info, RCR, val); 427562306a36Sopenharmony_ci 427662306a36Sopenharmony_ci /* CCR (clock control) 427762306a36Sopenharmony_ci * 427862306a36Sopenharmony_ci * 07..05 tx clock source 427962306a36Sopenharmony_ci * 04..02 rx clock source 428062306a36Sopenharmony_ci * 01 auxclk enable 428162306a36Sopenharmony_ci * 00 BRG enable 428262306a36Sopenharmony_ci */ 428362306a36Sopenharmony_ci val = 0; 428462306a36Sopenharmony_ci 428562306a36Sopenharmony_ci if (info->params.flags & HDLC_FLAG_TXC_BRG) 428662306a36Sopenharmony_ci { 428762306a36Sopenharmony_ci // when RxC source is DPLL, BRG generates 16X DPLL 428862306a36Sopenharmony_ci // reference clock, so take TxC from BRG/16 to get 428962306a36Sopenharmony_ci // transmit clock at actual data rate 429062306a36Sopenharmony_ci if (info->params.flags & HDLC_FLAG_RXC_DPLL) 429162306a36Sopenharmony_ci val |= BIT6 + BIT5; /* 011, txclk = BRG/16 */ 429262306a36Sopenharmony_ci else 429362306a36Sopenharmony_ci val |= BIT6; /* 010, txclk = BRG */ 429462306a36Sopenharmony_ci } 429562306a36Sopenharmony_ci else if (info->params.flags & HDLC_FLAG_TXC_DPLL) 429662306a36Sopenharmony_ci val |= BIT7; /* 100, txclk = DPLL Input */ 429762306a36Sopenharmony_ci else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN) 429862306a36Sopenharmony_ci val |= BIT5; /* 001, txclk = RXC Input */ 429962306a36Sopenharmony_ci 430062306a36Sopenharmony_ci if (info->params.flags & HDLC_FLAG_RXC_BRG) 430162306a36Sopenharmony_ci val |= BIT3; /* 010, rxclk = BRG */ 430262306a36Sopenharmony_ci else if (info->params.flags & HDLC_FLAG_RXC_DPLL) 430362306a36Sopenharmony_ci val |= BIT4; /* 100, rxclk = DPLL */ 430462306a36Sopenharmony_ci else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN) 430562306a36Sopenharmony_ci val |= BIT2; /* 001, rxclk = TXC Input */ 430662306a36Sopenharmony_ci 430762306a36Sopenharmony_ci if (info->params.clock_speed) 430862306a36Sopenharmony_ci val |= BIT1 + BIT0; 430962306a36Sopenharmony_ci 431062306a36Sopenharmony_ci wr_reg8(info, CCR, (unsigned char)val); 431162306a36Sopenharmony_ci 431262306a36Sopenharmony_ci if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL)) 431362306a36Sopenharmony_ci { 431462306a36Sopenharmony_ci // program DPLL mode 431562306a36Sopenharmony_ci switch(info->params.encoding) 431662306a36Sopenharmony_ci { 431762306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_MARK: 431862306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_SPACE: 431962306a36Sopenharmony_ci val = BIT7; break; 432062306a36Sopenharmony_ci case HDLC_ENCODING_BIPHASE_LEVEL: 432162306a36Sopenharmony_ci case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: 432262306a36Sopenharmony_ci val = BIT7 + BIT6; break; 432362306a36Sopenharmony_ci default: val = BIT6; // NRZ encodings 432462306a36Sopenharmony_ci } 432562306a36Sopenharmony_ci wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val)); 432662306a36Sopenharmony_ci 432762306a36Sopenharmony_ci // DPLL requires a 16X reference clock from BRG 432862306a36Sopenharmony_ci set_rate(info, info->params.clock_speed * 16); 432962306a36Sopenharmony_ci } 433062306a36Sopenharmony_ci else 433162306a36Sopenharmony_ci set_rate(info, info->params.clock_speed); 433262306a36Sopenharmony_ci 433362306a36Sopenharmony_ci tx_set_idle(info); 433462306a36Sopenharmony_ci 433562306a36Sopenharmony_ci msc_set_vcr(info); 433662306a36Sopenharmony_ci 433762306a36Sopenharmony_ci /* SCR (serial control) 433862306a36Sopenharmony_ci * 433962306a36Sopenharmony_ci * 15 1=tx req on FIFO half empty 434062306a36Sopenharmony_ci * 14 1=rx req on FIFO half full 434162306a36Sopenharmony_ci * 13 tx data IRQ enable 434262306a36Sopenharmony_ci * 12 tx idle IRQ enable 434362306a36Sopenharmony_ci * 11 underrun IRQ enable 434462306a36Sopenharmony_ci * 10 rx data IRQ enable 434562306a36Sopenharmony_ci * 09 rx idle IRQ enable 434662306a36Sopenharmony_ci * 08 overrun IRQ enable 434762306a36Sopenharmony_ci * 07 DSR IRQ enable 434862306a36Sopenharmony_ci * 06 CTS IRQ enable 434962306a36Sopenharmony_ci * 05 DCD IRQ enable 435062306a36Sopenharmony_ci * 04 RI IRQ enable 435162306a36Sopenharmony_ci * 03 reserved, must be zero 435262306a36Sopenharmony_ci * 02 1=txd->rxd internal loopback enable 435362306a36Sopenharmony_ci * 01 reserved, must be zero 435462306a36Sopenharmony_ci * 00 1=master IRQ enable 435562306a36Sopenharmony_ci */ 435662306a36Sopenharmony_ci wr_reg16(info, SCR, BIT15 + BIT14 + BIT0); 435762306a36Sopenharmony_ci 435862306a36Sopenharmony_ci if (info->params.loopback) 435962306a36Sopenharmony_ci enable_loopback(info); 436062306a36Sopenharmony_ci} 436162306a36Sopenharmony_ci 436262306a36Sopenharmony_ci/* 436362306a36Sopenharmony_ci * set transmit idle mode 436462306a36Sopenharmony_ci */ 436562306a36Sopenharmony_cistatic void tx_set_idle(struct slgt_info *info) 436662306a36Sopenharmony_ci{ 436762306a36Sopenharmony_ci unsigned char val; 436862306a36Sopenharmony_ci unsigned short tcr; 436962306a36Sopenharmony_ci 437062306a36Sopenharmony_ci /* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits 437162306a36Sopenharmony_ci * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits 437262306a36Sopenharmony_ci */ 437362306a36Sopenharmony_ci tcr = rd_reg16(info, TCR); 437462306a36Sopenharmony_ci if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) { 437562306a36Sopenharmony_ci /* disable preamble, set idle size to 16 bits */ 437662306a36Sopenharmony_ci tcr = (tcr & ~(BIT6 + BIT5)) | BIT4; 437762306a36Sopenharmony_ci /* MSB of 16 bit idle specified in tx preamble register (TPR) */ 437862306a36Sopenharmony_ci wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff)); 437962306a36Sopenharmony_ci } else if (!(tcr & BIT6)) { 438062306a36Sopenharmony_ci /* preamble is disabled, set idle size to 8 bits */ 438162306a36Sopenharmony_ci tcr &= ~(BIT5 + BIT4); 438262306a36Sopenharmony_ci } 438362306a36Sopenharmony_ci wr_reg16(info, TCR, tcr); 438462306a36Sopenharmony_ci 438562306a36Sopenharmony_ci if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) { 438662306a36Sopenharmony_ci /* LSB of custom tx idle specified in tx idle register */ 438762306a36Sopenharmony_ci val = (unsigned char)(info->idle_mode & 0xff); 438862306a36Sopenharmony_ci } else { 438962306a36Sopenharmony_ci /* standard 8 bit idle patterns */ 439062306a36Sopenharmony_ci switch(info->idle_mode) 439162306a36Sopenharmony_ci { 439262306a36Sopenharmony_ci case HDLC_TXIDLE_FLAGS: val = 0x7e; break; 439362306a36Sopenharmony_ci case HDLC_TXIDLE_ALT_ZEROS_ONES: 439462306a36Sopenharmony_ci case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break; 439562306a36Sopenharmony_ci case HDLC_TXIDLE_ZEROS: 439662306a36Sopenharmony_ci case HDLC_TXIDLE_SPACE: val = 0x00; break; 439762306a36Sopenharmony_ci default: val = 0xff; 439862306a36Sopenharmony_ci } 439962306a36Sopenharmony_ci } 440062306a36Sopenharmony_ci 440162306a36Sopenharmony_ci wr_reg8(info, TIR, val); 440262306a36Sopenharmony_ci} 440362306a36Sopenharmony_ci 440462306a36Sopenharmony_ci/* 440562306a36Sopenharmony_ci * get state of V24 status (input) signals 440662306a36Sopenharmony_ci */ 440762306a36Sopenharmony_cistatic void get_gtsignals(struct slgt_info *info) 440862306a36Sopenharmony_ci{ 440962306a36Sopenharmony_ci unsigned short status = rd_reg16(info, SSR); 441062306a36Sopenharmony_ci 441162306a36Sopenharmony_ci /* clear all serial signals except RTS and DTR */ 441262306a36Sopenharmony_ci info->signals &= SerialSignal_RTS | SerialSignal_DTR; 441362306a36Sopenharmony_ci 441462306a36Sopenharmony_ci if (status & BIT3) 441562306a36Sopenharmony_ci info->signals |= SerialSignal_DSR; 441662306a36Sopenharmony_ci if (status & BIT2) 441762306a36Sopenharmony_ci info->signals |= SerialSignal_CTS; 441862306a36Sopenharmony_ci if (status & BIT1) 441962306a36Sopenharmony_ci info->signals |= SerialSignal_DCD; 442062306a36Sopenharmony_ci if (status & BIT0) 442162306a36Sopenharmony_ci info->signals |= SerialSignal_RI; 442262306a36Sopenharmony_ci} 442362306a36Sopenharmony_ci 442462306a36Sopenharmony_ci/* 442562306a36Sopenharmony_ci * set V.24 Control Register based on current configuration 442662306a36Sopenharmony_ci */ 442762306a36Sopenharmony_cistatic void msc_set_vcr(struct slgt_info *info) 442862306a36Sopenharmony_ci{ 442962306a36Sopenharmony_ci unsigned char val = 0; 443062306a36Sopenharmony_ci 443162306a36Sopenharmony_ci /* VCR (V.24 control) 443262306a36Sopenharmony_ci * 443362306a36Sopenharmony_ci * 07..04 serial IF select 443462306a36Sopenharmony_ci * 03 DTR 443562306a36Sopenharmony_ci * 02 RTS 443662306a36Sopenharmony_ci * 01 LL 443762306a36Sopenharmony_ci * 00 RL 443862306a36Sopenharmony_ci */ 443962306a36Sopenharmony_ci 444062306a36Sopenharmony_ci switch(info->if_mode & MGSL_INTERFACE_MASK) 444162306a36Sopenharmony_ci { 444262306a36Sopenharmony_ci case MGSL_INTERFACE_RS232: 444362306a36Sopenharmony_ci val |= BIT5; /* 0010 */ 444462306a36Sopenharmony_ci break; 444562306a36Sopenharmony_ci case MGSL_INTERFACE_V35: 444662306a36Sopenharmony_ci val |= BIT7 + BIT6 + BIT5; /* 1110 */ 444762306a36Sopenharmony_ci break; 444862306a36Sopenharmony_ci case MGSL_INTERFACE_RS422: 444962306a36Sopenharmony_ci val |= BIT6; /* 0100 */ 445062306a36Sopenharmony_ci break; 445162306a36Sopenharmony_ci } 445262306a36Sopenharmony_ci 445362306a36Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_MSB_FIRST) 445462306a36Sopenharmony_ci val |= BIT4; 445562306a36Sopenharmony_ci if (info->signals & SerialSignal_DTR) 445662306a36Sopenharmony_ci val |= BIT3; 445762306a36Sopenharmony_ci if (info->signals & SerialSignal_RTS) 445862306a36Sopenharmony_ci val |= BIT2; 445962306a36Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_LL) 446062306a36Sopenharmony_ci val |= BIT1; 446162306a36Sopenharmony_ci if (info->if_mode & MGSL_INTERFACE_RL) 446262306a36Sopenharmony_ci val |= BIT0; 446362306a36Sopenharmony_ci wr_reg8(info, VCR, val); 446462306a36Sopenharmony_ci} 446562306a36Sopenharmony_ci 446662306a36Sopenharmony_ci/* 446762306a36Sopenharmony_ci * set state of V24 control (output) signals 446862306a36Sopenharmony_ci */ 446962306a36Sopenharmony_cistatic void set_gtsignals(struct slgt_info *info) 447062306a36Sopenharmony_ci{ 447162306a36Sopenharmony_ci unsigned char val = rd_reg8(info, VCR); 447262306a36Sopenharmony_ci if (info->signals & SerialSignal_DTR) 447362306a36Sopenharmony_ci val |= BIT3; 447462306a36Sopenharmony_ci else 447562306a36Sopenharmony_ci val &= ~BIT3; 447662306a36Sopenharmony_ci if (info->signals & SerialSignal_RTS) 447762306a36Sopenharmony_ci val |= BIT2; 447862306a36Sopenharmony_ci else 447962306a36Sopenharmony_ci val &= ~BIT2; 448062306a36Sopenharmony_ci wr_reg8(info, VCR, val); 448162306a36Sopenharmony_ci} 448262306a36Sopenharmony_ci 448362306a36Sopenharmony_ci/* 448462306a36Sopenharmony_ci * free range of receive DMA buffers (i to last) 448562306a36Sopenharmony_ci */ 448662306a36Sopenharmony_cistatic void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last) 448762306a36Sopenharmony_ci{ 448862306a36Sopenharmony_ci int done = 0; 448962306a36Sopenharmony_ci 449062306a36Sopenharmony_ci while(!done) { 449162306a36Sopenharmony_ci /* reset current buffer for reuse */ 449262306a36Sopenharmony_ci info->rbufs[i].status = 0; 449362306a36Sopenharmony_ci set_desc_count(info->rbufs[i], info->rbuf_fill_level); 449462306a36Sopenharmony_ci if (i == last) 449562306a36Sopenharmony_ci done = 1; 449662306a36Sopenharmony_ci if (++i == info->rbuf_count) 449762306a36Sopenharmony_ci i = 0; 449862306a36Sopenharmony_ci } 449962306a36Sopenharmony_ci info->rbuf_current = i; 450062306a36Sopenharmony_ci} 450162306a36Sopenharmony_ci 450262306a36Sopenharmony_ci/* 450362306a36Sopenharmony_ci * mark all receive DMA buffers as free 450462306a36Sopenharmony_ci */ 450562306a36Sopenharmony_cistatic void reset_rbufs(struct slgt_info *info) 450662306a36Sopenharmony_ci{ 450762306a36Sopenharmony_ci free_rbufs(info, 0, info->rbuf_count - 1); 450862306a36Sopenharmony_ci info->rbuf_fill_index = 0; 450962306a36Sopenharmony_ci info->rbuf_fill_count = 0; 451062306a36Sopenharmony_ci} 451162306a36Sopenharmony_ci 451262306a36Sopenharmony_ci/* 451362306a36Sopenharmony_ci * pass receive HDLC frame to upper layer 451462306a36Sopenharmony_ci * 451562306a36Sopenharmony_ci * return true if frame available, otherwise false 451662306a36Sopenharmony_ci */ 451762306a36Sopenharmony_cistatic bool rx_get_frame(struct slgt_info *info) 451862306a36Sopenharmony_ci{ 451962306a36Sopenharmony_ci unsigned int start, end; 452062306a36Sopenharmony_ci unsigned short status; 452162306a36Sopenharmony_ci unsigned int framesize = 0; 452262306a36Sopenharmony_ci unsigned long flags; 452362306a36Sopenharmony_ci struct tty_struct *tty = info->port.tty; 452462306a36Sopenharmony_ci unsigned char addr_field = 0xff; 452562306a36Sopenharmony_ci unsigned int crc_size = 0; 452662306a36Sopenharmony_ci 452762306a36Sopenharmony_ci switch (info->params.crc_type & HDLC_CRC_MASK) { 452862306a36Sopenharmony_ci case HDLC_CRC_16_CCITT: crc_size = 2; break; 452962306a36Sopenharmony_ci case HDLC_CRC_32_CCITT: crc_size = 4; break; 453062306a36Sopenharmony_ci } 453162306a36Sopenharmony_ci 453262306a36Sopenharmony_cicheck_again: 453362306a36Sopenharmony_ci 453462306a36Sopenharmony_ci framesize = 0; 453562306a36Sopenharmony_ci addr_field = 0xff; 453662306a36Sopenharmony_ci start = end = info->rbuf_current; 453762306a36Sopenharmony_ci 453862306a36Sopenharmony_ci for (;;) { 453962306a36Sopenharmony_ci if (!desc_complete(info->rbufs[end])) 454062306a36Sopenharmony_ci goto cleanup; 454162306a36Sopenharmony_ci 454262306a36Sopenharmony_ci if (framesize == 0 && info->params.addr_filter != 0xff) 454362306a36Sopenharmony_ci addr_field = info->rbufs[end].buf[0]; 454462306a36Sopenharmony_ci 454562306a36Sopenharmony_ci framesize += desc_count(info->rbufs[end]); 454662306a36Sopenharmony_ci 454762306a36Sopenharmony_ci if (desc_eof(info->rbufs[end])) 454862306a36Sopenharmony_ci break; 454962306a36Sopenharmony_ci 455062306a36Sopenharmony_ci if (++end == info->rbuf_count) 455162306a36Sopenharmony_ci end = 0; 455262306a36Sopenharmony_ci 455362306a36Sopenharmony_ci if (end == info->rbuf_current) { 455462306a36Sopenharmony_ci if (info->rx_enabled){ 455562306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 455662306a36Sopenharmony_ci rx_start(info); 455762306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 455862306a36Sopenharmony_ci } 455962306a36Sopenharmony_ci goto cleanup; 456062306a36Sopenharmony_ci } 456162306a36Sopenharmony_ci } 456262306a36Sopenharmony_ci 456362306a36Sopenharmony_ci /* status 456462306a36Sopenharmony_ci * 456562306a36Sopenharmony_ci * 15 buffer complete 456662306a36Sopenharmony_ci * 14..06 reserved 456762306a36Sopenharmony_ci * 05..04 residue 456862306a36Sopenharmony_ci * 02 eof (end of frame) 456962306a36Sopenharmony_ci * 01 CRC error 457062306a36Sopenharmony_ci * 00 abort 457162306a36Sopenharmony_ci */ 457262306a36Sopenharmony_ci status = desc_status(info->rbufs[end]); 457362306a36Sopenharmony_ci 457462306a36Sopenharmony_ci /* ignore CRC bit if not using CRC (bit is undefined) */ 457562306a36Sopenharmony_ci if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE) 457662306a36Sopenharmony_ci status &= ~BIT1; 457762306a36Sopenharmony_ci 457862306a36Sopenharmony_ci if (framesize == 0 || 457962306a36Sopenharmony_ci (addr_field != 0xff && addr_field != info->params.addr_filter)) { 458062306a36Sopenharmony_ci free_rbufs(info, start, end); 458162306a36Sopenharmony_ci goto check_again; 458262306a36Sopenharmony_ci } 458362306a36Sopenharmony_ci 458462306a36Sopenharmony_ci if (framesize < (2 + crc_size) || status & BIT0) { 458562306a36Sopenharmony_ci info->icount.rxshort++; 458662306a36Sopenharmony_ci framesize = 0; 458762306a36Sopenharmony_ci } else if (status & BIT1) { 458862306a36Sopenharmony_ci info->icount.rxcrc++; 458962306a36Sopenharmony_ci if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) 459062306a36Sopenharmony_ci framesize = 0; 459162306a36Sopenharmony_ci } 459262306a36Sopenharmony_ci 459362306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 459462306a36Sopenharmony_ci if (framesize == 0) { 459562306a36Sopenharmony_ci info->netdev->stats.rx_errors++; 459662306a36Sopenharmony_ci info->netdev->stats.rx_frame_errors++; 459762306a36Sopenharmony_ci } 459862306a36Sopenharmony_ci#endif 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_ci DBGBH(("%s rx frame status=%04X size=%d\n", 460162306a36Sopenharmony_ci info->device_name, status, framesize)); 460262306a36Sopenharmony_ci DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx"); 460362306a36Sopenharmony_ci 460462306a36Sopenharmony_ci if (framesize) { 460562306a36Sopenharmony_ci if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) { 460662306a36Sopenharmony_ci framesize -= crc_size; 460762306a36Sopenharmony_ci crc_size = 0; 460862306a36Sopenharmony_ci } 460962306a36Sopenharmony_ci 461062306a36Sopenharmony_ci if (framesize > info->max_frame_size + crc_size) 461162306a36Sopenharmony_ci info->icount.rxlong++; 461262306a36Sopenharmony_ci else { 461362306a36Sopenharmony_ci /* copy dma buffer(s) to contiguous temp buffer */ 461462306a36Sopenharmony_ci int copy_count = framesize; 461562306a36Sopenharmony_ci int i = start; 461662306a36Sopenharmony_ci unsigned char *p = info->tmp_rbuf; 461762306a36Sopenharmony_ci info->tmp_rbuf_count = framesize; 461862306a36Sopenharmony_ci 461962306a36Sopenharmony_ci info->icount.rxok++; 462062306a36Sopenharmony_ci 462162306a36Sopenharmony_ci while(copy_count) { 462262306a36Sopenharmony_ci int partial_count = min_t(int, copy_count, info->rbuf_fill_level); 462362306a36Sopenharmony_ci memcpy(p, info->rbufs[i].buf, partial_count); 462462306a36Sopenharmony_ci p += partial_count; 462562306a36Sopenharmony_ci copy_count -= partial_count; 462662306a36Sopenharmony_ci if (++i == info->rbuf_count) 462762306a36Sopenharmony_ci i = 0; 462862306a36Sopenharmony_ci } 462962306a36Sopenharmony_ci 463062306a36Sopenharmony_ci if (info->params.crc_type & HDLC_CRC_RETURN_EX) { 463162306a36Sopenharmony_ci *p = (status & BIT1) ? RX_CRC_ERROR : RX_OK; 463262306a36Sopenharmony_ci framesize++; 463362306a36Sopenharmony_ci } 463462306a36Sopenharmony_ci 463562306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 463662306a36Sopenharmony_ci if (info->netcount) 463762306a36Sopenharmony_ci hdlcdev_rx(info,info->tmp_rbuf, framesize); 463862306a36Sopenharmony_ci else 463962306a36Sopenharmony_ci#endif 464062306a36Sopenharmony_ci ldisc_receive_buf(tty, info->tmp_rbuf, NULL, 464162306a36Sopenharmony_ci framesize); 464262306a36Sopenharmony_ci } 464362306a36Sopenharmony_ci } 464462306a36Sopenharmony_ci free_rbufs(info, start, end); 464562306a36Sopenharmony_ci return true; 464662306a36Sopenharmony_ci 464762306a36Sopenharmony_cicleanup: 464862306a36Sopenharmony_ci return false; 464962306a36Sopenharmony_ci} 465062306a36Sopenharmony_ci 465162306a36Sopenharmony_ci/* 465262306a36Sopenharmony_ci * pass receive buffer (RAW synchronous mode) to tty layer 465362306a36Sopenharmony_ci * return true if buffer available, otherwise false 465462306a36Sopenharmony_ci */ 465562306a36Sopenharmony_cistatic bool rx_get_buf(struct slgt_info *info) 465662306a36Sopenharmony_ci{ 465762306a36Sopenharmony_ci unsigned int i = info->rbuf_current; 465862306a36Sopenharmony_ci unsigned int count; 465962306a36Sopenharmony_ci 466062306a36Sopenharmony_ci if (!desc_complete(info->rbufs[i])) 466162306a36Sopenharmony_ci return false; 466262306a36Sopenharmony_ci count = desc_count(info->rbufs[i]); 466362306a36Sopenharmony_ci switch(info->params.mode) { 466462306a36Sopenharmony_ci case MGSL_MODE_MONOSYNC: 466562306a36Sopenharmony_ci case MGSL_MODE_BISYNC: 466662306a36Sopenharmony_ci case MGSL_MODE_XSYNC: 466762306a36Sopenharmony_ci /* ignore residue in byte synchronous modes */ 466862306a36Sopenharmony_ci if (desc_residue(info->rbufs[i])) 466962306a36Sopenharmony_ci count--; 467062306a36Sopenharmony_ci break; 467162306a36Sopenharmony_ci } 467262306a36Sopenharmony_ci DBGDATA(info, info->rbufs[i].buf, count, "rx"); 467362306a36Sopenharmony_ci DBGINFO(("rx_get_buf size=%d\n", count)); 467462306a36Sopenharmony_ci if (count) 467562306a36Sopenharmony_ci ldisc_receive_buf(info->port.tty, info->rbufs[i].buf, NULL, 467662306a36Sopenharmony_ci count); 467762306a36Sopenharmony_ci free_rbufs(info, i, i); 467862306a36Sopenharmony_ci return true; 467962306a36Sopenharmony_ci} 468062306a36Sopenharmony_ci 468162306a36Sopenharmony_cistatic void reset_tbufs(struct slgt_info *info) 468262306a36Sopenharmony_ci{ 468362306a36Sopenharmony_ci unsigned int i; 468462306a36Sopenharmony_ci info->tbuf_current = 0; 468562306a36Sopenharmony_ci for (i=0 ; i < info->tbuf_count ; i++) { 468662306a36Sopenharmony_ci info->tbufs[i].status = 0; 468762306a36Sopenharmony_ci info->tbufs[i].count = 0; 468862306a36Sopenharmony_ci } 468962306a36Sopenharmony_ci} 469062306a36Sopenharmony_ci 469162306a36Sopenharmony_ci/* 469262306a36Sopenharmony_ci * return number of free transmit DMA buffers 469362306a36Sopenharmony_ci */ 469462306a36Sopenharmony_cistatic unsigned int free_tbuf_count(struct slgt_info *info) 469562306a36Sopenharmony_ci{ 469662306a36Sopenharmony_ci unsigned int count = 0; 469762306a36Sopenharmony_ci unsigned int i = info->tbuf_current; 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_ci do 470062306a36Sopenharmony_ci { 470162306a36Sopenharmony_ci if (desc_count(info->tbufs[i])) 470262306a36Sopenharmony_ci break; /* buffer in use */ 470362306a36Sopenharmony_ci ++count; 470462306a36Sopenharmony_ci if (++i == info->tbuf_count) 470562306a36Sopenharmony_ci i=0; 470662306a36Sopenharmony_ci } while (i != info->tbuf_current); 470762306a36Sopenharmony_ci 470862306a36Sopenharmony_ci /* if tx DMA active, last zero count buffer is in use */ 470962306a36Sopenharmony_ci if (count && (rd_reg32(info, TDCSR) & BIT0)) 471062306a36Sopenharmony_ci --count; 471162306a36Sopenharmony_ci 471262306a36Sopenharmony_ci return count; 471362306a36Sopenharmony_ci} 471462306a36Sopenharmony_ci 471562306a36Sopenharmony_ci/* 471662306a36Sopenharmony_ci * return number of bytes in unsent transmit DMA buffers 471762306a36Sopenharmony_ci * and the serial controller tx FIFO 471862306a36Sopenharmony_ci */ 471962306a36Sopenharmony_cistatic unsigned int tbuf_bytes(struct slgt_info *info) 472062306a36Sopenharmony_ci{ 472162306a36Sopenharmony_ci unsigned int total_count = 0; 472262306a36Sopenharmony_ci unsigned int i = info->tbuf_current; 472362306a36Sopenharmony_ci unsigned int reg_value; 472462306a36Sopenharmony_ci unsigned int count; 472562306a36Sopenharmony_ci unsigned int active_buf_count = 0; 472662306a36Sopenharmony_ci 472762306a36Sopenharmony_ci /* 472862306a36Sopenharmony_ci * Add descriptor counts for all tx DMA buffers. 472962306a36Sopenharmony_ci * If count is zero (cleared by DMA controller after read), 473062306a36Sopenharmony_ci * the buffer is complete or is actively being read from. 473162306a36Sopenharmony_ci * 473262306a36Sopenharmony_ci * Record buf_count of last buffer with zero count starting 473362306a36Sopenharmony_ci * from current ring position. buf_count is mirror 473462306a36Sopenharmony_ci * copy of count and is not cleared by serial controller. 473562306a36Sopenharmony_ci * If DMA controller is active, that buffer is actively 473662306a36Sopenharmony_ci * being read so add to total. 473762306a36Sopenharmony_ci */ 473862306a36Sopenharmony_ci do { 473962306a36Sopenharmony_ci count = desc_count(info->tbufs[i]); 474062306a36Sopenharmony_ci if (count) 474162306a36Sopenharmony_ci total_count += count; 474262306a36Sopenharmony_ci else if (!total_count) 474362306a36Sopenharmony_ci active_buf_count = info->tbufs[i].buf_count; 474462306a36Sopenharmony_ci if (++i == info->tbuf_count) 474562306a36Sopenharmony_ci i = 0; 474662306a36Sopenharmony_ci } while (i != info->tbuf_current); 474762306a36Sopenharmony_ci 474862306a36Sopenharmony_ci /* read tx DMA status register */ 474962306a36Sopenharmony_ci reg_value = rd_reg32(info, TDCSR); 475062306a36Sopenharmony_ci 475162306a36Sopenharmony_ci /* if tx DMA active, last zero count buffer is in use */ 475262306a36Sopenharmony_ci if (reg_value & BIT0) 475362306a36Sopenharmony_ci total_count += active_buf_count; 475462306a36Sopenharmony_ci 475562306a36Sopenharmony_ci /* add tx FIFO count = reg_value[15..8] */ 475662306a36Sopenharmony_ci total_count += (reg_value >> 8) & 0xff; 475762306a36Sopenharmony_ci 475862306a36Sopenharmony_ci /* if transmitter active add one byte for shift register */ 475962306a36Sopenharmony_ci if (info->tx_active) 476062306a36Sopenharmony_ci total_count++; 476162306a36Sopenharmony_ci 476262306a36Sopenharmony_ci return total_count; 476362306a36Sopenharmony_ci} 476462306a36Sopenharmony_ci 476562306a36Sopenharmony_ci/* 476662306a36Sopenharmony_ci * load data into transmit DMA buffer ring and start transmitter if needed 476762306a36Sopenharmony_ci * return true if data accepted, otherwise false (buffers full) 476862306a36Sopenharmony_ci */ 476962306a36Sopenharmony_cistatic bool tx_load(struct slgt_info *info, const u8 *buf, unsigned int size) 477062306a36Sopenharmony_ci{ 477162306a36Sopenharmony_ci unsigned short count; 477262306a36Sopenharmony_ci unsigned int i; 477362306a36Sopenharmony_ci struct slgt_desc *d; 477462306a36Sopenharmony_ci 477562306a36Sopenharmony_ci /* check required buffer space */ 477662306a36Sopenharmony_ci if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info)) 477762306a36Sopenharmony_ci return false; 477862306a36Sopenharmony_ci 477962306a36Sopenharmony_ci DBGDATA(info, buf, size, "tx"); 478062306a36Sopenharmony_ci 478162306a36Sopenharmony_ci /* 478262306a36Sopenharmony_ci * copy data to one or more DMA buffers in circular ring 478362306a36Sopenharmony_ci * tbuf_start = first buffer for this data 478462306a36Sopenharmony_ci * tbuf_current = next free buffer 478562306a36Sopenharmony_ci * 478662306a36Sopenharmony_ci * Copy all data before making data visible to DMA controller by 478762306a36Sopenharmony_ci * setting descriptor count of the first buffer. 478862306a36Sopenharmony_ci * This prevents an active DMA controller from reading the first DMA 478962306a36Sopenharmony_ci * buffers of a frame and stopping before the final buffers are filled. 479062306a36Sopenharmony_ci */ 479162306a36Sopenharmony_ci 479262306a36Sopenharmony_ci info->tbuf_start = i = info->tbuf_current; 479362306a36Sopenharmony_ci 479462306a36Sopenharmony_ci while (size) { 479562306a36Sopenharmony_ci d = &info->tbufs[i]; 479662306a36Sopenharmony_ci 479762306a36Sopenharmony_ci count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size); 479862306a36Sopenharmony_ci memcpy(d->buf, buf, count); 479962306a36Sopenharmony_ci 480062306a36Sopenharmony_ci size -= count; 480162306a36Sopenharmony_ci buf += count; 480262306a36Sopenharmony_ci 480362306a36Sopenharmony_ci /* 480462306a36Sopenharmony_ci * set EOF bit for last buffer of HDLC frame or 480562306a36Sopenharmony_ci * for every buffer in raw mode 480662306a36Sopenharmony_ci */ 480762306a36Sopenharmony_ci if ((!size && info->params.mode == MGSL_MODE_HDLC) || 480862306a36Sopenharmony_ci info->params.mode == MGSL_MODE_RAW) 480962306a36Sopenharmony_ci set_desc_eof(*d, 1); 481062306a36Sopenharmony_ci else 481162306a36Sopenharmony_ci set_desc_eof(*d, 0); 481262306a36Sopenharmony_ci 481362306a36Sopenharmony_ci /* set descriptor count for all but first buffer */ 481462306a36Sopenharmony_ci if (i != info->tbuf_start) 481562306a36Sopenharmony_ci set_desc_count(*d, count); 481662306a36Sopenharmony_ci d->buf_count = count; 481762306a36Sopenharmony_ci 481862306a36Sopenharmony_ci if (++i == info->tbuf_count) 481962306a36Sopenharmony_ci i = 0; 482062306a36Sopenharmony_ci } 482162306a36Sopenharmony_ci 482262306a36Sopenharmony_ci info->tbuf_current = i; 482362306a36Sopenharmony_ci 482462306a36Sopenharmony_ci /* set first buffer count to make new data visible to DMA controller */ 482562306a36Sopenharmony_ci d = &info->tbufs[info->tbuf_start]; 482662306a36Sopenharmony_ci set_desc_count(*d, d->buf_count); 482762306a36Sopenharmony_ci 482862306a36Sopenharmony_ci /* start transmitter if needed and update transmit timeout */ 482962306a36Sopenharmony_ci if (!info->tx_active) 483062306a36Sopenharmony_ci tx_start(info); 483162306a36Sopenharmony_ci update_tx_timer(info); 483262306a36Sopenharmony_ci 483362306a36Sopenharmony_ci return true; 483462306a36Sopenharmony_ci} 483562306a36Sopenharmony_ci 483662306a36Sopenharmony_cistatic int register_test(struct slgt_info *info) 483762306a36Sopenharmony_ci{ 483862306a36Sopenharmony_ci static unsigned short patterns[] = 483962306a36Sopenharmony_ci {0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696}; 484062306a36Sopenharmony_ci static unsigned int count = ARRAY_SIZE(patterns); 484162306a36Sopenharmony_ci unsigned int i; 484262306a36Sopenharmony_ci int rc = 0; 484362306a36Sopenharmony_ci 484462306a36Sopenharmony_ci for (i=0 ; i < count ; i++) { 484562306a36Sopenharmony_ci wr_reg16(info, TIR, patterns[i]); 484662306a36Sopenharmony_ci wr_reg16(info, BDR, patterns[(i+1)%count]); 484762306a36Sopenharmony_ci if ((rd_reg16(info, TIR) != patterns[i]) || 484862306a36Sopenharmony_ci (rd_reg16(info, BDR) != patterns[(i+1)%count])) { 484962306a36Sopenharmony_ci rc = -ENODEV; 485062306a36Sopenharmony_ci break; 485162306a36Sopenharmony_ci } 485262306a36Sopenharmony_ci } 485362306a36Sopenharmony_ci info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0; 485462306a36Sopenharmony_ci info->init_error = rc ? 0 : DiagStatus_AddressFailure; 485562306a36Sopenharmony_ci return rc; 485662306a36Sopenharmony_ci} 485762306a36Sopenharmony_ci 485862306a36Sopenharmony_cistatic int irq_test(struct slgt_info *info) 485962306a36Sopenharmony_ci{ 486062306a36Sopenharmony_ci unsigned long timeout; 486162306a36Sopenharmony_ci unsigned long flags; 486262306a36Sopenharmony_ci struct tty_struct *oldtty = info->port.tty; 486362306a36Sopenharmony_ci u32 speed = info->params.data_rate; 486462306a36Sopenharmony_ci 486562306a36Sopenharmony_ci info->params.data_rate = 921600; 486662306a36Sopenharmony_ci info->port.tty = NULL; 486762306a36Sopenharmony_ci 486862306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 486962306a36Sopenharmony_ci async_mode(info); 487062306a36Sopenharmony_ci slgt_irq_on(info, IRQ_TXIDLE); 487162306a36Sopenharmony_ci 487262306a36Sopenharmony_ci /* enable transmitter */ 487362306a36Sopenharmony_ci wr_reg16(info, TCR, 487462306a36Sopenharmony_ci (unsigned short)(rd_reg16(info, TCR) | BIT1)); 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci /* write one byte and wait for tx idle */ 487762306a36Sopenharmony_ci wr_reg16(info, TDR, 0); 487862306a36Sopenharmony_ci 487962306a36Sopenharmony_ci /* assume failure */ 488062306a36Sopenharmony_ci info->init_error = DiagStatus_IrqFailure; 488162306a36Sopenharmony_ci info->irq_occurred = false; 488262306a36Sopenharmony_ci 488362306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 488462306a36Sopenharmony_ci 488562306a36Sopenharmony_ci timeout=100; 488662306a36Sopenharmony_ci while(timeout-- && !info->irq_occurred) 488762306a36Sopenharmony_ci msleep_interruptible(10); 488862306a36Sopenharmony_ci 488962306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 489062306a36Sopenharmony_ci reset_port(info); 489162306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 489262306a36Sopenharmony_ci 489362306a36Sopenharmony_ci info->params.data_rate = speed; 489462306a36Sopenharmony_ci info->port.tty = oldtty; 489562306a36Sopenharmony_ci 489662306a36Sopenharmony_ci info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure; 489762306a36Sopenharmony_ci return info->irq_occurred ? 0 : -ENODEV; 489862306a36Sopenharmony_ci} 489962306a36Sopenharmony_ci 490062306a36Sopenharmony_cistatic int loopback_test_rx(struct slgt_info *info) 490162306a36Sopenharmony_ci{ 490262306a36Sopenharmony_ci unsigned char *src, *dest; 490362306a36Sopenharmony_ci int count; 490462306a36Sopenharmony_ci 490562306a36Sopenharmony_ci if (desc_complete(info->rbufs[0])) { 490662306a36Sopenharmony_ci count = desc_count(info->rbufs[0]); 490762306a36Sopenharmony_ci src = info->rbufs[0].buf; 490862306a36Sopenharmony_ci dest = info->tmp_rbuf; 490962306a36Sopenharmony_ci 491062306a36Sopenharmony_ci for( ; count ; count-=2, src+=2) { 491162306a36Sopenharmony_ci /* src=data byte (src+1)=status byte */ 491262306a36Sopenharmony_ci if (!(*(src+1) & (BIT9 + BIT8))) { 491362306a36Sopenharmony_ci *dest = *src; 491462306a36Sopenharmony_ci dest++; 491562306a36Sopenharmony_ci info->tmp_rbuf_count++; 491662306a36Sopenharmony_ci } 491762306a36Sopenharmony_ci } 491862306a36Sopenharmony_ci DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx"); 491962306a36Sopenharmony_ci return 1; 492062306a36Sopenharmony_ci } 492162306a36Sopenharmony_ci return 0; 492262306a36Sopenharmony_ci} 492362306a36Sopenharmony_ci 492462306a36Sopenharmony_cistatic int loopback_test(struct slgt_info *info) 492562306a36Sopenharmony_ci{ 492662306a36Sopenharmony_ci#define TESTFRAMESIZE 20 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_ci unsigned long timeout; 492962306a36Sopenharmony_ci u16 count; 493062306a36Sopenharmony_ci unsigned char buf[TESTFRAMESIZE]; 493162306a36Sopenharmony_ci int rc = -ENODEV; 493262306a36Sopenharmony_ci unsigned long flags; 493362306a36Sopenharmony_ci 493462306a36Sopenharmony_ci struct tty_struct *oldtty = info->port.tty; 493562306a36Sopenharmony_ci MGSL_PARAMS params; 493662306a36Sopenharmony_ci 493762306a36Sopenharmony_ci memcpy(¶ms, &info->params, sizeof(params)); 493862306a36Sopenharmony_ci 493962306a36Sopenharmony_ci info->params.mode = MGSL_MODE_ASYNC; 494062306a36Sopenharmony_ci info->params.data_rate = 921600; 494162306a36Sopenharmony_ci info->params.loopback = 1; 494262306a36Sopenharmony_ci info->port.tty = NULL; 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci /* build and send transmit frame */ 494562306a36Sopenharmony_ci for (count = 0; count < TESTFRAMESIZE; ++count) 494662306a36Sopenharmony_ci buf[count] = (unsigned char)count; 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_ci info->tmp_rbuf_count = 0; 494962306a36Sopenharmony_ci memset(info->tmp_rbuf, 0, TESTFRAMESIZE); 495062306a36Sopenharmony_ci 495162306a36Sopenharmony_ci /* program hardware for HDLC and enabled receiver */ 495262306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 495362306a36Sopenharmony_ci async_mode(info); 495462306a36Sopenharmony_ci rx_start(info); 495562306a36Sopenharmony_ci tx_load(info, buf, count); 495662306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 495762306a36Sopenharmony_ci 495862306a36Sopenharmony_ci /* wait for receive complete */ 495962306a36Sopenharmony_ci for (timeout = 100; timeout; --timeout) { 496062306a36Sopenharmony_ci msleep_interruptible(10); 496162306a36Sopenharmony_ci if (loopback_test_rx(info)) { 496262306a36Sopenharmony_ci rc = 0; 496362306a36Sopenharmony_ci break; 496462306a36Sopenharmony_ci } 496562306a36Sopenharmony_ci } 496662306a36Sopenharmony_ci 496762306a36Sopenharmony_ci /* verify received frame length and contents */ 496862306a36Sopenharmony_ci if (!rc && (info->tmp_rbuf_count != count || 496962306a36Sopenharmony_ci memcmp(buf, info->tmp_rbuf, count))) { 497062306a36Sopenharmony_ci rc = -ENODEV; 497162306a36Sopenharmony_ci } 497262306a36Sopenharmony_ci 497362306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 497462306a36Sopenharmony_ci reset_adapter(info); 497562306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 497662306a36Sopenharmony_ci 497762306a36Sopenharmony_ci memcpy(&info->params, ¶ms, sizeof(info->params)); 497862306a36Sopenharmony_ci info->port.tty = oldtty; 497962306a36Sopenharmony_ci 498062306a36Sopenharmony_ci info->init_error = rc ? DiagStatus_DmaFailure : 0; 498162306a36Sopenharmony_ci return rc; 498262306a36Sopenharmony_ci} 498362306a36Sopenharmony_ci 498462306a36Sopenharmony_cistatic int adapter_test(struct slgt_info *info) 498562306a36Sopenharmony_ci{ 498662306a36Sopenharmony_ci DBGINFO(("testing %s\n", info->device_name)); 498762306a36Sopenharmony_ci if (register_test(info) < 0) { 498862306a36Sopenharmony_ci printk("register test failure %s addr=%08X\n", 498962306a36Sopenharmony_ci info->device_name, info->phys_reg_addr); 499062306a36Sopenharmony_ci } else if (irq_test(info) < 0) { 499162306a36Sopenharmony_ci printk("IRQ test failure %s IRQ=%d\n", 499262306a36Sopenharmony_ci info->device_name, info->irq_level); 499362306a36Sopenharmony_ci } else if (loopback_test(info) < 0) { 499462306a36Sopenharmony_ci printk("loopback test failure %s\n", info->device_name); 499562306a36Sopenharmony_ci } 499662306a36Sopenharmony_ci return info->init_error; 499762306a36Sopenharmony_ci} 499862306a36Sopenharmony_ci 499962306a36Sopenharmony_ci/* 500062306a36Sopenharmony_ci * transmit timeout handler 500162306a36Sopenharmony_ci */ 500262306a36Sopenharmony_cistatic void tx_timeout(struct timer_list *t) 500362306a36Sopenharmony_ci{ 500462306a36Sopenharmony_ci struct slgt_info *info = from_timer(info, t, tx_timer); 500562306a36Sopenharmony_ci unsigned long flags; 500662306a36Sopenharmony_ci 500762306a36Sopenharmony_ci DBGINFO(("%s tx_timeout\n", info->device_name)); 500862306a36Sopenharmony_ci if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { 500962306a36Sopenharmony_ci info->icount.txtimeout++; 501062306a36Sopenharmony_ci } 501162306a36Sopenharmony_ci spin_lock_irqsave(&info->lock,flags); 501262306a36Sopenharmony_ci tx_stop(info); 501362306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock,flags); 501462306a36Sopenharmony_ci 501562306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC 501662306a36Sopenharmony_ci if (info->netcount) 501762306a36Sopenharmony_ci hdlcdev_tx_done(info); 501862306a36Sopenharmony_ci else 501962306a36Sopenharmony_ci#endif 502062306a36Sopenharmony_ci bh_transmit(info); 502162306a36Sopenharmony_ci} 502262306a36Sopenharmony_ci 502362306a36Sopenharmony_ci/* 502462306a36Sopenharmony_ci * receive buffer polling timer 502562306a36Sopenharmony_ci */ 502662306a36Sopenharmony_cistatic void rx_timeout(struct timer_list *t) 502762306a36Sopenharmony_ci{ 502862306a36Sopenharmony_ci struct slgt_info *info = from_timer(info, t, rx_timer); 502962306a36Sopenharmony_ci unsigned long flags; 503062306a36Sopenharmony_ci 503162306a36Sopenharmony_ci DBGINFO(("%s rx_timeout\n", info->device_name)); 503262306a36Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 503362306a36Sopenharmony_ci info->pending_bh |= BH_RECEIVE; 503462306a36Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 503562306a36Sopenharmony_ci bh_handler(&info->task); 503662306a36Sopenharmony_ci} 503762306a36Sopenharmony_ci 5038