/* -*- linux-c -*- */
/* $Id: 8253xutl.c,v 1.3 2002/02/10 22:17:26 martillo Exp $
 * 8253xutl.c: SYNC TTY Driver for the SIEMENS SAB8253X DUSCC.
 *
 * Implementation, modifications and extensions
 * Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

/* Standard in kernel modules */
#define DEFINE_VARIABLE
#include <linux/module.h>   /* Specifically, a module */
#include <asm/io.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/version.h>
#include <asm/uaccess.h>
#include "8253xctl.h"
#include "8253x.h"
#include <linux/pci.h>
#include <linux/fs.h>
#include "sp502.h"

#ifdef MODULE
#undef XCONFIG_SERIAL_CONSOLE
#endif

void sab8253x_start_txS(struct sab_port *port)
{
	unsigned long flags;
	register int count;
	register int total;
	register int offset;
	char temporary[32];
	register unsigned int slopspace;
	register int sendsize;
	unsigned int totaltransmit;
	unsigned fifospace;
	unsigned loadedcount;
	struct tty_struct *tty = port->tty; /* a little gross tty flags whether
					       invoked from a tty or the network */
	
	fifospace = port->xmit_fifo_size; /* This code can handle fragmented frames
					     although currently none are generated*/
	loadedcount = 0;
	
	if(port->sabnext2.transmit == NULL)
	{
		return;
	}
	
	save_flags(flags); 
	cli();			
	
	
	if(count = port->sabnext2.transmit->Count, (count & OWNER) == OWN_SAB)
	{
		count &= ~OWN_SAB; /* OWN_SAB is really 0 but cannot guarantee in the future */
		
		if(port->sabnext2.transmit->HostVaddr)
		{
			total = (port->sabnext2.transmit->HostVaddr->tail - 
				 port->sabnext2.transmit->HostVaddr->data); /* packet size */
		}
		else
		{
			total = 0;		/* the data is only the crc/trailer */
		}
		
		if(tty && (tty->stopped || tty->hw_stopped) && (count == total))
		{			/* works for frame that only has a trailer (crc) */
			port->interrupt_mask1 |= SAB82532_IMR1_XPR;
			WRITEB(port, imr1, port->interrupt_mask1);
			restore_flags(flags);	/* can't send */
			return;
		}
		
		offset = (total - count);	/* offset to data still to send */
		
		port->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
		WRITEB(port, imr1, port->interrupt_mask1);
		port->all_sent = 0;
		
		if(READB(port,star) & SAB82532_STAR_XFW)
		{
			if(count <= fifospace)
			{
				port->xmit_cnt = count;
				slopspace = 0;
				sendsize = 0;
				if(port->sabnext2.transmit->sendcrc) 
				/* obviously should not happen for async but might use for
				   priority transmission */
				{
					slopspace = fifospace - count;
				}
				if(slopspace)
				{
					if(count)
					{
						memcpy(temporary, &port->sabnext2.transmit->HostVaddr->data[offset], 
						       count);
					}
					sendsize = MIN(slopspace, (4 - port->sabnext2.transmit->crcindex)); 
				/* how many bytes to send */
					memcpy(&temporary[count], 
					       &((unsigned char*)(&port->sabnext2.transmit->crc))
					       [port->sabnext2.transmit->crcindex], 
					       sendsize);
					port->sabnext2.transmit->crcindex += sendsize;
					if(port->sabnext2.transmit->crcindex >= 4)
					{
						port->sabnext2.transmit->sendcrc = 0;
					}
					port->xmit_buf = temporary;
				}
				else
				{
					port->xmit_buf =	/* set up wrifefifo variables */
						&port->sabnext2.transmit->HostVaddr->data[offset];
				}
				port->xmit_cnt += sendsize;
				count = 0;
			}
			else
			{
				count -= fifospace;
				port->xmit_cnt = fifospace;
				port->xmit_buf =	/* set up wrifefifo variables */
					&port->sabnext2.transmit->HostVaddr->data[offset];
				
			}
			port->xmit_tail= 0;
			loadedcount = port->xmit_cnt;
			(*port->writefifo)(port);
			totaltransmit = Sab8253xCountTransmitDescriptors(port);
			if(tty && (totaltransmit < (sab8253xs_listsize/2))) /* only makes sense on a TTY */
			{
				sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP);
			}
			
			if((sab8253xt_listsize - totaltransmit) > (sab8253xt_listsize/2))
			{
				port->buffergreedy = 0;
			}
			else
			{
				port->buffergreedy = 1;
			}
			
			port->xmit_buf = NULL; /* this var is used to indicate whether to call kfree */
			
			/* fifospace -= loadedcount;*/
			/* Here to make mods to handle arbitrarily fragmented frames look to 8253xtty.c for help */
			
			if ((count <= 0) && (port->sabnext2.transmit->sendcrc == 0))
			{
				port->sabnext2.transmit->Count = OWN_DRIVER;
				if(!tty)
				{		/* called by network driver */
					++(port->Counters.transmitpacket);
				}
#ifdef FREEININTERRUPT		/* treat this routine as if taking place in interrupt */
				if(port->sabnext2.transmit->HostVaddr)
				{
					skb_unlink(port->sabnext2.transmit->HostVaddr);
					dev_kfree_skb_any(port->sabnext2.transmit->HostVaddr);
					port->sabnext2.transmit->HostVaddr = 0; /* no skb */
				}
				port->sabnext2.transmit->crcindex = 0; /* no single byte */
#endif
				sab8253x_cec_wait(port);
				WRITEB(port, cmdr, SAB82532_CMDR_XME|SAB82532_CMDR_XTF); /* Terminate the frame */
				
				port->sabnext2.transmit = port->sabnext2.transmit->VNext;
				
				if(!tty && port->tx_full)	/* invoked from the network driver */
				{
					port->tx_full = 0; /* there is a free slot */
					switch(port->open_type)
					{
					case OPEN_SYNC_NET:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
						port->dev->start = 1;
						port->dev->tbusy = 0;	/* maybe need mark_bh here */
#else
						netif_start_queue(port->dev);
#endif
						break;
						
					case OPEN_SYNC_CHAR:
						wake_up_interruptible(&port->write_wait);
						break;
						
					default:
						break;
					}
				}
				
				if((port->sabnext2.transmit->Count & OWNER) == OWN_SAB)
				{		/* new frame to send */
					port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
					WRITEB(port, imr1, port->interrupt_mask1);
				}
				else
				{
					port->interrupt_mask1 |= SAB82532_IMR1_XPR;
					WRITEB(port, imr1, port->interrupt_mask1);
					if((port->open_type == OPEN_SYNC_CHAR) && port->async_queue)
					{		/* if indication of transmission is needed by the */
						/* application on a per-frame basis kill_fasync */
						/* can provide it */
						kill_fasync(&port->async_queue, SIGIO, POLL_OUT);
					}
				}
				restore_flags(flags);
				return;
			}
			/* Issue a Transmit FIFO command. */
			sab8253x_cec_wait(port);
			WRITEB(port, cmdr, SAB82532_CMDR_XTF);	
			port->sabnext2.transmit->Count = (count|OWN_SAB);
		}
		port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR); /* more to send */
		WRITEB(port, imr1, port->interrupt_mask1);
	}
	else
	{				/* nothing to send */
		port->interrupt_mask1 |= SAB82532_IMR1_XPR;
		WRITEB(port, imr1, port->interrupt_mask1);
	}
	restore_flags(flags);
	return;
}

void sab8253x_transmit_charsS(struct sab_port *port,
			      union sab8253x_irq_status *stat)
{
	if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) 
	{
		port->interrupt_mask1 |= SAB82532_IMR1_ALLS;
		WRITEB(port, imr1, port->interrupt_mask1);
		port->all_sent = 1;
	}
	sab8253x_start_txS(port);
}

/*
 * This routine is called to set the UART divisor registers to match
 * the specified baud rate for a serial port.
 */

/***************************************************************************
 * sab_baudenh:      Function to compute the "enhanced" baudrate.
 *                
 *
 *     Parameters   : 
 *                  encbaud  2* the baudrate. We use the
 *                           double value so as to support 134.5 (in only)
 *                  clkspeed The board clock speed in Hz.
 *                  bgr      Value of reg BGR for baudrate(output)
 *                  ccr2     Value of reg // CCR2 for baudrate (output)
 *                  ccr4     Value of reg CCR4 for baudrate (output)
 *                  truebaud The actual baudrate achieved (output).
 *
 *
 *     Return value : Return FALSE the parameters could not be computed, 
 *
 *     Prerequisite : The various ports must have been initialized
 *
 *     Remark       : Stolen from the Aurora ase driver.
 *
 *     Author       : fw
 *
 *     Revision     : Oct 9 2000, creation
 ***************************************************************************/
/*
 * Macro to check to see if the high n bits of the given unsigned long
 *  are zero.
 */               
#define HIZERO(x, n)        ( ((unsigned long) ((x) << (n)) >> (n)) == (x))
/* form an n-bit bitmask */
#define NBM(n)                  (~(((~(unsigned long) 0) >> (n)) << (n)))
/* shift x by y bits to right, rounded */
#define ROUND_SHIFT(x, y)       (((unsigned long) (x) + (NBM(y - 1) + 1)) >> (y))
/* perform rounded division */
#define ROUND_DIV(x, y) (((x) + ((y) >> 1)) / (y))
#define ABSDIF(x, y)    ((x) > (y) ? ((x) - (y)) : ((y) - (x))) 
static unsigned int
sab8253x_baudenh(unsigned long encbaud, unsigned long clk_speed,
		 unsigned char *bgr, unsigned char *ccr2,
		 unsigned long *truebaudp)
{
	register unsigned short	 tmp;
	register unsigned char	 ccr2tmp;
	unsigned long		 power2, mant;
	unsigned int			 fastclock;
	
	if (encbaud == 0) {
		return FALSE;
	}
	
	/*
	 * Keep dividing quotien by two until it is between the value of 1 and 64,
	 *  inclusive.
	 */
	
	fastclock = (clk_speed >= 10000000);	/* >= 10 MHz */
	
	for (power2 = 0; power2 < 16; power2++) 
	{
		/* divisor = baud * 2^M * 16 */
		if (!HIZERO(encbaud, power2 + 3)) 
		{
			if (!HIZERO(encbaud, power2)) 
			{	/* baud rate still too big? */
				mant = ROUND_DIV(ROUND_SHIFT(clk_speed, power2 + 3), encbaud);
				
				/* mant = (clk_speed / (8 * 2^M)) / (baud * 2) */
				/*	= clk_speed / (baud * 16 * 2^M) */
			}
			else 
			{
				mant = ROUND_DIV(ROUND_SHIFT(clk_speed, 3), encbaud << power2);
				/* mant = (clk_speed / 8) / (baud * 2 * 2^M) */
				/*	= clk_speed / (baud * 16 * 2^M) */
			}
		}
		else 
		{
			mant = ROUND_DIV(clk_speed, encbaud << (power2 + 3));
			/* mant = clk_speed / (baud * 2 * 8 * 2^M) */
			/*	    = clk_speed / (baud * 16 * 2^M) */
		}
		
		/* mant = clk_speed / (baud * 2^M * 16) */
		
		if (mant < 2
		    || (mant <= 64 && (!fastclock || power2 != 0))) 
		{
			break;
		}
	}
	
	/*
	 * Did we not succeed?  (Baud rate is too small)
	 */
	if (mant > 64) 
	{
		return FALSE;
	}
	
	/*
	 * Now, calculate the true baud rate.
	 */
	
	if (mant < 1 || (mant == 1 && power2 == 0)) 
	{
		/* bgr and ccr2 should be initialized to 0 */
		*truebaudp = ROUND_SHIFT(clk_speed, 4);
	}
	else 
	{
		*truebaudp = ROUND_DIV(clk_speed, mant << (4 + power2));
		/* divisor is not zero because mant is [1, 64] */
		mant--; /* now [0, 63] */
		
		/*
		 * Encode the N and M values into the bgr and ccr2 registers.
		 */
		
		tmp = ((unsigned short) mant) | ((unsigned short) power2 << 6);
		
		ccr2tmp = SAB82532_CCR2_BDF;
		if ((tmp & 0x200) != 0) 
		{
			ccr2tmp |= SAB82532_CCR2_BR9;
		}
		if ((tmp & 0x100) != 0) 
		{
			ccr2tmp |= SAB82532_CCR2_BR8;
		}
		
		*ccr2 = ccr2tmp | (*ccr2 & ~(SAB82532_CCR2_BDF|SAB82532_CCR2_BR8|SAB82532_CCR2_BR9));
		*bgr = (unsigned char) tmp;
	}
	
	return TRUE;
}

/*
 * Calculate the standard mode baud divisor using an integral algorithm.
 */
/***************************************************************************
 * sab_baudstd:      Function to compute the "standard " baudrate.
 *                
 *
 *     Parameters   : 
 *                  encbaud  2* the baudrate. We use the
 *                           double value so as to support 134.5 (in only)
 *                  clkspeed The board clock speed in Hz.
 *                  bgr      Value of reg BGR for baudrate(output)
 *                  ccr2     Value of reg CCR2 for baudrate (output)
 *                  ccr4     Value of reg CCR4 for baudrate (output)
 *                  truebaud The actual baudrate achieved (output).
 *
 *
 *     Return value : Return FALSE the parameters could not be computed, 
 *
 *     Prerequisite : The various ports must have been initialized
 *
 *     Remark       : Stolen from the Aurora ase driver.
 *
 *     Author       : fw
 *
 *     Revision     : Oct 9 2000, creation
 ***************************************************************************/
static unsigned int
sab8253x_baudstd(unsigned long encbaud, unsigned long clk_speed,
		 unsigned char *bgr, unsigned char *ccr2,
		 unsigned long *truebaudp)
{
  register unsigned short	 quot;
  register unsigned char	 ccr2tmp;
  
  if (encbaud == 0) 
  {
	  return FALSE;
  }
  
  /*
   * This divisor algorithm is a little strange.  The
   *  divisors are all multiples of 2, except for the
   *  magic value of 1.
   *
   * What we do is do most of the algorithm for multiples
   *  of 1, and then switch at the last minute to multiples
   *  of 2.
   */
  
  /*
   * Will we lose any information by left shifting encbaud?
   *  If so, then right shift clk_speed instead.
   */
  if (!HIZERO(encbaud, 3)) 
  {
	  quot = (unsigned short) ROUND_DIV(ROUND_SHIFT(clk_speed, 3),
					    encbaud);
	  /* quot = (clk_speed / 8) / (baud * 2) = clk_speed / (16 * baud) */
  }
  else 
  {
	  /* encbaud isn't a multiple of 2^29 (baud not mult. of 2^28) */
	  quot = (unsigned short) ROUND_DIV(clk_speed, encbaud << 3);
  }
  
  /* quot = clk_speed / (baud * 16) */
  if (quot < 2) 
  {
	  /* bgr and ccr2 should be initialized to 0 */
	  *truebaudp = ROUND_SHIFT(clk_speed, 4);
	  return TRUE;
  }
  
  /*
   * Divide the quotient by two.
   */
  quot = ROUND_SHIFT(quot, 1);
  
  if (quot <= 0x400) 
  {
	  /* quot = [1, 0x400]  -> (quot << 5) != 0 */
	  *truebaudp = ROUND_DIV(clk_speed, ((unsigned long) quot << 5));
	  quot--;
	  
	  ccr2tmp = SAB82532_CCR2_BDF;
	  if ((quot & 0x200) != 0) 
	  {
		  ccr2tmp |= SAB82532_CCR2_BR9;
	  }
	  if ((quot & 0x100) != 0) 
	  {
		  ccr2tmp |=SAB82532_CCR2_BR8;
	  }
	  
	  *ccr2 = ccr2tmp | (*ccr2 & ~(SAB82532_CCR2_BDF|SAB82532_CCR2_BR8|SAB82532_CCR2_BR9));
	  *bgr = (unsigned char) quot;
  }
  else 
  {			/* the baud rate is too small. */
	  return FALSE;
  }
  
  return TRUE;
}

/***************************************************************************
 * sab_baud:      Function to compute the best register value to achieve
 *                a given baudrate.
 *                
 *
 *     Parameters   : 
 *                  port:    The port being used  (in only)
 *                  encbaud: 2* the baudrate. We use the
 *                           double value so as to support 134.5 (in only)
 *                  bgr      Value of reg BGR for baudrate(output)
 *                  ccr2     Value of reg CCR2 for baudrate (output)
 *                  ccr4     Value of reg CCR4 for baudrate (output)
 *                  truebaud The actual baudrate achieved (output).
 *
 *
 *     Return value : Return TRUE if the vaudrate can be set, FALSE otherwise 
 *
 *     Prerequisite : The various ports must have been initialized
 *
 *     Remark       : Stolen from the Aurora ase driver.
 *
 *     Author       : fw
 *
 *     Revision     : Oct 9 2000, creation
 ***************************************************************************/
unsigned int 
sab8253x_baud(sab_port_t *port, unsigned long encbaud,
	      unsigned char *bgr, unsigned char *ccr2,
	      unsigned char *ccr4, unsigned long *truebaudp)
{
	unsigned char	 bgr_std, bgr_enh, ccr2_std, ccr2_enh, ccr4_enh;
	unsigned int		 ok_std, ok_enh;
	unsigned long	 truebaud_std, truebaud_enh, truebaud,clkspeed;
	
	bgr_std = bgr_enh = 0;
	ccr2_std = ccr2_enh = 0;
	ccr4_enh = 0;
	
	/*
	 * the port/chip/board structure will tell us:
	 *  1) clock speed
	 *  2) chip revision (to figure out if the enhanced method is
	 *     available.
	 */
	
	clkspeed = port->chip->c_cim ? port->chip->c_cim->ci_clkspeed :  port->board->b_clkspeed;
	
#ifdef NODEBUGGING
	printk("With clk speed %ld, baud rate = %ld\n",clkspeed, encbaud);
#endif
	
	ok_std = sab8253x_baudstd(encbaud, clkspeed, &bgr_std,
				  &ccr2_std, &truebaud_std);
#ifdef NODEBUGGING
	printk("Std gives bgr = 0x%x, ccr2=0x%x for speed %ld\n",bgr_std,ccr2_std,truebaud_std);
#endif
	if(port->chip->c_revision >= SAB82532_VSTR_VN_3_2) 
	{
		ok_enh = sab8253x_baudenh(encbaud, clkspeed,
					  &bgr_enh, &ccr2_enh, &truebaud_enh);
#ifdef NODEBUGGING
		printk("Enh gives bgr = 0x%x, ccr2=0x%x for speed %ld\n",bgr_enh,ccr2_enh,truebaud_enh);
#endif
	} 
	else 
		ok_enh = FALSE;
	
	/*
	 * Did both methods return values?
	 */
	if (ok_std && ok_enh) 
	{
		/*
		 * Find the closest of the two.
		 */
		if (ABSDIF((truebaud_enh<<1), encbaud) <
		    ABSDIF((truebaud_std<<1), encbaud)) 
		{
			ok_std = FALSE;
		}
		else 
		{
			ok_enh = FALSE;
		}
	}
	
	/*
	 * Now return the values.
	 */
	
	if (ok_std || ok_enh) 
	{
		truebaud = ok_std ? truebaud_std : truebaud_enh;
		
		/*
		 * If the true baud rate is off by more than 5%, then
		 *  we don't support it.
		 */
		if (ROUND_DIV(ABSDIF((truebaud<<1), encbaud), encbaud) != 0) 
		{
			/*
			 * We're not even in the right ballpark.  This
			 *  test is here to deal with overflow conditions.
			 */
			return FALSE;
		}
		else if (ROUND_DIV(ABSDIF((truebaud<<1), encbaud) * 100,
				   encbaud) >= 5) 
		{
			return FALSE;
		}
		
		*truebaudp = truebaud;
		
		if (ok_enh) 
		{
			*ccr4 |= SAB82532_CCR4_EBRG;
			*ccr2 = ccr2_enh;
			*bgr = bgr_enh;
#ifdef DEBUGGING
			printk("Enhanced Baud at %ld, ccr4 = 0x%x, ccr2 = 9x%x, bgr = 0x%x\n",
			       truebaud,*ccr4,*ccr2,*bgr);
#endif
		} 
		else 
		{
			*ccr4 &= ~SAB82532_CCR4_EBRG;
			*ccr2 = ccr2_std;
			*bgr = bgr_std;
#ifdef DEBUGGING
			printk("Standard Baud at %ld, ccr4 = 0x%x, ccr2 = 9x%x, bgr = 0x%x\n",
			       truebaud,*ccr4,*ccr2,*bgr);
#endif
		}
		
		return TRUE;
	}
	else 
	{
		return FALSE;
	}
}

int Sab8253xCountTransmit(SAB_PORT *port)
{
	register RING_DESCRIPTOR *rd;
	register int total;
	register int count;
	unsigned long flags;
	RING_DESCRIPTOR *start;
	
	if(port->sabnext2.transmit == NULL)
	{
		return 0;
	}
	
	save_flags(flags);
	cli();
	rd = port->sabnext2.transmit;
	start = rd;
	total = 0;
	while(1)
	{
		count = rd->Count;
		if((count & OWNER) == OWN_DRIVER)
		{
			break;
		}
		total += (count & ~OWNER);
		if(rd->sendcrc)
		{
			total += (4 - rd->crcindex);
		}
		rd = rd->VNext;
		if(rd == start)
		{
			break;
		}
	}
	restore_flags(flags);
	return total;
}

int Sab8253xCountTransmitDescriptors(SAB_PORT *port)
{
	register RING_DESCRIPTOR *rd;
	register int total;
	register int count;
	unsigned long flags;
	RING_DESCRIPTOR *start;
	
	if(port->sabnext2.transmit == NULL)
	{
		return 0;
	}
	
	save_flags(flags);
	cli();
	rd = port->sabnext2.transmit;
	start = rd;
	total = 0;
	while(1)
	{
		count = rd->Count;
		if((count & OWNER) == OWN_DRIVER)
		{
			break;
		}
		++total;
		rd = rd->VNext;
		if(rd == start)
		{
			break;
		}
	}
	restore_flags(flags);
	return total;
}

int getccr0configS(struct sab_port *port)
{
	return port->ccontrol.ccr0;
}

int getccr1configS(struct sab_port *port)
{
	return port->ccontrol.ccr1;
}

int getccr2configS(struct sab_port *port)
{
	return port->ccontrol.ccr2;
}

int getccr3configS(struct sab_port *port)
{
	return port->ccontrol.ccr3;
}

int getccr4configS(struct sab_port *port)
{
	return port->ccontrol.ccr4;
}

int getrlcrconfigS(struct sab_port *port)
{
	return port->ccontrol.rlcr;
}

int getmodeS(struct sab_port *port)
{
	return port->ccontrol.mode;
}

void sab8253x_init_lineS(struct sab_port *port)
{
	unsigned char stat;
	
	if(port->chip->c_cim)
	{
		if(port->chip->c_cim->ci_type == CIM_SP502)
		{
			aura_sp502_program(port, SP502_OFF_MODE);
		}
	}

	/*
	 * Wait for any commands or immediate characters
	 */
	sab8253x_cec_wait(port);
#if 0
	sab8253x_tec_wait(port);	/* I have to think about this one
					 * should I assume the line was
					 * previously in async mode*/
#endif
	
	/*
	 * Clear the FIFO buffers.
	 */
	
	WRITEB(port, cmdr, SAB82532_CMDR_RHR);
	sab8253x_cec_wait(port);
	WRITEB(port,cmdr,SAB82532_CMDR_XRES);
	
	
	/*
	 * Clear the interrupt registers.
	 */
	stat = READB(port, isr0);	/* acks ints */
	stat = READB(port, isr1);
	
	/*
	 * Now, initialize the UART 
	 */
	WRITEB(port, ccr0, 0);	  /* power-down */
	WRITEB(port, ccr0, getccr0configS(port));
	WRITEB(port, ccr1, getccr1configS(port));
	WRITEB(port, ccr2, getccr2configS(port));
	WRITEB(port, ccr3, getccr3configS(port));
	WRITEB(port, ccr4, getccr4configS(port));	/* 32 byte receive fifo */
	WRITEB(port, mode, getmodeS(port));
	WRITEB(port, tic /* really rlcr */, getrlcrconfigS(port));
	/* power-up */
	
	switch(port->ccontrol.ccr4 & SAB82532_CCR4_RF02)
	{
	case SAB82532_CCR4_RF32:
		port->recv_fifo_size = 32;
		break;
	case SAB82532_CCR4_RF16:
		port->recv_fifo_size = 16;
		break;
	case SAB82532_CCR4_RF04:
		port->recv_fifo_size = 4;
		break;
	case SAB82532_CCR4_RF02:
		port->recv_fifo_size = 2;
		break;
	default:
		port->recv_fifo_size = 32;
		port->ccontrol.ccr4 &= ~SAB82532_CCR4_RF02;
		break;
	}
	
	if(port->ccontrol.ccr2 & SAB82532_CCR2_TOE)
	{
		RAISE(port, txclkdir);
	}
	else
	{
		LOWER(port, txclkdir);
	}
	
	SET_REG_BIT(port,ccr0,SAB82532_CCR0_PU);

	if(port->chip->c_cim)
	{
		if(port->chip->c_cim->ci_type == CIM_SP502)
		{
			aura_sp502_program(port, port->sigmode);
		}
	}
}

/* frees up all skbuffs currently */
/* held by driver */
void Sab8253xFreeAllFreeListSKBUFFS(SAB_PORT* priv) /* empty the skbuffer list */
/* either on failed open */
/* or on close*/
{
	struct sk_buff* skb;
	
	if(priv->sab8253xbuflist == NULL)
	{
		return;
	}
	
	DEBUGPRINT((KERN_ALERT "sab8253x: freeing %i skbuffs.\n", 
		    skb_queue_len(priv->sab8253xbuflist)));
	
	while(skb_queue_len(priv->sab8253xbuflist) > 0)
	{
		skb = skb_dequeue(priv->sab8253xbuflist);
		dev_kfree_skb_any(skb);
	}
	kfree(priv->sab8253xbuflist);
	priv->sab8253xbuflist = NULL;
}

int Sab8253xSetUpLists(SAB_PORT *priv)
{
	if(priv->sab8253xbuflist)
	{
		if(priv->sab8253xc_rcvbuflist)
		{
			return 0;
		}
		else
		{
			return -1;
		}
		return 0;
	}
	else if(priv->sab8253xc_rcvbuflist)
	{
		return -1;
	}
	
	priv->sab8253xbuflist = (struct sk_buff_head*) kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
	if(priv->sab8253xbuflist == NULL)
	{
		return -1;
	}
	priv->sab8253xc_rcvbuflist = (struct sk_buff_head*) kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);  
	if(priv->sab8253xc_rcvbuflist == NULL)
	{
		kfree(priv->sab8253xbuflist);
		return -1;
	}
	skb_queue_head_init(priv->sab8253xbuflist);
	skb_queue_head_init(priv->sab8253xc_rcvbuflist);
	return 0;
}

/* sets up transmit ring and one receive sk_buff */

/* set up transmit and receive
   sk_buff control structures */
int Sab8253xInitDescriptors2(SAB_PORT *priv, int listsize, int rbufsize)
{
	RING_DESCRIPTOR *desc;
	RING_DESCRIPTOR *xdesc;
	
	if(priv->dcontrol2.transmit != NULL)
		
	{
		if(priv->dcontrol2.receive != NULL)
		{
			return 0;
		}
		return -1;
	}
	else if(priv->dcontrol2.receive != NULL)
	{
		return -1;
	}
	
	priv->dcontrol2.transmit = (RING_DESCRIPTOR*) 
		kmalloc(sizeof(RING_DESCRIPTOR) * listsize, GFP_KERNEL);
	/* dcontrol2 is an historical
	   artifact from when the code
	   talked to an intelligent controller */
	if(priv->dcontrol2.transmit == NULL)
	{
		return -1;
	}
	
	priv->dcontrol2.receive = (RING_DESCRIPTOR*)
		kmalloc(sizeof(RING_DESCRIPTOR), GFP_KERNEL); /* only one receive sk_buffer */
	if(priv->dcontrol2.receive == NULL)
	{
		kfree(priv->dcontrol2.transmit);
		priv->dcontrol2.transmit = NULL;
		return -1;
	}
	
	for(xdesc = priv->dcontrol2.transmit; 
	    xdesc < &priv->dcontrol2.transmit[listsize - 1];
	    xdesc = &xdesc[1])	/* set up transmit descriptors */
	{
		xdesc->HostVaddr = NULL;
		xdesc->VNext = &xdesc[1];
		xdesc->Count = 0 | OWN_DRIVER;
		xdesc->crc = 0;
		xdesc->sendcrc = 0;
		xdesc->crcindex = 0;
	}
	xdesc->HostVaddr = NULL;
	xdesc->VNext = priv->dcontrol2.transmit; /* circular list */
	xdesc->Count = 0 | OWN_DRIVER;
	xdesc->crc = 0;
	xdesc->sendcrc = 0;
	xdesc->crcindex = 0;
	
	desc = priv->dcontrol2.receive; /* only need one descriptor for receive */
	desc->HostVaddr = NULL;
	desc->VNext = &desc[0];
	
	desc = priv->dcontrol2.receive;
	desc->HostVaddr = dev_alloc_skb(rbufsize);
	if(desc->HostVaddr == NULL)
	{
		printk(KERN_ALERT "Unable to allocate skb_buffers (rx 0).\n");
		printk(KERN_ALERT "Driver initialization failed.\n");
		kfree(priv->dcontrol2.transmit);
		kfree(priv->dcontrol2.receive);
		priv->dcontrol2.transmit = NULL; /* get rid of descriptor ring */
		priv->dcontrol2.receive = NULL; /* get rid of descriptor */
		/* probably should do some deallocation of sk_buffs*/
		/* but will take place in the open */
		return -1;
	}
	skb_queue_head(priv->sab8253xbuflist, (struct sk_buff*) desc->HostVaddr);
	desc->Count = rbufsize|OWN_SAB;	/* belongs to int handler */
	desc->crc = 0;
	desc->sendcrc = 0;
	desc->crcindex = 0;
	
	/* setup the various pointers */
	priv->active2 = priv->dcontrol2; /* insert new skbuff */
	priv->sabnext2 = priv->dcontrol2; /* transmit from here */
	
	return 0;
}

/* loads program, waits for PPC */
/* and completes initialization*/

void Sab8253xCleanUpTransceiveN(SAB_PORT* priv)
{
	Sab8253xFreeAllFreeListSKBUFFS(priv);	
	Sab8253xFreeAllReceiveListSKBUFFS(priv);
	
	/* these are also cleaned up in the module cleanup routine */
	/* should probably only be done here */
	if(priv->dcontrol2.receive)
	{
		kfree(priv->dcontrol2.receive);
		priv->dcontrol2.receive = NULL;
	}
	if(priv->dcontrol2.transmit)
	{
		kfree(priv->dcontrol2.transmit);
		priv->dcontrol2.transmit = NULL;
	}
	priv->active2 = priv->dcontrol2;
	priv->sabnext2 = priv->dcontrol2; 
}

void Sab8253xFreeAllReceiveListSKBUFFS(SAB_PORT* priv) /* empty the skbuffer list */
/* either on failed open */
/* or on close*/
{
	struct sk_buff* skb;
	
	if(priv->sab8253xc_rcvbuflist == NULL)
	{
		return;
	}
	
	DEBUGPRINT((KERN_ALERT "sab8253x: freeing %i skbuffs.\n", 
		    skb_queue_len(priv->sab8253xc_rcvbuflist)));
	
	while(skb_queue_len(priv->sab8253xc_rcvbuflist) > 0)
	{
		skb = skb_dequeue(priv->sab8253xc_rcvbuflist);
		dev_kfree_skb_any(skb);
	}
	kfree(priv->sab8253xc_rcvbuflist);
	priv->sab8253xc_rcvbuflist = NULL;
}

/*
 * This routine is called to set the UART divisor registers to match
 * the specified baud rate for a serial port.
 */

void sab8253x_change_speedN(struct sab_port *port)
{
	unsigned long	flags;
	unsigned char ccr2=0, ccr4=0, ebrg=0;
	int bits = 8;
	
#ifdef DEBUGGING
	printk("Change speed!  ");
#endif
	
	if(!sab8253x_baud(port, (port->baud)*2, &ebrg, &ccr2, &ccr4, &(port->baud))) 
	{
		printk("Aurora Warning. baudrate %ld could not be set! Using 115200", port->baud);
		port->baud = 115200;
		sab8253x_baud(port, (port->baud*2), &ebrg, &ccr2, &ccr4, &(port->baud));
	}
	
	if (port->baud)
	{
		port->timeout = (port->xmit_fifo_size * HZ * bits) / port->baud;
		port->cec_timeout = port->tec_timeout >> 2;
	}
	else
	{
		port->timeout = 0;
		port->cec_timeout = SAB8253X_MAX_CEC_DELAY;
	}
	port->timeout += HZ / 50;		/* Add .02 seconds of slop */
	
	save_flags(flags); 
	cli();
	sab8253x_cec_wait(port);
	
	WRITEB(port, bgr, ebrg);
	WRITEB(port, ccr2, READB(port, ccr2) & ~(0xc0)); /* clear out current baud rage */
	WRITEB(port, ccr2, READB(port, ccr2) | ccr2);
	WRITEB(port, ccr4, (READB(port,ccr4) & ~SAB82532_CCR4_EBRG) | ccr4);
	
	if (port->flags & FLAG8253X_CTS_FLOW) 
	{
		WRITEB(port, mode, READB(port,mode) & ~(SAB82532_MODE_RTS));
		port->interrupt_mask1 &= ~(SAB82532_IMR1_CSC);
		WRITEB(port, imr1, port->interrupt_mask1);
	} 
	else 
	{
		WRITEB(port, mode, READB(port,mode) | SAB82532_MODE_RTS);
		port->interrupt_mask1 |= SAB82532_IMR1_CSC;
		WRITEB(port, imr1, port->interrupt_mask1);
	}
	WRITEB(port, mode, READB(port, mode) | SAB82532_MODE_RAC);
	restore_flags(flags);
}

void sab8253x_shutdownN(struct sab_port *port)
{
	unsigned long flags;
	
	if (!(port->flags & FLAG8253X_INITIALIZED))
	{
		return;
	}
	
	save_flags(flags); cli(); /* Disable interrupts */
	
	/*
	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
	 * here so the queue might never be waken up
	 */
	wake_up_interruptible(&port->delta_msr_wait);
	
	/* Disable Interrupts */
	
	port->interrupt_mask0 = 0xff;
	WRITEB(port, imr0, port->interrupt_mask0);
	port->interrupt_mask1 = 0xff;
	WRITEB(port, imr1, port->interrupt_mask1);
	
	LOWER(port,rts);
	LOWER(port,dtr);
	
	/* Disable Receiver */	
	CLEAR_REG_BIT(port,mode,SAB82532_MODE_RAC);
	
	/* Power Down */	
	CLEAR_REG_BIT(port,ccr0,SAB82532_CCR0_PU);
	
	port->flags &= ~FLAG8253X_INITIALIZED;
	restore_flags(flags);
}

int sab8253x_block_til_ready(struct tty_struct *tty, struct file * filp,
			     struct sab_port *port)
{
	DECLARE_WAITQUEUE(wait, current);
	int retval;
	int do_clocal = 0;
	unsigned long flags;
	
	/*
	 * If the device is in the middle of being closed, then block
	 * until it's done, and then try again.
	 */
	if (tty_hung_up_p(filp) ||
	    (port->flags & FLAG8253X_CLOSING)) 
	{
		if (port->flags & FLAG8253X_CLOSING)
		{
			interruptible_sleep_on(&port->close_wait); /* finish up previous close */
		}
#ifdef SERIAL_DO_RESTART
		if (port->flags & FLAG8253X_HUP_NOTIFY)
		{
			return -EAGAIN;
		}
		else
		{
			return -ERESTARTSYS;
		}
#else
		return -EAGAIN;
#endif
	}
	
	/*
	 * If this is a callout device, then just make sure the normal
	 * device isn't being used.
	 */
	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) 
	{
		if (port->flags & FLAG8253X_NORMAL_ACTIVE) 
		{
			return -EBUSY;	/* async, sync tty or network driver active */
		}
		if ((port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
		    (port->flags & FLAG8253X_SESSION_LOCKOUT) &&
		    (port->session != current->session))
		{
			return -EBUSY;
		}
		if ((port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
		    (port->flags & FLAG8253X_PGRP_LOCKOUT) &&
		    (port->pgrp != current->pgrp))
		{
			return -EBUSY;
		}
		port->flags |= FLAG8253X_CALLOUT_ACTIVE; /* doing a callout */
		return 0;
	}
	
	/* sort out async vs sync tty, not call out */
	/*
	 * If non-blocking mode is set, or the port is not enabled,
	 * then make the check up front and then exit.
	 */
	
	if ((filp->f_flags & O_NONBLOCK) ||
	    (tty->flags & (1 << TTY_IO_ERROR))) 
	{
		if (port->flags & FLAG8253X_CALLOUT_ACTIVE)
		{
			return -EBUSY;
		}
		port->flags |= FLAG8253X_NORMAL_ACTIVE;
		return 0;
	}
	
	if (port->flags & FLAG8253X_CALLOUT_ACTIVE) 
	{
		if (port->normal_termios.c_cflag & CLOCAL)
		{
			do_clocal = 1;
		}
	} 
	else if (tty->termios->c_cflag & CLOCAL)
	{
		do_clocal = 1;
	}
	
	/*
	 * Block waiting for the carrier detect and the line to become
	 * free (i.e., not in use by the callout).  While we are in
	 * this loop, port->count is dropped by one, so that
	 * sab8253x_close() knows when to free things.  We restore it upon
	 * exit, either normal or abnormal.
	 */
	
	/* The port decrement logic is probably */
	/* broken -- hence if def'd out -- it does*/
	retval = 0;
	add_wait_queue(&port->open_wait, &wait); /* starts the wait but does not block here */
	port->blocked_open++;
	while (1) 
	{
		save_flags(flags);
		cli();
		if (!(port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
		    (tty->termios->c_cflag & CBAUD)) 
		{
			RAISE(port, dtr);
			RAISE(port, rts);	/* maybe not correct for sync */
			/*
			 * ??? Why changing the mode here? 
			 *  port->regs->rw.mode |= SAB82532_MODE_FRTS;
			 *  port->regs->rw.mode &= ~(SAB82532_MODE_RTS);
			 */
		}
		restore_flags(flags);
		current->state = TASK_INTERRUPTIBLE;
		if (tty_hung_up_p(filp) ||
		    !(port->flags & FLAG8253X_INITIALIZED)) 
		{
#ifdef SERIAL_DO_RESTART
			if (port->flags & FLAG8253X_HUP_NOTIFY)
			{
				retval = -EAGAIN;
			}
			else
			{
				retval = -ERESTARTSYS;	
			}
#else
			retval = -EAGAIN;
#endif
			break;
		}
		if (!(port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
		    !(port->flags & FLAG8253X_CLOSING) &&
		    (do_clocal || ISON(port,dcd))) 
		{
			break;
		}
#ifdef DEBUG_OPEN
		printk("sab8253x_block_til_ready:2 flags = 0x%x\n",port->flags);
#endif
		if (signal_pending(current)) 
		{
			retval = -ERESTARTSYS;
			break;
		}
#ifdef DEBUG_OPEN
		printk("sab8253x_block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x\n",
		       port->line, port->count, port->flags, do_clocal, READB(port,vstr));
#endif
		schedule();
	}
	current->state = TASK_RUNNING;
	remove_wait_queue(&port->open_wait, &wait);
	port->blocked_open--;
#ifdef DEBUG_OPEN
	printk("sab8253x_block_til_ready after blocking: ttys%d, count = %d\n",
	       port->line, port->count);
#endif
	if (retval)
	{
		return retval;
	}
	port->flags |= FLAG8253X_NORMAL_ACTIVE;
	return 0;
}

/*
 * sab8253x_wait_until_sent() --- wait until the transmitter is empty
 */
void sab8253x_wait_until_sent(struct tty_struct *tty, int timeout)
{
	struct sab_port *port = (struct sab_port *)tty->driver_data;
	unsigned long orig_jiffies, char_time;
	
	if (sab8253x_serial_paranoia_check(port,tty->device,"sab8253x_wait_until_sent"))
	{
		return;
	}
	
	orig_jiffies = jiffies;
	/*
	 * Set the check interval to be 1/5 of the estimated time to
	 * send a single character, and make it at least 1.  The check
	 * interval should also be less than the timeout.
	 * 
	 * Note: we have to use pretty tight timings here to satisfy
	 * the NIST-PCTS.
	 */
	char_time = (port->timeout - HZ/50) / port->xmit_fifo_size;
	char_time = char_time / 5;
	if (char_time == 0)
	{
		char_time = 1;
	}
	if (timeout)
	{
		char_time = MIN(char_time, timeout);
	}
	while ((Sab8253xCountTransmit(port) > 0) || !port->all_sent) 
	{
		current->state = TASK_INTERRUPTIBLE;
		schedule_timeout(char_time);
		if (signal_pending(current))
		{
			break;
		}
		if (timeout && time_after(jiffies, orig_jiffies + timeout))
		{
			break;
		}
	}
}

void sab8253x_flush_buffer(struct tty_struct *tty)
{
	struct sab_port *port = (struct sab_port *)tty->driver_data;
	unsigned long flags;
	register RING_DESCRIPTOR *freeme;
	
	if(sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_flush_buffer"))
	{
		return;
	}
	
	if(port->sabnext2.transmit == NULL)
	{
		return;
	}
	
	save_flags(flags); 
	cli();			/* need to turn off ints because mucking
				   with sabnext2 */
#ifndef FREEININTERRUPT
	freeme = port->active2.transmit;
	do				/* just go all around */
	{
		if(freeme->HostVaddr)
		{
			skb_unlink((struct sk_buff*)freeme->HostVaddr);
			dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr);
			freeme->HostVaddr = NULL;
		}
		freeme->sendcrc = 0;
		freeme->crcindex = 0;
		freeme->Count = OWN_DRIVER;
		freeme = (RING_DESCRIPTOR*) freeme->VNext;
	}
	while(freeme != port->active2.transmit);
#else  /* buffers only from sabnext2.transmit to active2.transmit */
	while((port->sabnext2.transmit->Count & OWNER) == OWN_SAB) /* clear out stuff waiting to be transmitted */
	{
		freeme = port->sabnext2.transmit;
		if(freeme->HostVaddr)
		{
			skb_unlink((struct sk_buff*)freeme->HostVaddr);
			dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr);
			freeme->HostVaddr = NULL;
		}
		freeme->sendcrc = 0;
		freeme->crcindex = 0;
		freeme->Count = OWN_DRIVER;
		port->sabnext2.transmit = freeme->VNext;
	}
#endif
	port->sabnext2.transmit = port->active2.transmit; /* should already be equal to be sure */
	sab8253x_cec_wait(port);
	WRITEB(port,cmdr,SAB82532_CMDR_XRES);
	restore_flags(flags);
	
	tty_wakeup(tty);
}

