.MX6 processor family.
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/tty/synclink_gt.c
blob: 1abf946463f664522d1721a1537a4fdba9d45c25 (plain) (tree)
1
  








































                                                                                                                        

                                          

 
                         














                            
                           









                            
                           
 





                        



                                                                                               





                                             



                                             
                      


                                                                                  
                                                                                   











                                                                                   
                                     

  
                           









                                          



                                           



                                                                                           



                            




                                                              
                                                                              

                                                                               
                                                              







                                                                 
                                                                               


                                                    
                                                              



                                     
                         















                                                                    













                                                                           



                                          



                                                               




                                                                     
                                 





                                                                                                                               
                                                                           























                                                                            
                             



                                                              










                                                                
                                                                              


                                       

                                                                        


                                                   




                                                    


                                             



                                                                         

                          

                         

                                                           








                                                                                
                                




                                                                 
                                     
                            
                             
                                

                           


                           

                        
 

                        

                                                            
                                                   



                              
                       
                                 













                                                                              

                                       











                                          
                           
                         






























                                                      

                          


                                      




                                              














                                                  

                                              












































                                                                                               
                                              





                                                                                      

                                                 




                                                            
                                                       

                                                
                                                                                 






                                                            
                                                 




                                                                     


























                                                                                      
                                             

                                                                      
                                                              

                                                                       


                                                                             



                                                                   

















































































































                                                                                                  

                                                                      












                                                          
                                        














                                                                                    
                             
 
                                                                                        

                                                            



                                                                       



                                                
                                      
                                                                                




                                                              
                                                

                             
                           

                                                      
                                    

                                                            

                                                        
                                     
                 
         
                                        










                                                                                   


                                                                                      











                                                                
                                                                                     
 
                                                              

                             
                                      
                                                 
                                                    
                          


                             
                                        
 
                                             
                              
        
                                                                                    




                                                  
                            





                                                    

                                      

                       
                                                   


                                                 

                                                        
 
                                                     

 
                                                                             





                                                         



                                            
                                              
                                                                        






                                                          
                                           
                                                  
                                                        









                                                            
                                                




                                    











                                                                                







                                                     

                            

                                                                   

                                                            
 

                                                      
 
                                              
 
                             
                                                            


                                                                 

         

                                      

        
                                                   



                                                              
                                                             


                                                  
                    

                                                      
                         
                                                              
                          
                         
                                             
                                                    
                                                    

                        
                                                  
                   




























                                                                   
                                                    


























                                                                           































                                                                                           

                                                                          











                                                          


                                                   
 






























                                                                     



                                                                          







                                            




                                            
                                        


                                                     
                                               
                





                                                                   
                                  



                                                     




                                                       





                                             







                                                 

                                      

                             

                                             
                             

                                             
                             

                                             
                             

                                                 
                              

                                                
                              

                                                
                             

                                     
                            

                                            
                         

                                                
                         

                                                   
                
                                   
         
                                        
                   

 


























                                                                      
  







                                                                                          
                                                   


























                                                                                         
















                                                                          

                                 
                         



                 
                                                     



























                                                                          

                            





                              

                            
                                          










                                                                                    

                  
                                                                        

                          

                            
                                                             























                                                             
                                                       

                                                                     
                                                                           
                                         
                                                                           
                                         
                                                                           
                                        
                                                                         
                                        
                                                                         
                                       
                                                                       
                
                                                    

                                                                 
                                                                    
                                        
                                                                     
                                     
                                                                   
                                         
                                                                      


                                                
                                           
 
                                                                          

                                                                           



                                            
                                                             
 

                               
                                            


                                
                                   

                                         

                 
 


                                                                        

 







                                                             





                                                  
                  

                                                             


                                                                         














                                                                
                                             























                                                                       
                                             










                                                          
                                                             





                                                       
                               









                                                                        
                 

 
                         


















                                                                               
                             






















                                                                                    
                                            












                                                         
   

                                                             

                                                  



                                               


                                    


                                                     
                                       

                                        
 


                                                            





                                                   
 
                            















                                                  


                                         







                                                     
                                                           














                                                              

                                                             









                                                             



                                             




























                                                      
                                






















                                                                            
                             




                                                 

                                               



















































































                                                                                                                



                                                        

                                       































                                                                   




                                                                          
                                        


                       
                                              
 
                                                  
 

                                    

                      

 








                                                  


























                                                                                     

                                           





































                                                                                    





                                                   


                         










                                                                             
                                

                                     

                                 

                                                                
                                                         
                                                       



                                                                              
                                                  
                                                          
                                                       
                                                         
                         

                                                                    




                                                          
                                                                













                                                                     

                                                  






















                                                    

                                           










                                                  
                                                
 
                                                                            

                   
                                












                                                                      

                                                
                                             


























                                                                          
                                                

                                                       
                
                                

 
                                                                     
 






                                                     





                                                                                   




                                                          
                                                                     
 






                                                     





                                                                                   



                                                          
                                                

                                                         
                                                                       
                                                                       




                                                                        
                                                                       




                         
                                                                     
 






                                                     





                                                                                   
                         





                                                        




                                                          
                                                
                                                     
                                                                     
                      

                                                           



                 
                                                                    
 






                                                    




                                                                                  
                           




                                                          

































                                                                                







                                                                           
                                  


                                                   
                                            

                                                        

                                                          


                                                                           
                                             

                                                                                  
                                                                                                

                                                                                 






                                                        

                                                          












                                                                   
                                         
                             
                                         
                             
                                         
                            
                                        






















                                                                          
                                        




























                                                                         





























                                                                           





                                                                      













                                                                                      




                                              
                                        




                                                                                        
                                                          


                                          
                         




                                              
                                                                                                        







                                                        

















                                                                                      



                                                                
   
                                                          
 
                                        


                         
                                                                   
 

                                                                  
                                          


                                                        
                                                              





                                                                
                                                                


                 


                                     
                                       










                                                                                      
                                         

         

                                                             



                                                           



                                                                      
                                                  
                 
                                         

         
                                                                  






                                                     
                                                 
















                                                                                     

                                                                
 
                                              










                                                       
                                                    





















                                                            
                                                                         
                                                                        


                                  

                                            

                                                  

                                                              
 
                                               










                                              
                                                   
                           
                                









                                 
                                                                


                             
                                                                        












                                                  
                            


                                                           
                                                
 

                                                                   
                          
                                                                     
            
                                                                        























                                                                                              
                                                                   







                                                                     
                                                   
            
                                                    

                           
                                                    
            
                                                   



                                             
                                    
                                                                     
                                                                 
                                                     
                                     
                                                                       
                                       



                                                                  
                                             



































                                                                                         



                                                                        

                                                   
                         

















                                                                            

                                                 



































                                                                   

                                                                     
                                             






                                                              

                                                                              
                                       
                 
                                                        



                                                        









                                                                           


                                       



                                                                        







































































































































                                                                                              
                           


















                                                                        






























































                                                                       






















                                                                               
                                                             











                                                    
                                                                  



































































































                                                                                
                                                             










                                                                
                                                                          




                                               
                                                                     





                                                                 
                                                                  





                                                                      




















































                                                                      
                                           


























                                                                         
                                           





















                                                                         










                                                                            
                                                  




                                                                            
               
                                                                     
            
                                                                        




                                                  







                                                                     

                                            
                              

                                            




                                                                            
                                                   


                         
                                          
                                 


                                                                   
                                                            




                                                                 
                                                


                                              
                                   
                              

                                                   
                             

                   
                                                                              
                                                     


                                                      

                                                                               



                                                               
                                                   
 
                                                                         
                              






                                                                          
                                
                           
                              


                                        
                                                   

                        

                              

                    
                                                   




                                                                                  





                                                                

                                                 
                                                                       

                                   






                                                                         






                                                 

                              






















































































































                                                                                                                           
                                                
 
                                                                             
                              
                                                                     



                                                                     










                                                     
                                            



                                                                       
                                                 


                             
                                        

















                                                                       





















                                                                 


                                    













                                                                 
                         



                           

                                                         
                           

  






                                                                                       
                                                             




                                                                      
                                           
                                                
                                         
                                                   
                                            
                                            
                                                   

                                                 







                                                                          

                                                                              





                                                                     
                                                   
                                              












                                                                                      


                                                        





                                                                

                                                                       
                                                     
                         


















                                                                                  













                                                                            
                                                            
                                                    
                                                          
                                                                                      

                                                                                          

                 
 




                                                                                

 
                                        










                                                              
                                           


 
                                          








                                           
                                          




                                           





                                   
                                 
                                            







                               
                                                     

                            

                                                                                   














                                                                         
                         







                                                
                                             













                                                   
                                              
 

                                                      

                                                                      



                                                 








                                                      

                                                    
                                                                            







                                                                           

                                                  
 




                                                                             
                              



                                                            





















                                                                          


                                                    





































































                                                                            
                                                       


























                                                                          
                                            






























                                                                              

                                 


















                                                                              







                                                                                    
                











                                                                                   






                                                                          

                                 





                                            
                                                                                 
                                        

         
                                                        
                                                  






                                                                          
                                                                         






                                                                                        
                        



                                                        
                 


                                                                           
                                       













                                                                              







                                                                        

                                 









                                              
                                                                



















































































































                                                            













                                          
                                            




                                                  










                                                                     



                                                    



                                      
                                             








                                                 






                                                










                                        
                   
 
                                   


                                     



                                                             













                                                                                   
                                                      


































                                                                    






                                                









                                      
                                   


                                     



                                                             











                                                                                   
                                                      




































































































                                                                                 

                           
 




























                                                                                   











                                                    

                                                             







































                                                           

                                                     





































                                                                                 
                                                                      













                                                  

                                  




                                         
                                                  
   
                                                



                                   
                            
                                                
                                        





                                                        











































                                                                          
                                                                     







                                                                                  

                                                          
                              




                                                                  
 
                         
                             

                                                      
         
      


                                                       
                                                                                                  

                        





                                                                    










                                                                          
                                                                                                  






                                                                             




                                                                            
                         







                                                                                                  
                    

        
                     



                                                          
                                                   
   
                                              

                                            
                           

                                           
                             



                                           
                             







                                                              
                                                                     
                                                         
                               
                    




























                                                           

                                                                





                        

















































                                                                    

                                                                          
   
                                                                               




                             


                                                                   


                                       










                                                                               



                                                  






                                                                                  






                                                                     


                                            


                                                                   
                                     


                                            


                               










                                                                               





                                                                 
                                                         











                                                                     
                                                                  







                                                              
                                                   


                                           
                              













                                                              
                                   











                                                   
                                






































                                                                          
                                                   






                                                       
                              











                                                            
                                  





















                                                             
                                







                                                          
                                      

                                                                
                                        

                                                            
                                             

















                                                                        
                      

                                                  
                         


















                                                            
                                

 
/*
 * Device driver for Microgate SyncLink GT serial adapters.
 *
 * written by Paul Fulghum for Microgate Corporation
 * paulkf@microgate.com
 *
 * Microgate and SyncLink are trademarks of Microgate Corporation
 *
 * This code is released under the GNU General Public License (GPL)
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * DEBUG OUTPUT DEFINITIONS
 *
 * uncomment lines below to enable specific types of debug output
 *
 * DBGINFO   information - most verbose output
 * DBGERR    serious errors
 * DBGBH     bottom half service routine debugging
 * DBGISR    interrupt service routine debugging
 * DBGDATA   output receive and transmit data
 * DBGTBUF   output transmit DMA buffers and registers
 * DBGRBUF   output receive DMA buffers and registers
 */

#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt
#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt
#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
/*#define DBGTBUF(info) dump_tbufs(info)*/
/*#define DBGRBUF(info) dump_rbufs(info)*/


#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/termios.h>
#include <linux/bitops.h>
#include <linux/workqueue.h>
#include <linux/hdlc.h>
#include <linux/synclink.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/types.h>
#include <asm/uaccess.h>

#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE))
#define SYNCLINK_GENERIC_HDLC 1
#else
#define SYNCLINK_GENERIC_HDLC 0
#endif

/*
 * module identification
 */
static char *driver_name     = "SyncLink GT";
static char *tty_driver_name = "synclink_gt";
static char *tty_dev_prefix  = "ttySLG";
MODULE_LICENSE("GPL");
#define MGSL_MAGIC 0x5401
#define MAX_DEVICES 32

static struct pci_device_id pci_table[] = {
	{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
	{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT2_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
	{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT4_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
	{PCI_VENDOR_ID_MICROGATE, SYNCLINK_AC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
	{0,}, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_table);

static int  init_one(struct pci_dev *dev,const struct pci_device_id *ent);
static void remove_one(struct pci_dev *dev);
static struct pci_driver pci_driver = {
	.name		= "synclink_gt",
	.id_table	= pci_table,
	.probe		= init_one,
	.remove		= remove_one,
};

static bool pci_registered;

/*
 * module configuration and status
 */
static struct slgt_info *slgt_device_list;
static int slgt_device_count;

static int ttymajor;
static int debug_level;
static int maxframe[MAX_DEVICES];

module_param(ttymajor, int, 0);
module_param(debug_level, int, 0);
module_param_array(maxframe, int, NULL, 0);

MODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned");
MODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail");
MODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)");

/*
 * tty support and callbacks
 */
static struct tty_driver *serial_driver;

static int  open(struct tty_struct *tty, struct file * filp);
static void close(struct tty_struct *tty, struct file * filp);
static void hangup(struct tty_struct *tty);
static void set_termios(struct tty_struct *tty, struct ktermios *old_termios);

static int  write(struct tty_struct *tty, const unsigned char *buf, int count);
static int put_char(struct tty_struct *tty, unsigned char ch);
static void send_xchar(struct tty_struct *tty, char ch);
static void wait_until_sent(struct tty_struct *tty, int timeout);
static int  write_room(struct tty_struct *tty);
static void flush_chars(struct tty_struct *tty);
static void flush_buffer(struct tty_struct *tty);
static void tx_hold(struct tty_struct *tty);
static void tx_release(struct tty_struct *tty);

static int  ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
static int  chars_in_buffer(struct tty_struct *tty);
static void throttle(struct tty_struct * tty);
static void unthrottle(struct tty_struct * tty);
static int set_break(struct tty_struct *tty, int break_state);

/*
 * generic HDLC support and callbacks
 */
#if SYNCLINK_GENERIC_HDLC
#define dev_to_port(D) (dev_to_hdlc(D)->priv)
static void hdlcdev_tx_done(struct slgt_info *info);
static void hdlcdev_rx(struct slgt_info *info, char *buf, int size);
static int  hdlcdev_init(struct slgt_info *info);
static void hdlcdev_exit(struct slgt_info *info);
#endif


/*
 * device specific structures, macros and functions
 */

#define SLGT_MAX_PORTS 4
#define SLGT_REG_SIZE  256

/*
 * conditional wait facility
 */
struct cond_wait {
	struct cond_wait *next;
	wait_queue_head_t q;
	wait_queue_t wait;
	unsigned int data;
};
static void init_cond_wait(struct cond_wait *w, unsigned int data);
static void add_cond_wait(struct cond_wait **head, struct cond_wait *w);
static void remove_cond_wait(struct cond_wait **head, struct cond_wait *w);
static void flush_cond_wait(struct cond_wait **head);

/*
 * DMA buffer descriptor and access macros
 */
struct slgt_desc
{
	__le16 count;
	__le16 status;
	__le32 pbuf;  /* physical address of data buffer */
	__le32 next;  /* physical address of next descriptor */

	/* driver book keeping */
	char *buf;          /* virtual  address of data buffer */
    	unsigned int pdesc; /* physical address of this descriptor */
	dma_addr_t buf_dma_addr;
	unsigned short buf_count;
};

#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b))
#define set_desc_next(a,b) (a).next   = cpu_to_le32((unsigned int)(b))
#define set_desc_count(a,b)(a).count  = cpu_to_le16((unsigned short)(b))
#define set_desc_eof(a,b)  (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0))
#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b))
#define desc_count(a)      (le16_to_cpu((a).count))
#define desc_status(a)     (le16_to_cpu((a).status))
#define desc_complete(a)   (le16_to_cpu((a).status) & BIT15)
#define desc_eof(a)        (le16_to_cpu((a).status) & BIT2)
#define desc_crc_error(a)  (le16_to_cpu((a).status) & BIT1)
#define desc_abort(a)      (le16_to_cpu((a).status) & BIT0)
#define desc_residue(a)    ((le16_to_cpu((a).status) & 0x38) >> 3)

struct _input_signal_events {
	int ri_up;
	int ri_down;
	int dsr_up;
	int dsr_down;
	int dcd_up;
	int dcd_down;
	int cts_up;
	int cts_down;
};

/*
 * device instance data structure
 */
struct slgt_info {
	void *if_ptr;		/* General purpose pointer (used by SPPP) */
	struct tty_port port;

	struct slgt_info *next_device;	/* device list link */

	int magic;

	char device_name[25];
	struct pci_dev *pdev;

	int port_count;  /* count of ports on adapter */
	int adapter_num; /* adapter instance number */
	int port_num;    /* port instance number */

	/* array of pointers to port contexts on this adapter */
	struct slgt_info *port_array[SLGT_MAX_PORTS];

	int			line;		/* tty line instance number */

	struct mgsl_icount	icount;

	int			timeout;
	int			x_char;		/* xon/xoff character */
	unsigned int		read_status_mask;
	unsigned int 		ignore_status_mask;

	wait_queue_head_t	status_event_wait_q;
	wait_queue_head_t	event_wait_q;
	struct timer_list	tx_timer;
	struct timer_list	rx_timer;

	unsigned int            gpio_present;
	struct cond_wait        *gpio_wait_q;

	spinlock_t lock;	/* spinlock for synchronizing with ISR */

	struct work_struct task;
	u32 pending_bh;
	bool bh_requested;
	bool bh_running;

	int isr_overflow;
	bool irq_requested;	/* true if IRQ requested */
	bool irq_occurred;	/* for diagnostics use */

	/* device configuration */

	unsigned int bus_type;
	unsigned int irq_level;
	unsigned long irq_flags;

	unsigned char __iomem * reg_addr;  /* memory mapped registers address */
	u32 phys_reg_addr;
	bool reg_addr_requested;

	MGSL_PARAMS params;       /* communications parameters */
	u32 idle_mode;
	u32 max_frame_size;       /* as set by device config */

	unsigned int rbuf_fill_level;
	unsigned int rx_pio;
	unsigned int if_mode;
	unsigned int base_clock;
	unsigned int xsync;
	unsigned int xctrl;

	/* device status */

	bool rx_enabled;
	bool rx_restart;

	bool tx_enabled;
	bool tx_active;

	unsigned char signals;    /* serial signal states */
	int init_error;  /* initialization error */

	unsigned char *tx_buf;
	int tx_count;

	char *flag_buf;
	bool drop_rts_on_tx_done;
	struct	_input_signal_events	input_signal_events;

	int dcd_chkcount;	/* check counts to prevent */
	int cts_chkcount;	/* too many IRQs if a signal */
	int dsr_chkcount;	/* is floating */
	int ri_chkcount;

	char *bufs;		/* virtual address of DMA buffer lists */
	dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */

	unsigned int rbuf_count;
	struct slgt_desc *rbufs;
	unsigned int rbuf_current;
	unsigned int rbuf_index;
	unsigned int rbuf_fill_index;
	unsigned short rbuf_fill_count;

	unsigned int tbuf_count;
	struct slgt_desc *tbufs;
	unsigned int tbuf_current;
	unsigned int tbuf_start;

	unsigned char *tmp_rbuf;
	unsigned int tmp_rbuf_count;

	/* SPPP/Cisco HDLC device parts */

	int netcount;
	spinlock_t netlock;
#if SYNCLINK_GENERIC_HDLC
	struct net_device *netdev;
#endif

};

static MGSL_PARAMS default_params = {
	.mode            = MGSL_MODE_HDLC,
	.loopback        = 0,
	.flags           = HDLC_FLAG_UNDERRUN_ABORT15,
	.encoding        = HDLC_ENCODING_NRZI_SPACE,
	.clock_speed     = 0,
	.addr_filter     = 0xff,
	.crc_type        = HDLC_CRC_16_CCITT,
	.preamble_length = HDLC_PREAMBLE_LENGTH_8BITS,
	.preamble        = HDLC_PREAMBLE_PATTERN_NONE,
	.data_rate       = 9600,
	.data_bits       = 8,
	.stop_bits       = 1,
	.parity          = ASYNC_PARITY_NONE
};


#define BH_RECEIVE  1
#define BH_TRANSMIT 2
#define BH_STATUS   4
#define IO_PIN_SHUTDOWN_LIMIT 100

#define DMABUFSIZE 256
#define DESC_LIST_SIZE 4096

#define MASK_PARITY  BIT1
#define MASK_FRAMING BIT0
#define MASK_BREAK   BIT14
#define MASK_OVERRUN BIT4

#define GSR   0x00 /* global status */
#define JCR   0x04 /* JTAG control */
#define IODR  0x08 /* GPIO direction */
#define IOER  0x0c /* GPIO interrupt enable */
#define IOVR  0x10 /* GPIO value */
#define IOSR  0x14 /* GPIO interrupt status */
#define TDR   0x80 /* tx data */
#define RDR   0x80 /* rx data */
#define TCR   0x82 /* tx control */
#define TIR   0x84 /* tx idle */
#define TPR   0x85 /* tx preamble */
#define RCR   0x86 /* rx control */
#define VCR   0x88 /* V.24 control */
#define CCR   0x89 /* clock control */
#define BDR   0x8a /* baud divisor */
#define SCR   0x8c /* serial control */
#define SSR   0x8e /* serial status */
#define RDCSR 0x90 /* rx DMA control/status */
#define TDCSR 0x94 /* tx DMA control/status */
#define RDDAR 0x98 /* rx DMA descriptor address */
#define TDDAR 0x9c /* tx DMA descriptor address */
#define XSR   0x40 /* extended sync pattern */
#define XCR   0x44 /* extended control */

#define RXIDLE      BIT14
#define RXBREAK     BIT14
#define IRQ_TXDATA  BIT13
#define IRQ_TXIDLE  BIT12
#define IRQ_TXUNDER BIT11 /* HDLC */
#define IRQ_RXDATA  BIT10
#define IRQ_RXIDLE  BIT9  /* HDLC */
#define IRQ_RXBREAK BIT9  /* async */
#define IRQ_RXOVER  BIT8
#define IRQ_DSR     BIT7
#define IRQ_CTS     BIT6
#define IRQ_DCD     BIT5
#define IRQ_RI      BIT4
#define IRQ_ALL     0x3ff0
#define IRQ_MASTER  BIT0

#define slgt_irq_on(info, mask) \
	wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask)))
#define slgt_irq_off(info, mask) \
	wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask)))

static __u8  rd_reg8(struct slgt_info *info, unsigned int addr);
static void  wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value);
static __u16 rd_reg16(struct slgt_info *info, unsigned int addr);
static void  wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value);
static __u32 rd_reg32(struct slgt_info *info, unsigned int addr);
static void  wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value);

static void  msc_set_vcr(struct slgt_info *info);

static int  startup(struct slgt_info *info);
static int  block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info);
static void shutdown(struct slgt_info *info);
static void program_hw(struct slgt_info *info);
static void change_params(struct slgt_info *info);

static int  register_test(struct slgt_info *info);
static int  irq_test(struct slgt_info *info);
static int  loopback_test(struct slgt_info *info);
static int  adapter_test(struct slgt_info *info);

static void reset_adapter(struct slgt_info *info);
static void reset_port(struct slgt_info *info);
static void async_mode(struct slgt_info *info);
static void sync_mode(struct slgt_info *info);

static void rx_stop(struct slgt_info *info);
static void rx_start(struct slgt_info *info);
static void reset_rbufs(struct slgt_info *info);
static void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last);
static void rdma_reset(struct slgt_info *info);
static bool rx_get_frame(struct slgt_info *info);
static bool rx_get_buf(struct slgt_info *info);

static void tx_start(struct slgt_info *info);
static void tx_stop(struct slgt_info *info);
static void tx_set_idle(struct slgt_info *info);
static unsigned int free_tbuf_count(struct slgt_info *info);
static unsigned int tbuf_bytes(struct slgt_info *info);
static void reset_tbufs(struct slgt_info *info);
static void tdma_reset(struct slgt_info *info);
static bool tx_load(struct slgt_info *info, const char *buf, unsigned int count);

static void get_signals(struct slgt_info *info);
static void set_signals(struct slgt_info *info);
static void enable_loopback(struct slgt_info *info);
static void set_rate(struct slgt_info *info, u32 data_rate);

static int  bh_action(struct slgt_info *info);
static void bh_handler(struct work_struct *work);
static void bh_transmit(struct slgt_info *info);
static void isr_serial(struct slgt_info *info);
static void isr_rdma(struct slgt_info *info);
static void isr_txeom(struct slgt_info *info, unsigned short status);
static void isr_tdma(struct slgt_info *info);

static int  alloc_dma_bufs(struct slgt_info *info);
static void free_dma_bufs(struct slgt_info *info);
static int  alloc_desc(struct slgt_info *info);
static void free_desc(struct slgt_info *info);
static int  alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count);
static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count);

static int  alloc_tmp_rbuf(struct slgt_info *info);
static void free_tmp_rbuf(struct slgt_info *info);

static void tx_timeout(unsigned long context);
static void rx_timeout(unsigned long context);

/*
 * ioctl handlers
 */
static int  get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount);
static int  get_params(struct slgt_info *info, MGSL_PARAMS __user *params);
static int  set_params(struct slgt_info *info, MGSL_PARAMS __user *params);
static int  get_txidle(struct slgt_info *info, int __user *idle_mode);
static int  set_txidle(struct slgt_info *info, int idle_mode);
static int  tx_enable(struct slgt_info *info, int enable);
static int  tx_abort(struct slgt_info *info);
static int  rx_enable(struct slgt_info *info, int enable);
static int  modem_input_wait(struct slgt_info *info,int arg);
static int  wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr);
static int  tiocmget(struct tty_struct *tty);
static int  tiocmset(struct tty_struct *tty,
				unsigned int set, unsigned int clear);
static int set_break(struct tty_struct *tty, int break_state);
static int  get_interface(struct slgt_info *info, int __user *if_mode);
static int  set_interface(struct slgt_info *info, int if_mode);
static int  set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
static int  get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
static int  wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
static int  get_xsync(struct slgt_info *info, int __user *if_mode);
static int  set_xsync(struct slgt_info *info, int if_mode);
static int  get_xctrl(struct slgt_info *info, int __user *if_mode);
static int  set_xctrl(struct slgt_info *info, int if_mode);

/*
 * driver functions
 */
static void add_device(struct slgt_info *info);
static void device_init(int adapter_num, struct pci_dev *pdev);
static int  claim_resources(struct slgt_info *info);
static void release_resources(struct slgt_info *info);

/*
 * DEBUG OUTPUT CODE
 */
#ifndef DBGINFO
#define DBGINFO(fmt)
#endif
#ifndef DBGERR
#define DBGERR(fmt)
#endif
#ifndef DBGBH
#define DBGBH(fmt)
#endif
#ifndef DBGISR
#define DBGISR(fmt)
#endif

#ifdef DBGDATA
static void trace_block(struct slgt_info *info, const char *data, int count, const char *label)
{
	int i;
	int linecount;
	printk("%s %s data:\n",info->device_name, label);
	while(count) {
		linecount = (count > 16) ? 16 : count;
		for(i=0; i < linecount; i++)
			printk("%02X ",(unsigned char)data[i]);
		for(;i<17;i++)
			printk("   ");
		for(i=0;i<linecount;i++) {
			if (data[i]>=040 && data[i]<=0176)
				printk("%c",data[i]);
			else
				printk(".");
		}
		printk("\n");
		data  += linecount;
		count -= linecount;
	}
}
#else
#define DBGDATA(info, buf, size, label)
#endif

#ifdef DBGTBUF
static void dump_tbufs(struct slgt_info *info)
{
	int i;
	printk("tbuf_current=%d\n", info->tbuf_current);
	for (i=0 ; i < info->tbuf_count ; i++) {
		printk("%d: count=%04X status=%04X\n",
			i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status));
	}
}
#else
#define DBGTBUF(info)
#endif

#ifdef DBGRBUF
static void dump_rbufs(struct slgt_info *info)
{
	int i;
	printk("rbuf_current=%d\n", info->rbuf_current);
	for (i=0 ; i < info->rbuf_count ; i++) {
		printk("%d: count=%04X status=%04X\n",
			i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status));
	}
}
#else
#define DBGRBUF(info)
#endif

static inline int sanity_check(struct slgt_info *info, char *devname, const char *name)
{
#ifdef SANITY_CHECK
	if (!info) {
		printk("null struct slgt_info for (%s) in %s\n", devname, name);
		return 1;
	}
	if (info->magic != MGSL_MAGIC) {
		printk("bad magic number struct slgt_info (%s) in %s\n", devname, name);
		return 1;
	}
#else
	if (!info)
		return 1;
#endif
	return 0;
}

/**
 * line discipline callback wrappers
 *
 * The wrappers maintain line discipline references
 * while calling into the line discipline.
 *
 * ldisc_receive_buf  - pass receive data to line discipline
 */
static void ldisc_receive_buf(struct tty_struct *tty,
			      const __u8 *data, char *flags, int count)
{
	struct tty_ldisc *ld;
	if (!tty)
		return;
	ld = tty_ldisc_ref(tty);
	if (ld) {
		if (ld->ops->receive_buf)
			ld->ops->receive_buf(tty, data, flags, count);
		tty_ldisc_deref(ld);
	}
}

/* tty callbacks */

static int open(struct tty_struct *tty, struct file *filp)
{
	struct slgt_info *info;
	int retval, line;
	unsigned long flags;

	line = tty->index;
	if (line >= slgt_device_count) {
		DBGERR(("%s: open with invalid line #%d.\n", driver_name, line));
		return -ENODEV;
	}

	info = slgt_device_list;
	while(info && info->line != line)
		info = info->next_device;
	if (sanity_check(info, tty->name, "open"))
		return -ENODEV;
	if (info->init_error) {
		DBGERR(("%s init error=%d\n", info->device_name, info->init_error));
		return -ENODEV;
	}

	tty->driver_data = info;
	info->port.tty = tty;

	DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count));

	/* If port is closing, signal caller to try again */
	if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){
		if (info->port.flags & ASYNC_CLOSING)
			interruptible_sleep_on(&info->port.close_wait);
		retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ?
			-EAGAIN : -ERESTARTSYS);
		goto cleanup;
	}

	mutex_lock(&info->port.mutex);
	info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;

	spin_lock_irqsave(&info->netlock, flags);
	if (info->netcount) {
		retval = -EBUSY;
		spin_unlock_irqrestore(&info->netlock, flags);
		mutex_unlock(&info->port.mutex);
		goto cleanup;
	}
	info->port.count++;
	spin_unlock_irqrestore(&info->netlock, flags);

	if (info->port.count == 1) {
		/* 1st open on this device, init hardware */
		retval = startup(info);
		if (retval < 0) {
			mutex_unlock(&info->port.mutex);
			goto cleanup;
		}
	}
	mutex_unlock(&info->port.mutex);
	retval = block_til_ready(tty, filp, info);
	if (retval) {
		DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
		goto cleanup;
	}

	retval = 0;

cleanup:
	if (retval) {
		if (tty->count == 1)
			info->port.tty = NULL; /* tty layer will release tty struct */
		if(info->port.count)
			info->port.count--;
	}

	DBGINFO(("%s open rc=%d\n", info->device_name, retval));
	return retval;
}

static void close(struct tty_struct *tty, struct file *filp)
{
	struct slgt_info *info = tty->driver_data;

	if (sanity_check(info, tty->name, "close"))
		return;
	DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count));

	if (tty_port_close_start(&info->port, tty, filp) == 0)
		goto cleanup;

	mutex_lock(&info->port.mutex);
 	if (info->port.flags & ASYNC_INITIALIZED)
 		wait_until_sent(tty, info->timeout);
	flush_buffer(tty);
	tty_ldisc_flush(tty);

	shutdown(info);
	mutex_unlock(&info->port.mutex);

	tty_port_close_end(&info->port, tty);
	info->port.tty = NULL;
cleanup:
	DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count));
}

static void hangup(struct tty_struct *tty)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "hangup"))
		return;
	DBGINFO(("%s hangup\n", info->device_name));

	flush_buffer(tty);

	mutex_lock(&info->port.mutex);
	shutdown(info);

	spin_lock_irqsave(&info->port.lock, flags);
	info->port.count = 0;
	info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
	info->port.tty = NULL;
	spin_unlock_irqrestore(&info->port.lock, flags);
	mutex_unlock(&info->port.mutex);

	wake_up_interruptible(&info->port.open_wait);
}

static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	DBGINFO(("%s set_termios\n", tty->driver->name));

	change_params(info);

	/* Handle transition to B0 status */
	if (old_termios->c_cflag & CBAUD &&
	    !(tty->termios.c_cflag & CBAUD)) {
		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
		spin_lock_irqsave(&info->lock,flags);
		set_signals(info);
		spin_unlock_irqrestore(&info->lock,flags);
	}

	/* Handle transition away from B0 status */
	if (!(old_termios->c_cflag & CBAUD) &&
	    tty->termios.c_cflag & CBAUD) {
		info->signals |= SerialSignal_DTR;
 		if (!(tty->termios.c_cflag & CRTSCTS) ||
 		    !test_bit(TTY_THROTTLED, &tty->flags)) {
			info->signals |= SerialSignal_RTS;
 		}
		spin_lock_irqsave(&info->lock,flags);
	 	set_signals(info);
		spin_unlock_irqrestore(&info->lock,flags);
	}

	/* Handle turning off CRTSCTS */
	if (old_termios->c_cflag & CRTSCTS &&
	    !(tty->termios.c_cflag & CRTSCTS)) {
		tty->hw_stopped = 0;
		tx_release(tty);
	}
}

static void update_tx_timer(struct slgt_info *info)
{
	/*
	 * use worst case speed of 1200bps to calculate transmit timeout
	 * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
	 */
	if (info->params.mode == MGSL_MODE_HDLC) {
		int timeout  = (tbuf_bytes(info) * 7) + 1000;
		mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
	}
}

static int write(struct tty_struct *tty,
		 const unsigned char *buf, int count)
{
	int ret = 0;
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "write"))
		return -EIO;

	DBGINFO(("%s write count=%d\n", info->device_name, count));

	if (!info->tx_buf || (count > info->max_frame_size))
		return -EIO;

	if (!count || tty->stopped || tty->hw_stopped)
		return 0;

	spin_lock_irqsave(&info->lock, flags);

	if (info->tx_count) {
		/* send accumulated data from send_char() */
		if (!tx_load(info, info->tx_buf, info->tx_count))
			goto cleanup;
		info->tx_count = 0;
	}

	if (tx_load(info, buf, count))
		ret = count;

cleanup:
	spin_unlock_irqrestore(&info->lock, flags);
	DBGINFO(("%s write rc=%d\n", info->device_name, ret));
	return ret;
}

static int put_char(struct tty_struct *tty, unsigned char ch)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;
	int ret = 0;

	if (sanity_check(info, tty->name, "put_char"))
		return 0;
	DBGINFO(("%s put_char(%d)\n", info->device_name, ch));
	if (!info->tx_buf)
		return 0;
	spin_lock_irqsave(&info->lock,flags);
	if (info->tx_count < info->max_frame_size) {
		info->tx_buf[info->tx_count++] = ch;
		ret = 1;
	}
	spin_unlock_irqrestore(&info->lock,flags);
	return ret;
}

static void send_xchar(struct tty_struct *tty, char ch)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "send_xchar"))
		return;
	DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch));
	info->x_char = ch;
	if (ch) {
		spin_lock_irqsave(&info->lock,flags);
		if (!info->tx_enabled)
		 	tx_start(info);
		spin_unlock_irqrestore(&info->lock,flags);
	}
}

static void wait_until_sent(struct tty_struct *tty, int timeout)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long orig_jiffies, char_time;

	if (!info )
		return;
	if (sanity_check(info, tty->name, "wait_until_sent"))
		return;
	DBGINFO(("%s wait_until_sent entry\n", info->device_name));
	if (!(info->port.flags & ASYNC_INITIALIZED))
		goto exit;

	orig_jiffies = jiffies;

	/* Set check interval to 1/5 of estimated time to
	 * send a character, and make it at least 1. The check
	 * interval should also be less than the timeout.
	 * Note: use tight timings here to satisfy the NIST-PCTS.
	 */

	if (info->params.data_rate) {
	       	char_time = info->timeout/(32 * 5);
		if (!char_time)
			char_time++;
	} else
		char_time = 1;

	if (timeout)
		char_time = min_t(unsigned long, char_time, timeout);

	while (info->tx_active) {
		msleep_interruptible(jiffies_to_msecs(char_time));
		if (signal_pending(current))
			break;
		if (timeout && time_after(jiffies, orig_jiffies + timeout))
			break;
	}
exit:
	DBGINFO(("%s wait_until_sent exit\n", info->device_name));
}

static int write_room(struct tty_struct *tty)
{
	struct slgt_info *info = tty->driver_data;
	int ret;

	if (sanity_check(info, tty->name, "write_room"))
		return 0;
	ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
	DBGINFO(("%s write_room=%d\n", info->device_name, ret));
	return ret;
}

static void flush_chars(struct tty_struct *tty)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "flush_chars"))
		return;
	DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count));

	if (info->tx_count <= 0 || tty->stopped ||
	    tty->hw_stopped || !info->tx_buf)
		return;

	DBGINFO(("%s flush_chars start transmit\n", info->device_name));

	spin_lock_irqsave(&info->lock,flags);
	if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
		info->tx_count = 0;
	spin_unlock_irqrestore(&info->lock,flags);
}

static void flush_buffer(struct tty_struct *tty)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "flush_buffer"))
		return;
	DBGINFO(("%s flush_buffer\n", info->device_name));

	spin_lock_irqsave(&info->lock, flags);
	info->tx_count = 0;
	spin_unlock_irqrestore(&info->lock, flags);

	tty_wakeup(tty);
}

/*
 * throttle (stop) transmitter
 */
static void tx_hold(struct tty_struct *tty)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "tx_hold"))
		return;
	DBGINFO(("%s tx_hold\n", info->device_name));
	spin_lock_irqsave(&info->lock,flags);
	if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC)
	 	tx_stop(info);
	spin_unlock_irqrestore(&info->lock,flags);
}

/*
 * release (start) transmitter
 */
static void tx_release(struct tty_struct *tty)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "tx_release"))
		return;
	DBGINFO(("%s tx_release\n", info->device_name));
	spin_lock_irqsave(&info->lock, flags);
	if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
		info->tx_count = 0;
	spin_unlock_irqrestore(&info->lock, flags);
}

/*
 * Service an IOCTL request
 *
 * Arguments
 *
 * 	tty	pointer to tty instance data
 * 	cmd	IOCTL command code
 * 	arg	command argument/context
 *
 * Return 0 if success, otherwise error code
 */
static int ioctl(struct tty_struct *tty,
		 unsigned int cmd, unsigned long arg)
{
	struct slgt_info *info = tty->driver_data;
	void __user *argp = (void __user *)arg;
	int ret;

	if (sanity_check(info, tty->name, "ioctl"))
		return -ENODEV;
	DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd));

	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
	    (cmd != TIOCMIWAIT)) {
		if (tty->flags & (1 << TTY_IO_ERROR))
		    return -EIO;
	}

	switch (cmd) {
	case MGSL_IOCWAITEVENT:
		return wait_mgsl_event(info, argp);
	case TIOCMIWAIT:
		return modem_input_wait(info,(int)arg);
	case MGSL_IOCSGPIO:
		return set_gpio(info, argp);
	case MGSL_IOCGGPIO:
		return get_gpio(info, argp);
	case MGSL_IOCWAITGPIO:
		return wait_gpio(info, argp);
	case MGSL_IOCGXSYNC:
		return get_xsync(info, argp);
	case MGSL_IOCSXSYNC:
		return set_xsync(info, (int)arg);
	case MGSL_IOCGXCTRL:
		return get_xctrl(info, argp);
	case MGSL_IOCSXCTRL:
		return set_xctrl(info, (int)arg);
	}
	mutex_lock(&info->port.mutex);
	switch (cmd) {
	case MGSL_IOCGPARAMS:
		ret = get_params(info, argp);
		break;
	case MGSL_IOCSPARAMS:
		ret = set_params(info, argp);
		break;
	case MGSL_IOCGTXIDLE:
		ret = get_txidle(info, argp);
		break;
	case MGSL_IOCSTXIDLE:
		ret = set_txidle(info, (int)arg);
		break;
	case MGSL_IOCTXENABLE:
		ret = tx_enable(info, (int)arg);
		break;
	case MGSL_IOCRXENABLE:
		ret = rx_enable(info, (int)arg);
		break;
	case MGSL_IOCTXABORT:
		ret = tx_abort(info);
		break;
	case MGSL_IOCGSTATS:
		ret = get_stats(info, argp);
		break;
	case MGSL_IOCGIF:
		ret = get_interface(info, argp);
		break;
	case MGSL_IOCSIF:
		ret = set_interface(info,(int)arg);
		break;
	default:
		ret = -ENOIOCTLCMD;
	}
	mutex_unlock(&info->port.mutex);
	return ret;
}

static int get_icount(struct tty_struct *tty,
				struct serial_icounter_struct *icount)

{
	struct slgt_info *info = tty->driver_data;
	struct mgsl_icount cnow;	/* kernel counter temps */
	unsigned long flags;

	spin_lock_irqsave(&info->lock,flags);
	cnow = info->icount;
	spin_unlock_irqrestore(&info->lock,flags);

	icount->cts = cnow.cts;
	icount->dsr = cnow.dsr;
	icount->rng = cnow.rng;
	icount->dcd = cnow.dcd;
	icount->rx = cnow.rx;
	icount->tx = cnow.tx;
	icount->frame = cnow.frame;
	icount->overrun = cnow.overrun;
	icount->parity = cnow.parity;
	icount->brk = cnow.brk;
	icount->buf_overrun = cnow.buf_overrun;

	return 0;
}

/*
 * support for 32 bit ioctl calls on 64 bit systems
 */
#ifdef CONFIG_COMPAT
static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params)
{
	struct MGSL_PARAMS32 tmp_params;

	DBGINFO(("%s get_params32\n", info->device_name));
	memset(&tmp_params, 0, sizeof(tmp_params));
	tmp_params.mode            = (compat_ulong_t)info->params.mode;
	tmp_params.loopback        = info->params.loopback;
	tmp_params.flags           = info->params.flags;
	tmp_params.encoding        = info->params.encoding;
	tmp_params.clock_speed     = (compat_ulong_t)info->params.clock_speed;
	tmp_params.addr_filter     = info->params.addr_filter;
	tmp_params.crc_type        = info->params.crc_type;
	tmp_params.preamble_length = info->params.preamble_length;
	tmp_params.preamble        = info->params.preamble;
	tmp_params.data_rate       = (compat_ulong_t)info->params.data_rate;
	tmp_params.data_bits       = info->params.data_bits;
	tmp_params.stop_bits       = info->params.stop_bits;
	tmp_params.parity          = info->params.parity;
	if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32)))
		return -EFAULT;
	return 0;
}

static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params)
{
	struct MGSL_PARAMS32 tmp_params;

	DBGINFO(("%s set_params32\n", info->device_name));
	if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32)))
		return -EFAULT;

	spin_lock(&info->lock);
	if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) {
		info->base_clock = tmp_params.clock_speed;
	} else {
		info->params.mode            = tmp_params.mode;
		info->params.loopback        = tmp_params.loopback;
		info->params.flags           = tmp_params.flags;
		info->params.encoding        = tmp_params.encoding;
		info->params.clock_speed     = tmp_params.clock_speed;
		info->params.addr_filter     = tmp_params.addr_filter;
		info->params.crc_type        = tmp_params.crc_type;
		info->params.preamble_length = tmp_params.preamble_length;
		info->params.preamble        = tmp_params.preamble;
		info->params.data_rate       = tmp_params.data_rate;
		info->params.data_bits       = tmp_params.data_bits;
		info->params.stop_bits       = tmp_params.stop_bits;
		info->params.parity          = tmp_params.parity;
	}
	spin_unlock(&info->lock);

	program_hw(info);

	return 0;
}

static long slgt_compat_ioctl(struct tty_struct *tty,
			 unsigned int cmd, unsigned long arg)
{
	struct slgt_info *info = tty->driver_data;
	int rc = -ENOIOCTLCMD;

	if (sanity_check(info, tty->name, "compat_ioctl"))
		return -ENODEV;
	DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd));

	switch (cmd) {

	case MGSL_IOCSPARAMS32:
		rc = set_params32(info, compat_ptr(arg));
		break;

	case MGSL_IOCGPARAMS32:
		rc = get_params32(info, compat_ptr(arg));
		break;

	case MGSL_IOCGPARAMS:
	case MGSL_IOCSPARAMS:
	case MGSL_IOCGTXIDLE:
	case MGSL_IOCGSTATS:
	case MGSL_IOCWAITEVENT:
	case MGSL_IOCGIF:
	case MGSL_IOCSGPIO:
	case MGSL_IOCGGPIO:
	case MGSL_IOCWAITGPIO:
	case MGSL_IOCGXSYNC:
	case MGSL_IOCGXCTRL:
	case MGSL_IOCSTXIDLE:
	case MGSL_IOCTXENABLE:
	case MGSL_IOCRXENABLE:
	case MGSL_IOCTXABORT:
	case TIOCMIWAIT:
	case MGSL_IOCSIF:
	case MGSL_IOCSXSYNC:
	case MGSL_IOCSXCTRL:
		rc = ioctl(tty, cmd, arg);
		break;
	}

	DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc));
	return rc;
}
#else
#define slgt_compat_ioctl NULL
#endif /* ifdef CONFIG_COMPAT */

/*
 * proc fs support
 */
static inline void line_info(struct seq_file *m, struct slgt_info *info)
{
	char stat_buf[30];
	unsigned long flags;

	seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n",
		      info->device_name, info->phys_reg_addr,
		      info->irq_level, info->max_frame_size);

	/* output current serial signal states */
	spin_lock_irqsave(&info->lock,flags);
	get_signals(info);
	spin_unlock_irqrestore(&info->lock,flags);

	stat_buf[0] = 0;
	stat_buf[1] = 0;
	if (info->signals & SerialSignal_RTS)
		strcat(stat_buf, "|RTS");
	if (info->signals & SerialSignal_CTS)
		strcat(stat_buf, "|CTS");
	if (info->signals & SerialSignal_DTR)
		strcat(stat_buf, "|DTR");
	if (info->signals & SerialSignal_DSR)
		strcat(stat_buf, "|DSR");
	if (info->signals & SerialSignal_DCD)
		strcat(stat_buf, "|CD");
	if (info->signals & SerialSignal_RI)
		strcat(stat_buf, "|RI");

	if (info->params.mode != MGSL_MODE_ASYNC) {
		seq_printf(m, "\tHDLC txok:%d rxok:%d",
			       info->icount.txok, info->icount.rxok);
		if (info->icount.txunder)
			seq_printf(m, " txunder:%d", info->icount.txunder);
		if (info->icount.txabort)
			seq_printf(m, " txabort:%d", info->icount.txabort);
		if (info->icount.rxshort)
			seq_printf(m, " rxshort:%d", info->icount.rxshort);
		if (info->icount.rxlong)
			seq_printf(m, " rxlong:%d", info->icount.rxlong);
		if (info->icount.rxover)
			seq_printf(m, " rxover:%d", info->icount.rxover);
		if (info->icount.rxcrc)
			seq_printf(m, " rxcrc:%d", info->icount.rxcrc);
	} else {
		seq_printf(m, "\tASYNC tx:%d rx:%d",
			       info->icount.tx, info->icount.rx);
		if (info->icount.frame)
			seq_printf(m, " fe:%d", info->icount.frame);
		if (info->icount.parity)
			seq_printf(m, " pe:%d", info->icount.parity);
		if (info->icount.brk)
			seq_printf(m, " brk:%d", info->icount.brk);
		if (info->icount.overrun)
			seq_printf(m, " oe:%d", info->icount.overrun);
	}

	/* Append serial signal status to end */
	seq_printf(m, " %s\n", stat_buf+1);

	seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
		       info->tx_active,info->bh_requested,info->bh_running,
		       info->pending_bh);
}

/* Called to print information about devices
 */
static int synclink_gt_proc_show(struct seq_file *m, void *v)
{
	struct slgt_info *info;

	seq_puts(m, "synclink_gt driver\n");

	info = slgt_device_list;
	while( info ) {
		line_info(m, info);
		info = info->next_device;
	}
	return 0;
}

static int synclink_gt_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, synclink_gt_proc_show, NULL);
}

static const struct file_operations synclink_gt_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= synclink_gt_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

/*
 * return count of bytes in transmit buffer
 */
static int chars_in_buffer(struct tty_struct *tty)
{
	struct slgt_info *info = tty->driver_data;
	int count;
	if (sanity_check(info, tty->name, "chars_in_buffer"))
		return 0;
	count = tbuf_bytes(info);
	DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count));
	return count;
}

/*
 * signal remote device to throttle send data (our receive data)
 */
static void throttle(struct tty_struct * tty)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "throttle"))
		return;
	DBGINFO(("%s throttle\n", info->device_name));
	if (I_IXOFF(tty))
		send_xchar(tty, STOP_CHAR(tty));
 	if (tty->termios.c_cflag & CRTSCTS) {
		spin_lock_irqsave(&info->lock,flags);
		info->signals &= ~SerialSignal_RTS;
	 	set_signals(info);
		spin_unlock_irqrestore(&info->lock,flags);
	}
}

/*
 * signal remote device to stop throttling send data (our receive data)
 */
static void unthrottle(struct tty_struct * tty)
{
	struct slgt_info *info = tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "unthrottle"))
		return;
	DBGINFO(("%s unthrottle\n", info->device_name));
	if (I_IXOFF(tty)) {
		if (info->x_char)
			info->x_char = 0;
		else
			send_xchar(tty, START_CHAR(tty));
	}
 	if (tty->termios.c_cflag & CRTSCTS) {
		spin_lock_irqsave(&info->lock,flags);
		info->signals |= SerialSignal_RTS;
	 	set_signals(info);
		spin_unlock_irqrestore(&info->lock,flags);
	}
}

/*
 * set or clear transmit break condition
 * break_state	-1=set break condition, 0=clear
 */
static int set_break(struct tty_struct *tty, int break_state)
{
	struct slgt_info *info = tty->driver_data;
	unsigned short value;
	unsigned long flags;

	if (sanity_check(info, tty->name, "set_break"))
		return -EINVAL;
	DBGINFO(("%s set_break(%d)\n", info->device_name, break_state));

	spin_lock_irqsave(&info->lock,flags);
	value = rd_reg16(info, TCR);
 	if (break_state == -1)
		value |= BIT6;
	else
		value &= ~BIT6;
	wr_reg16(info, TCR, value);
	spin_unlock_irqrestore(&info->lock,flags);
	return 0;
}

#if SYNCLINK_GENERIC_HDLC

/**
 * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
 * set encoding and frame check sequence (FCS) options
 *
 * dev       pointer to network device structure
 * encoding  serial encoding setting
 * parity    FCS setting
 *