/*
 *  linux/drivers/serial/tsuart0.c
 *
 *  Core Driver for Technologic Systems UARTs
 *
 *  (c) 2006 Technologic Systems
 *
 * This source code is hereby licensed to the Linux 2.4 kernel under the
 * GPL, and to Technologic Systems customers under the BSD license.
 *
 * 2006-05-16:mos:fixed parity as it was backwards, some general cleanup done
 * 2006-05-12:mos:initial version completed, based on tsuart.c
 *
 */
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/proc_fs.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/serial_core.h>
//#include <serial_core.h>
#include <asm/hardware/serial_amba.h>
#include <linux/ts_sbc.h>
#include <asm/uaccess.h>
#define TSUART_CORE
#include "tsuart.h"

#define PARM_MASK_TTL 1
#define PARM_MASK_9BIT 2
#define PARM_MASK_FORCE 4
#define PARM_MASK_ISAIO 8
#define PARM_MASK_IDSUP 16

#define DEBUG_ENDS 1
#define DEBUG_CONFIG 2
#define DEBUG_PROBE 4
#define DEBUG_INTERFACE 8
#define DEBUG_CONTROL 16
#define DEBUG_INTERRUPT 32
#define DEBUG_DATA 64
#define DEBUG_TXRX 128
#define DEBUG_PARITY 256

MODULE_DESCRIPTION("Technologic Systems UART modular driver core");

#define DELAY_TIME      HZ / 100        // 100 Hz
#define UART_PORT_SIZE  64              // not used
#define PORT_TSUART     55              // see include/linux/serial_core.c
#define UART_NR		64

#define SERIAL_TSUART_MAJOR	234	// in unassigned range (231-239)
#define SERIAL_TSUART_MINOR	1
#define SERIAL_TSUART_NR	UART_NR
#define SERIAL_TSUART_NAME	"ttyTS"

#define CALLOUT_TSUART_NAME	"cuats"
#define CALLOUT_TSUART_MAJOR	235
#define CALLOUT_TSUART_MINOR	1
#define CALLOUT_TSUART_NR	UART_NR

//-----------------------
// Forward Declarations <
//-----------------------

static struct tsuart_port *TS = 0;
static void tsuart_stop_tx(struct uart_port *port, unsigned int tty_stop);
static struct timer_list timer;
static int timer_added = 0;
static inline int bit_CTS(unsigned char reg_value);

//-----------------------------
// Begin hardware access code <
//-----------------------------

#define TSUART_REG(name,offset) \
static inline unsigned tsuartGet##name(struct tsuart_port *port) {\
  return port->get(port,offset);\
}\
static inline void tsuartPut##name(struct tsuart_port *port,unsigned value) {\
  port->put(port,offset,value);\
}

// TS7XXX_IO8_BASE must be added to iobase
unsigned tsuartGetIO8(struct tsuart_port *port,int offset) {
  return inb(port->u.iobase + offset);
}

unsigned tsuartGetMEM8(struct tsuart_port *port,int offset) {
  return *((volatile unsigned char *)port->u.membase + offset);
}

unsigned tsuartGetMEM16(struct tsuart_port *port,int offset) {
  return *((volatile unsigned short *)port->u.membase + offset);
  /*
  unsigned value;
  value = *((volatile unsigned short *)port->u.membase + offset);
  if (offset == 1) {
    printk("RX:%X\n",value);
  }
  return value;
  */
}

unsigned tsuartGetMEM16_9(struct tsuart_port *port,int offset) {
  unsigned val = *((volatile unsigned short *)port->u.membase + offset);

  if (offset == 1) {
    return val & 0x1FF;
  } else {
    return val;
  }
}

void tsuartPutIO8(struct tsuart_port *port,int offset,unsigned value) {
  outb(value,port->u.iobase + offset);
}

void tsuartPutMEM8(struct tsuart_port *port,int offset,unsigned value) {
  *((volatile unsigned char *)port->u.membase + offset) = value;
}

void tsuartPutMEM16(struct tsuart_port *port,int offset,unsigned value) {
  /*
  if (offset == 1) {
    printk("TX:%X\n",value);
  }
  */
  *((volatile unsigned short *)port->u.membase + offset) = value;
}

void tsuartPutMEM16_9(struct tsuart_port *port,int offset,unsigned value) {
  if (offset != 1) {
    *((volatile unsigned short *)port->u.membase + offset) = value;
  } else if (port->pstatus & PORT_STATUS_TXMSB) {
    if ((value & 0xFE) == 0) {
      port->pstatus = (port->pstatus & 0xFFFF00FF) | ((value & 0xFF) << 8);
      port->pstatus &= ~PORT_STATUS_TXMSB;
      uart_write_wakeup(&port->u);
    } else {
      printk("***TX Rejected (sync)\n");
    }
  } else {
    value += (port->pstatus & 0xFF00);
    *((volatile unsigned short *)port->u.membase + offset) = value;
    port->pstatus |= PORT_STATUS_TXMSB;
  }
}

TSUART_REG(ID,-2)
TSUART_REG(PLDREV,-1)
TSUART_REG(STAT,0)
TSUART_REG(DATA,1)

//--------------------------
// Begin OS dependant code <
//--------------------------


int mincounter=256;

// process the byte in the lsb of data by sending it to the
// appropriate OS buffers for the given TS UART UART
static void tsuart_handleRx(struct tsuart_port *port,int data) {
  struct tty_struct *tty;
  unsigned int ch = data;

  if (port->debugFlags & DEBUG_DATA) {
    unsigned int ttyinf = port->u.info ? (port->u.info->tty ? 3:1) : 0;
    printk("tsuart Rx(%d):%02X:%d\n",port->u.line,ch,ttyinf);
  }
  if (!port->u.info || !port->u.info->tty) {
    return; 
  }
  tty = port->u.info->tty;
  if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
    tty_flip_buffer_push(tty); 
  }
  if (port->rxsize > 3) {
    tty_insert_flip_char(tty, (ch >> 24) & 0xFF, 0);
  }
  if (port->rxsize > 2) {
    tty_insert_flip_char(tty, (ch >> 16) & 0xFF, 0);
  }
  if (port->rxsize > 1) {
    tty_insert_flip_char(tty, (ch >> 8) & 0xFF, 0);
  }
  tty_insert_flip_char(tty, (ch & 0xFF), 0);
  port->gotBytes++;
  return;
}

static inline void tsuart_parity_Tx(struct tsuart_port *port,unsigned data) {
  if (port->parity) {
    //    printk("Tx parity: %X -> ",data);
    data = port->parity[data & port->dataMask2];
    //    printk("%X\n",data);
  }
  tsuartPutDATA(port,data);
}

// If there is data ready to be transmitted for the given UART,
// then feed to the byte to the UART
static void tsuart_try_handleTx(struct tsuart_port *port) {
  struct circ_buf *xmit;
  unsigned int ch=0;

  if (!port->u.info || !port->u.info->tty) {
    if (port->debugFlags & DEBUG_DATA) {
      unsigned int ttyinf = port->u.info ? (port->u.info->tty ? 3:1) : 0;
      printk("tsuart Tx(%d) <%d>\n",port->u.line,ttyinf);
    }
    return;
  }
  if (port->u.info->flags & ASYNC_CTS_FLOW) {
    if (bit_CTS(tsuartGetSTAT(port))) {
      port->u.info->tty->hw_stopped = 0;
    } else {
      port->u.info->tty->hw_stopped = 1;
      if (!timer_added) {
	timer_added = 1;
        if (port->debugFlags & DEBUG_INTERRUPT) {
	  printk("Adding timer (%d)\n",port->u.line);
	}
	timer.expires = jiffies + DELAY_TIME;
	add_timer(&timer);
      }
    }
  }
  xmit = &port->u.info->xmit;
  if (uart_circ_empty(xmit) || uart_tx_stopped(&(port->u))) {
    tsuart_stop_tx(&port->u,0);
    return;
  }
  // handle one character
  ch = xmit->buf[xmit->tail];
  if (port->debugFlags & DEBUG_DATA) {
    unsigned int ttyinf = port->u.info ? (port->u.info->tty ? 3:1) : 0;
    printk("tsuart Tx(%d):%02X:%d\n",port->u.line,ch,ttyinf);
  }
  tsuart_parity_Tx(port,ch);
  xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
  port->u.icount.tx++;
  // done handling one character

  if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
    uart_write_wakeup(&port->u);
  }
  if (uart_circ_empty(xmit)) {
    if (port->debugFlags & DEBUG_TXRX) {
      printk("tsuart stopping xmit at end\n");
    }
    tsuart_stop_tx(&port->u, 0);
  }
}


//----------------------------
// Begin FPGA dependant code <
//----------------------------

// Note: Most functions moved to tsuart.h since they are inline!

// accepts the value read from the TS UART STAT register
// returns the baud rate
/*
static unsigned bits_BAUD(unsigned reg_value) {
  int encoded_rate;

  encoded_rate = (reg_value & 0xE0) >> 5;
  switch (encoded_rate) {
  case 0: return 115200;
  case 1: return 57600;
  case 2: return 38400;
  case 3: return 19200;
  case 4: return 9600;
  case 5: return 4800;
  case 6: return 2400;
  case 7: return 0;    // IRQ off
  }
  return 0; // not reached
}
*/

// accepts the value read from the TS UART STAT register,
// and the encoded baud rate to set, and returns the value to be written
// back to the STAT register.
static inline unsigned encode_BAUD(unsigned reg_value,int bits) {
  return (reg_value & 0xFFFFFF1F) | (bits << 5);
}

// accepts the value read from the TS UART STAT register,
// encodes the given baud rate, and then returns the value to be written
// back to the STAT register.  If the passed baud rate is not supported
// the passed value is returned unchanged.
static unsigned assign_bits_BAUD(unsigned reg_value,int baud_rate) {
  switch (baud_rate) {
  case 115200: return encode_BAUD(reg_value,0);
  case 57600:  return encode_BAUD(reg_value,1);
  case 38400:  return encode_BAUD(reg_value,2);
  case 19200:  return encode_BAUD(reg_value,3);
  case 9600:   return encode_BAUD(reg_value,4);
  case 4800:   return encode_BAUD(reg_value,5);
  case 2400:   return encode_BAUD(reg_value,6);
  case 0:      return encode_BAUD(reg_value,7);
  default:     return reg_value;
  }
}

//---------------------
// End dependant code <
//---------------------

static inline unsigned tsuart_parity_Rx(struct tsuart_port *port) {
  unsigned data,rxdata = tsuartGetDATA(port) & port->dataMask;

  if (port->localStatus & PORT_STATUS_PAR) {
    //    printk("Rx parity: %X -> ",rxdata);
    data = rxdata & port->dataMask2;
    //    printk("%X (%X)\n",data,port->parity[data]);
  } else {
    data = rxdata & port->dataMask2;
  }

  if (!port->parity || port->parity[data] == rxdata) {
    return data;
  } else {
    port->u.icount.parity++;
    return -1;
  }
}

// Check if data is ready to be received from the specified UART
// and if so, read it into the data location provided.
static inline void tsuart_checkRx(struct tsuart_port *port) {
  int stat = tsuartGetSTAT(port);

  if ((port->localStatus & PORT_STATUS_OPEN) || !port->portStatus
      || !(*(port->portStatus) & PORT_STATUS_OPEN)) {
    if (bit_DR(stat)) {
      if (bit_OERR(stat)) {
	port->u.icount.overrun++;
      }
      port->data = tsuart_parity_Rx(port);
    }
  } else {
  }

}


// See if the given UART wants to transmit data, and if so, 
// transmit data if any is ready to be sent.
static inline void tsuart_checkHandleTx(struct tsuart_port *port) {
  int stat;

  // Check the STAT register, and if it says the transmit
  // buffer is empty, then check if we have any data to send.
  // We do it in this order for two reasons. First, because if 
  // the transmit register became empty after our last access 
  // to the UART, we may wind up getting another interrupt for 
  // which we may have nothing to do (except clear the IRQ), so
  // we might as well do that here.  Second, it probably less
  // expensive to check STAT then it is to see if we have more
  // data to send, so if we don't have to do both we might as
  // well not do the more expensive operation.
  
  
  stat = tsuartGetSTAT(port);
  if (bit_TBRE(stat)) { // if buffer empty, then try to send
    tsuart_try_handleTx(port);
  }
}

// See if we read any data, and if so, pass it to the upper layers
static inline void tsuart_try_handleRx(struct tsuart_port *port) {
  if (port->data >= 0) {
    tsuart_handleRx(port,port->data);
  }
}

//-----------------------------------------------------------------------
// Interrupt Routine                                                    <
//-----------------------------------------------------------------------

static void tsuart_interrupt(int irq, void *dev_id, struct pt_regs *regs) {
  struct tsuart_port *root,*port = (struct tsuart_port *)dev_id;
  unsigned counter=256;

  if (port->debugFlags & DEBUG_INTERRUPT) {  
    printk("tsuart_interrupt()\n");
  }
  port = root = port->root;
  while (port) {
    port->gotBytes = 0;
    port = port->next;
  }
  while (--counter) {
    port = root;
    while (port) {
      port->data = -1;
      tsuart_checkRx(port);
      port = port->next;
    }
    port = root;
    while (port) {
      if (port->data != -1) {
	break;
      }
      port = port->next;
    }
    if (!port) {
      break;
    }
    port = root;
    while (port) {
      tsuart_try_handleRx(port);
      port = port->next;
    }
  }
  if (counter < mincounter) { 
    mincounter = counter; 
  }
  port = root;
  while (port) {
    tsuart_checkHandleTx(port);
    if (port->gotBytes) {
      // stats[gotbytes[i]-1]++;
      if (port->debugFlags & DEBUG_DATA) {
	printk("tsuart_interrupt: pushing buffer %d\n",port->u.line);
      }
      tty_flip_buffer_push(port->u.info->tty);
    }
    port = port->next;
  }
/*
  if (root->debugFlags & DEBUG_INTERRUPT) {  
    printk("end tsuart_interrupt()\n");
  }
*/
}

//-----------------------------------------------------------------------
// Interface Routines                                                   /
//                                                                      \_
//                                                                      /
//                                                                      \_
//                                                                      /
//                                                                      \_
//-----------------------------------------------------------------------

static void tsuart_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {  
    printk("tsuart_stop_tx(%d,%d)\n",PORTNUM(port),tty_stop);
  }
}

static void tsuart_start_tx(struct uart_port *port, unsigned int tty_start)
{
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {  
    printk("tsuart_start_tx(%d,%d)\n",PORTNUM(port),tty_start);
  }
  tsuart_checkHandleTx(TSPORT(port));
}

static void tsuart_stop_rx(struct uart_port *port)
{
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_stop_rx(%d)\n",PORTNUM(port));
  }
}

static void tsuart_enable_ms(struct uart_port *port)
{
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_enable_ms(%d)\n",PORTNUM(port));
  }
}

static unsigned int tsuart_tx_empty(struct uart_port *port)
{
  int bit = bit_TBRE(tsuartGetSTAT(TSPORT(port)));
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_tx_empty called(%d) = %d\n",PORTNUM(port),bit);
  }
  return bit;
}

static unsigned int tsuart_get_mctrl(struct uart_port *port)
{
  struct tsuart_port *p = TSPORT(port);
  unsigned int result = 0;

  if (p->mdmctrl) {
    result |= p->mdmctrl(p,0);
  } else {
    result |= TIOCM_CAR;  // pretend DCD is always set
    result |= TIOCM_DSR;  // pretend DSR is always set
  }
  if (bit_CTS(tsuartGetSTAT(TSPORT(port)))) {
    result |= TIOCM_CTS;
  }
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_get_mctrl(%d) CTS=%d\n",PORTNUM(port),
	   (result & TIOCM_CTS) ? 1 : 0);
  }
  return result;
}

static void tsuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
  struct tsuart_port *p = TSPORT(port);

  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_set_mctrl(%d,RTS=%d)\n",PORTNUM(port),((mctrl&TIOCM_RTS) != 0) ? 1:0);
  }
  tsuartPutSTAT(p,assign_bit_RTS(tsuartGetSTAT(p),(mctrl&TIOCM_RTS)!=0));
  if (p->mdmctrl) {
    p->mdmctrl(p,0x80000000 | mctrl);
  } else {
    // No support for TIOCM_DTR, so ignore it
  }
}

static void tsuart_break_ctl(struct uart_port *port, int break_state)
{
  // UART does not support BREAK
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_break_ctl(%d,%d)\n",PORTNUM(port),break_state);
  }
}

static int started = 0;

static void tsuart_cts_timer(unsigned long data) {
  //  struct uart_port *port = (struct uart_port *)(tsuart_ports);
  //  struct uart_port *port = (struct uart_port *)data;
  /*
  if (portXDebug & DEBUG_INTERRUPT) {  
    printk("tsuart_cts_timer()\n");
  }
  */
  timer_added = 0;
  /*
  tsuart_checkHandleTx(port);
  tsuart_checkHandleTx(port+1);
  tsuart_checkHandleTx(port+2);
  */
}

static inline void setMODE9(struct uart_port *port,int on) {
  //  printk("MODE9=%d\n",on);
  tsuartPutSTAT(TSPORT(port),assign_bit_MODE9(tsuartGetSTAT(TSPORT(port)),on));
}

static int tsuart_startup(struct uart_port *port)
{
#if 0
  int i;
#endif

  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_startup(%d), started=%d\n",PORTNUM(port),started+1);
  }
  if (TSPORT(port)->portStatus) {
    if (*(TSPORT(port)->portStatus) & PORT_STATUS_OPEN) {
      printk("Port busy, can't open!\n");
      return -EBUSY;
    } else {
      *(TSPORT(port)->portStatus) |= PORT_STATUS_OPEN;
    }
  }
  TSPORT(port)->localStatus |= PORT_STATUS_OPEN;

  setMODE9(port,TSPORT(port)->rxsize > 1 || (TSPORT(port)->localStatus & PORT_STATUS_8PLUS1));
  if (started++) { // not needed?
    return 0;
  }

  /* create our timer but don't submit it just yet */
  timer.data = (unsigned long )port;
  timer.function = tsuart_cts_timer;

#if 0
  for (i=0;i<256;i++) {
    stats[i] = 0;
  }
#endif
  return 0;
}

static void tsuart_shutdown(struct uart_port *port)
{
  struct tsuart_port *p = TSPORT(port);
#if 0
  int i;
#endif
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_shutdown(%d), started=%d,overflows=%d\n",PORTNUM(port),started-1,port->icount.overrun);
  }
  if (TSPORT(port)->portStatus) {
    if (*(TSPORT(port)->portStatus) & PORT_STATUS_OPEN) {
      *(TSPORT(port)->portStatus) &= ~PORT_STATUS_OPEN;
    } else {
      return; // should not happen???
    }
  }
  TSPORT(port)->localStatus &= ~PORT_STATUS_OPEN;

  if (--started) { // not needed?
    return;
  }
  if (timer_added) {
    del_timer (&timer);
    timer_added = 0;
  }

#if 0
  for (i=0;i<6;i++) {
    printk("tsuart@%d:%d\n",i+1,stats[i]);
  }
#endif

  tsuartPutSTAT(p,assign_bits_BAUD(tsuartGetSTAT(p),0));
}

static int calcpar(int val,int parodd) {
    int ones = 0;

    while (val) {
      if (val & 1) {
        ones++;
      }
      val >>= 1;
    }
    return (ones % 2 == parodd) ? 0 : 1;
}

struct parity_cache {
  int dbits,par,parodd,use;
  unsigned *parity;
  struct parity_cache *next;
};

struct parity_cache *parc = 0;

unsigned *alloc_parity_buffer(int dbits,int par,int parodd) {
  struct parity_cache *pc = parc;
  int entries,size;

  while (pc) {
    if (pc->dbits == dbits && pc->par == par && pc->parodd == parodd) {
      pc->use++;
      return pc->parity;
    }
    pc = pc->next;
  }
  entries = 1 << (dbits+par);
  size = (dbits + par) / 8 + ((dbits + par) % 8) ? 1 : 0;
  pc = kmalloc(sizeof(struct parity_cache),GFP_KERNEL);
  pc->parity = kmalloc(entries * size * sizeof(unsigned),GFP_KERNEL);
  pc->dbits = dbits;
  pc->par = par;
  pc->parodd = parodd;
  pc->use = 1;
  pc->next = parc;
  parc = pc;
  return pc->parity;
}

void dealloc_parity_buffer(unsigned *buffer) {
  struct parity_cache *pc = parc,*last = 0;

  if (!buffer) {
    return;
  }
  while (pc) {
    if (pc->parity == buffer) {
      if (--pc->use == 0) {
	if (last) {
	  last->next = pc->next;
	} else {
	  parc = pc->next;
	}
	kfree(pc->parity);
	kfree(pc);
        return; 
      }
    }
    last = pc;
    pc = pc->next;
  }
}

static void initpar(struct tsuart_port *port,int dbits,int par,int parodd) {
    int entries,i;

    dealloc_parity_buffer(port->parity);
    if (par) {
      port->localStatus |= PORT_STATUS_PAR;
    } else {
      port->localStatus &= ~PORT_STATUS_PAR;
    }
    //    printk("initpar: dbits=%d, par=%d\n",dbits,par);
    entries = 1 << (dbits);
    port->parity = alloc_parity_buffer(dbits,par,parodd);
    for (i=0;i<entries;i++) {
      port->parity[i] = i + (par ? (calcpar(i,parodd) << dbits) : 0);
      //      printk("%X:%X--",i,port->parity[i]);
    }
    //    printk("\n");
}

static void tsuart_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot)
{
  struct tsuart_port *p = TSPORT(port);
  int encoded_baud,baud,par=0,parodd=0,dbits=8; // ,stop=0;

  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_change_speed(%d,%d,%d,%d)\n",PORTNUM(port),cflag,iflag,quot);
  }

  encoded_baud = cflag & CBAUD;
  switch (encoded_baud) {
  case B2400:   baud = 2400; break;
  case B4800:   baud = 4800; break;
  case B9600:   baud = 9600; break;
  case B19200:  baud = 19200; break;
  case B38400:  baud = 38400; break;
  case B57600:  baud = 57600; break;
  case B115200: baud = 115200; break;
  default: baud=0;
  }
  if (baud) {
    if (TSPORT(port)->debugFlags & DEBUG_CONTROL) {
      printk("tsuart:change_speed, port %d: %d baud\n",port->line,baud);
    }
    tsuartPutSTAT(p,assign_bits_BAUD(tsuartGetSTAT(p),baud));
  } else {
    printk("tsuart:unknown change_speed, port %d: %X\n",port->line,encoded_baud);
    printk("%08X & %08X -> %08X\n",cflag,CBAUD,cflag & CBAUD);
  }

  if (cflag & PARENB) {
    par = 1;
    parodd = (cflag & PARODD) != 0;
  }
  /* byte size and parity */
  switch (cflag & CSIZE) {
  case CS7:
    dbits = 7;
    break;
  case CS8:
    dbits = 8;
    break;
  default: // not CS8
    printk("tsuart:unsupported byte size, using CS8\n");
    break;
  }
  //  printk("tsuart_change_speed: %d bits, parity=%s\n",dbits,par?(parodd?"O":"E"):"N");
  /* This test is invalid, because we report 8-bit size even for a 9-bit port
     when we aren't explicitly accessing it as 9-bit
  if (dbits + par + stop > 8*TSPORT(port)->rxsize) {
    printk("tsuart:port does not have enough bits to support requested\n");
    printk("%d+%d+%d>%d\n",dbits,par,stop,8*TSPORT(port)->rxsize);
    //    return;
  }
  */
  TSPORT(port)->dataMask2 = (1 << (dbits)) - 1;
  // TO DO: only allow this on an 8-bit port!
  if (dbits == 8 && par) {
    TSPORT(port)->localStatus |= PORT_STATUS_8PLUS1;
    TSPORT(port)->dataMask = 0x1FF;
  } else {
    TSPORT(port)->localStatus &= ~PORT_STATUS_8PLUS1;
    TSPORT(port)->dataMask = 0xFF;
  }
  setMODE9(port,TSPORT(port)->rxsize > 1 || (TSPORT(port)->localStatus & PORT_STATUS_8PLUS1));

  if (cflag & CSTOPB) {
    printk("tsuart:unsupported stop bits, using 1\n");
  }
  initpar(TSPORT(port),dbits,par,parodd);  
}

static const char *tsuart_type(struct uart_port *port)
{
  return port->type == PORT_TSUART ? "TSUART" : NULL;
}

/*
 * Release the memory region(s) being used by 'port'
 */
static void tsuart_release_port(struct uart_port *port)
{
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_release_port(%d)\n",PORTNUM(port));
  }
  if (port->iotype == SERIAL_IO_MEM) {
    __iounmap(port->membase);
    port->membase = 0;
  }
}

/*
 * Request the memory region(s) being used by 'port'
 */
static int tsuart_request_port(struct uart_port *port) {
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_request_port(%d)\n",PORTNUM(port));
  }
  if (port->iotype == SERIAL_IO_MEM) {
    if (port->membase == 0) {
      port->membase = (void *) __ioremap(port->mapbase & 0xFFFFF000, 4096,0)
	+ (port->mapbase & 0xFFF);
      //      printk("port %p:mapbase=%X, membase=%X\n",port,port->mapbase,port->membase);
      if (port->membase == (void *)(port->mapbase & 0xFFF)) {
	port->membase = 0;
	printk(KERN_ERR "tsuart: cannot map io memory\n");
	return -ENOMEM;
      }
    }
  }
  return 0;
}

/*
 * Configure/autoconfigure the port.
 */
static void tsuart_config_port(struct uart_port *port, int flags)
{
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_config_port(%d,%X)\n",PORTNUM(port),flags);
  }
  if (flags & UART_CONFIG_TYPE) {
    port->type = PORT_TSUART;
    tsuart_request_port(port);
  }
}

/*
 * verify the new serial_struct (for TIOCSSERIAL).
 */
static int tsuart_verify_port(struct uart_port *port, struct serial_struct *ser)
{
  int ret = 0;

  if (port == NULL) return -EINVAL; // avoid warning
  if (ser->type != PORT_UNKNOWN && ser->type != PORT_TSUART)
    ret = -EINVAL;
  if (ser->irq < 0 || ser->irq >= NR_IRQS)
    ret = -EINVAL;
  //  if (ser->baud_base != 115200)
  //    ret = -EINVAL;
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_verify_port(%d,%d)=%d\n",PORTNUM(port),ser->irq,ret);
  }
  return ret;
}

static int tsuart_ioctls (struct uart_port *port, unsigned int cmd, unsigned long arg)
{
  if (TSPORT(port)->debugFlags & DEBUG_INTERFACE) {
    printk("tsuart_ioctls(%d,%d,%ld)\n",PORTNUM(port),cmd,arg);
  }
  return -ENOIOCTLCMD;
}


//-----------------------------------------------------------------------
// Module Definitions                                                   /
//                                                                      \_
//                                                                      /
//                                                                      \_
//                                                                      /
//                                                                      \_
//-----------------------------------------------------------------------



static struct uart_ops tsuart_ops = {
	.stop_tx	= tsuart_stop_tx,
	.start_tx	= tsuart_start_tx,
	.stop_rx	= tsuart_stop_rx,
	.enable_ms	= tsuart_enable_ms,
 	.tx_empty	= tsuart_tx_empty,
	.get_mctrl	= tsuart_get_mctrl,
	.set_mctrl	= tsuart_set_mctrl,
	.break_ctl	= tsuart_break_ctl,
	.startup	= tsuart_startup,
	.shutdown	= tsuart_shutdown,
	.change_speed	= tsuart_change_speed,
	.type		= tsuart_type,
	.release_port	= tsuart_release_port,
	.request_port	= tsuart_request_port,
	.config_port	= tsuart_config_port,
	.verify_port	= tsuart_verify_port,
	.ioctl		= tsuart_ioctls,
};

static inline char *kstrcat(char *src,char *src2) {
  char *dst;

  dst = kmalloc(strlen(src)+strlen(src2)+1,GFP_KERNEL);
  if (!dst) {
    printk("Fatal: Unable to kmalloc\n");
  }
  strcpy(dst,src);
  strcat(dst,src2);
  return dst;
}

struct uart_driver_list {
  struct uart_driver *car;
  int lines;
  struct uart_driver_list *cdr;  
};

struct uart_driver_list *drivers = 0;

int tsuart_configure_driver(struct uart_driver *dr,int major,int minor,
			    char *tty,char *cua,int maxports) {
  struct uart_driver_list *udl;
  char *tty1,*cua1;

#ifdef CONFIG_DEVFS_FS
  tty = kstrcat(tty,"%d");
  cua = kstrcat(cua,"%d");
#endif

  memset(dr,0,sizeof(struct uart_driver));
  dr->owner = THIS_MODULE;
  dr->normal_major = major;
  tty1 = kmalloc(strlen(tty)+1,GFP_KERNEL);
  strcpy(tty1,tty);
  dr->normal_name = tty1;
  cua1 = kmalloc(strlen(cua)+1,GFP_KERNEL);
  strcpy(cua1,cua);
  dr->callout_name = cua1;

  dr->normal_driver = kmalloc(sizeof(struct tty_driver),GFP_KERNEL);
  memset(dr->normal_driver,0,sizeof(struct tty_driver));
  if (dr->normal_driver == NULL) {
  }
  dr->callout_major = major+1;
  dr->callout_driver = kmalloc(sizeof(struct tty_driver),GFP_KERNEL);
  memset(dr->callout_driver,0,sizeof(struct tty_driver));
  if (dr->normal_driver == NULL) {
  }
  dr->table = kmalloc(sizeof(struct tty_struct *) * maxports,GFP_KERNEL);
  memset(dr->table,0,sizeof(struct tty_struct *) * maxports);
  if (dr->table == NULL) {
    printk("Unable to allocate memory\n");
    return 0;
  }
  dr->termios = kmalloc(sizeof(struct termios *) * maxports,GFP_KERNEL);
  memset(dr->termios,0,sizeof(struct termios *) * maxports);
  if (dr->termios == NULL) {
    printk("Unable to allocate memory\n");
    kfree(dr->table);
    return 0;
  }
  dr->termios_locked = kmalloc(sizeof(struct termios *) * maxports,GFP_KERNEL);
  memset(dr->termios_locked,0,sizeof(struct termios *) * maxports);
  if (dr->termios_locked == NULL) {
    printk("Unable to allocate memory\n");
    kfree(dr->table);
    kfree(dr->termios);    
    return 0;
  }
  dr->minor = minor;
  dr->nr = maxports;
  dr->cons = NULL;
  //  printk("Registering UART driver %p with minor %d\n",dr,dr->minor);
  if (uart_register_driver(dr)) {
    printk("Unable to register driver\n");
    return 0;
  }
  udl = kmalloc(sizeof(struct uart_driver_list),GFP_KERNEL);
  if (!udl) {
    printk("Unable to kmalloc memory for uart driver\n");
    return 0;
  }
  udl->car = dr;
  udl->lines = 0;
  udl->cdr = drivers;
  drivers = udl;
  return 1;
}

#define TTY_NAME SERIAL_TSUART_NAME
#define CUA_NAME CALLOUT_TSUART_NAME


static struct uart_driver tsuart_reg;

typedef int (*portCompF)(struct tsuart_port *,struct tsuart_port *);

/*
static int irqsEqual(struct tsuart_port *p1,struct tsuart_port *p2) {
  return p1->u.irq == p2->u.irq;
}
*/

static int portsEqual(struct tsuart_port *p1,struct tsuart_port *p2) {
  if (p1->u.iotype != p2->u.iotype) {
    return 0;
  }
  if (p1->u.iotype == SERIAL_IO_PORT) {
    return (p1->u.iobase == p2->u.iobase) && (p1->portStatus!=p2->portStatus);
  } else {
    return p1->u.mapbase == p2->u.mapbase && (p1->portStatus!=p2->portStatus);
  }
  return 1; // unreachable
}

static int portFind(struct tsuart_port *port,portCompF comp) {
  struct tsuart_port *nextd,*next;

  nextd = TS;
  while (nextd) {
    next = nextd;
    while (next) {
      if (comp(next,port)) {
	return 1;
      }
      next = next->next;
    }
    nextd = nextd->nextd;
  }
  return 0;
}

static inline int portExists(struct tsuart_port *port) {
  return portFind(port,portsEqual);
}

/*
static inline int portIrqInUse(struct tsuart_port *port) {
  return portFind(port,irqsEqual);
}
*/

inline int shares(struct tsuart_port *port) {
  return (port->localStatus & PORT_STATUS_SHIRQ) == PORT_STATUS_SHIRQ;
}

static int irqConflict(struct tsuart_port *port) {
  struct tsuart_port *next;

  if (!TS) {
    return 0;
  }
  next = TS;
  while (next) {
    if (next->u.irq == port->u.irq) {
      // allow only if all ports already registered are either
      // 1. the same boardId as the new port or
      // 2. allowing sharing, and the new port allows sharing
      while (next) {
	if (next->boardId == port->boardId) {
	  return 0;
	}
	if (!shares(port) || !shares(next)) {
	  return 1;
	}
	next = next->nextd;
      }
      return 0;
    }
    next = next->next;
  }
  return 0;
}

// Returns TRUE if an IRQ is already allocated for the given port
static int addPort(struct tsuart_port *port) {
  struct tsuart_port *next;

  if (!TS) {
    TS = port;
    port->root = port;
    return 0;
  }
  next = TS;
  while (next) {
    if (next->u.irq == port->u.irq) {
      port->root = next;
      while (next->next) {
	next = next->next;
      }
      next->next = port;
      return 1;
    }
    if (next->nextd) {
      next = next->nextd;
    } else {
      next->nextd = port;
      port->root = port;
      return 0;
    }
  }
  return 0; // unreachable
}

int tsuart_sanity_check_STAT(struct tsuart_port *port) {
  unsigned char stat,stat2;

  stat = tsuartGetSTAT(port);
  tsuartPutSTAT(port,(stat & 0xFF) | 0xEF);
  stat2 = tsuartGetSTAT(port);
  if (port->debugFlags & DEBUG_PROBE) {
    printk("STAT probe: %X %X\n",stat,stat2);
  }
  tsuartPutSTAT(port,stat);
  return ((stat & 0x1F) == (stat2 & 0x1F) && (stat2 & 0xE0) == 0xE0);
}

int tsuart_register_port(struct uart_driver *driver,
				struct tsuart_port *port) {
  struct uart_port *p = (struct uart_port *)(port);
  struct uart_state *state;
  int ret=0;
  struct uart_driver_list *d = drivers;

  if (!driver) {
    driver = &tsuart_reg;
  }
  while (d) {
    if (d->car == driver) {
      break;
    }
    d = d->cdr;
  }
  if (!d) {
    printk("Trying to register port on an unregistered driver\n");
    return -1;
  }
  port->driver = driver;
  if (irqConflict(port)) {
    printk("IRQ in use\n");
    return -1;
  }
  if (portExists(port)) {
    printk("Port already registered\n");
    return -1;
  }
  port->u.ops = &tsuart_ops;
  port->u.line = d->lines;
  state = driver->state + port->u.line;
  if ((ret = uart_add_one_port(driver,p)) == 0) {
    if (port->debugFlags & DEBUG_ENDS) {
      printk("registered port %p\n",port);
    }    
    if (!addPort(port)) {		
      ret=request_irq(port->u.irq, tsuart_interrupt, 
		      port->localStatus & PORT_STATUS_SHIRQ ?
		      SA_SHIRQ : 0, 
		      "tsuart_uarts", p);
      if (ret != 0) {
	printk("Unable to allocate irq %d\n",port->u.irq);
      } else {
	if (TSPORT(port)->debugFlags & DEBUG_INTERRUPT) {
	  printk("Allocated IRQ %d\n",port->u.irq);
	}
      }
    } else {
	if (TSPORT(port)->debugFlags & DEBUG_INTERRUPT) {
	  printk("IRQ %d already allocated\n",port->u.irq);
	}
    }
  } else {
    if (port->debugFlags & DEBUG_ENDS) {
      printk("Failed to register port %p with driver %p, ret=%d\n",port,driver,ret);
    }
    return -1;
  }
  if (ret == 0) {
    d->lines++;

    if (!tsuart_sanity_check_STAT(port)) {
      printk("WARNING: port %d failed STAT sanity check!\n",port->u.line);
    }
  }
  return ret;
}

/*
int tsuart_unregister_port(struct tsuart_port *port) {
  struct tsuart_port *next;

  if (!portExists(port)) {
    printk("Port not registered\n");
    return -1;
  }
  if (port->root == port && !port->next) {
    free_irq(port->u.irq,port);
  }
  // This isn't quite right I don't think...
  if (port->root == port) {
    if (port->next) {
      // replace port with next in nextd list
      if (TS == port) {
	TS = port->nextd;
      } else {
	next = TS;
	while (next->nextd) {
	}
      }
    } else {
      // unlink port from nextd list
    }
    // remove port from nextd list...
  } else {
    // remote port from next list...
    next = port->root;
    while (next) {
    }
  }
  uart_remove_one_port(port->driver, (struct uart_port *)port);
  // TO DO: we are responsible for kfree()'ing port
  return -1;
}
*/

static int __init tsuart_init(void)
{
  tsuart_configure_driver(&tsuart_reg,SERIAL_TSUART_MAJOR,SERIAL_TSUART_MINOR,
                          TTY_NAME,CUA_NAME,64);
  
  return 0;
}

static void __exit tsuart_exit(void)
{
  struct tsuart_port *next,*nextd,*tmp;
  struct parity_cache *pc = parc;

  //printk("tsuart_exit\n");
  nextd = TS;
  while (nextd) {
    //  printk("nextd=%p\n",nextd);
    if (portDebug & DEBUG_ENDS) {
      printk("Freeing irq %d\n",nextd->u.irq);
    }
    free_irq(nextd->u.irq,nextd);
    next = nextd;
    nextd = nextd->nextd;
    do {
      //      printk("next=%p\n",next);
      if (portDebug & DEBUG_ENDS) {
	printk("Removing port %p\n",next);
      }
      uart_remove_one_port(next->driver, (struct uart_port *)next);
      tmp = next;
      next = next->next;
      kfree(tmp);
    } while (next); 
  }
  //  printk("unregistering drivers\n");
  while (drivers) {
    if (drivers->car) {
      //printk("unregistering driver %p\n",drivers->car);
      uart_unregister_driver(drivers->car);
    }
    drivers = drivers->cdr;
  }
  //  printk("done unregistering drivers\n");
  while (parc) {
    pc = parc->next;
    kfree(parc->parity);
    kfree(parc);
    parc = pc;
  }
}

module_init(tsuart_init);
module_exit(tsuart_exit);

EXPORT_SYMBOL(tsuart_register_port);
//EXPORT_SYMBOL(tsuart_unregister_port);
EXPORT_SYMBOL(tsuartGetIO8);
EXPORT_SYMBOL(tsuartGetMEM8);
EXPORT_SYMBOL(tsuartGetMEM16);
EXPORT_SYMBOL(tsuartGetMEM16_9);
EXPORT_SYMBOL(tsuartPutIO8);
EXPORT_SYMBOL(tsuartPutMEM8);
EXPORT_SYMBOL(tsuartPutMEM16);
EXPORT_SYMBOL(tsuartPutMEM16_9);
EXPORT_SYMBOL(tsuart_configure_driver);

/*
  This is the core TS-UART driver which implements the functionality of
  interfacing to the TS-UART through the STAT and DATA registers, and
  which provides an interface for the Linux 2.4 operating system.

  When the TS-UART is re-used in a new board design, it is only necessary
  to write a loader driver.  In most cases all the loader driver does is
  probe for ports it knows about.
 */
