summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/ppc403/tty_drv/tty_drv.c
blob: 1d7b0135bd088b62cab8e5e310b103d75a96ac4d (plain) (tree)
1
2
3
4
5
6




                                                            
                                            










                               
                         
                                                                          
                    
 
                                   


                                      
                                

















































                                                                              
                   















































                                                                                        







                                     


















                                                                                                                                       

                    



                                                     

                              
 
                 
 
                                                                   







                                          
 







                                                      
                                                                




























                                                               
                          




                          
                                       
 
                       


 

                                                     
 

                     
                                       



                           
      

                                                        
 
                                                                          





               
 
                       
 
















                                                                
                      












                                                                                
 



                     


                                                                  



                                                              
 


                                      
 




                                                         
                                         

                                         
                                                                            


















                                                           
  
  
              

   
                            

    

                                                                 


                                          
                               




                    
  
  
           

   
                        
                    






                                                     
                                   

     
    


                                                            
                                                                        

                                            
                                                                          
 
                                                                                        
 
                                                                                   


                                                           
                                                   





                                                       
                               
 




                                       
                         


                                                                         
 



                                                          
                         

                         
                                                     
















































                                                          
 
                                                      
 












                                                           
 















































                                                                
 


                     
 







                                   
 


                                                       
 







                                  
 


                                                                  
 







                                   
 


                          
 







                                   
/*
 * This file contains the PowerPC 405GP tty driver.
 *
 * Derived from /c/src/lib/libbsp/i386/shared/comm/tty_drv.c
 *
 * Modifications to PPC405GP by Dennis Ehlin
 *
 */

#define NO_BSP_INIT

#include <stdio.h>
#include <rtems/termiostypes.h>
#include <termios.h>
#include <assert.h>
#include <rtems.h>
#include <rtems/libio.h>
#include "../irq/ictrl.h"
#include <stdlib.h>                                     /* for atexit() */
#include <tty_drv.h>

extern uint32_t bsp_serial_per_sec;
extern bool bsp_serial_external_clock;
extern bool bsp_serial_cts_rts;
extern bool bsp_serial_xon_xoff;
extern uint32_t bsp_serial_rate;

struct ttyasync {
/*---------------------------------------------------------------------------+
| Data Register.
+---------------------------------------------------------------------------*/
  unsigned char RBR;	/* 0x00 */
  #define THR   RBR
/*---------------------------------------------------------------------------+
| Interrupt registers
+---------------------------------------------------------------------------*/
  unsigned char IER;    /* Interrupt Enable Register 0x01 */
  #define IER_RCV		 0x01
  #define IER_XMT		 0x02
  #define IER_LS		 0x04
  #define IER_MS		 0x08

  unsigned char ISR;    /* Interrupt Status Register 0x02 */
  #define ISR_MS		 0x00
  #define ISR_nIP		 0x01
  #define ISR_Tx		 0x02
  #define ISR_Rx		 0x04
  #define ISR_LS		 0x06
  #define ISR_RxTO		 0x0C
  #define ISR_64BFIFO		 0x20
  #define ISR_FIFOworks		 0x40
  #define ISR_FIFOen		 0x80

/*---------------------------------------------------------------------------+
| FIFO Control registers
+---------------------------------------------------------------------------*/
  #define FCR   ISR
  #define FCR_FE		0x01    /* FIFO enable */
  #define FCR_CRF		0x02    /* Clear receive FIFO */
  #define FCR_CTF		0x04    /* Clear transmit FIFO */
  #define FCR_DMA		0x08    /* DMA mode select */
  #define FCR_F64		0x20    /* Enable 64 byte fifo (16750+) */
  #define FCR_RT14		0xC0    /* Set Rx trigger at 14 */
  #define FCR_RT8		0x80    /* Set Rx trigger at 8 */
  #define FCR_RT4		0x40    /* Set Rx trigger at 4 */
  #define FCR_RT1		0x00    /* Set Rx trigger at 1 */

/*---------------------------------------------------------------------------+
| Baud rate divisor registers
+---------------------------------------------------------------------------*/
  #define DLL   RBR
  #define DLM   IER

/*---------------------------------------------------------------------------+
| Alternate function registers
+---------------------------------------------------------------------------*/
  #define AFR   ISR

/*---------------------------------------------------------------------------+
| Line control Register.
+---------------------------------------------------------------------------*/
  unsigned char LCR;	/* 0x03 */
  #define LCR_WL5		 0x00   /* Word length 5 */
  #define LCR_WL6		 0x01   /* Word length 6 */
  #define LCR_WL7		 0x02   /* Word length 7 */
  #define LCR_WL8		 0x03   /* Word length 8 */

  #define LCR_SB1		 0x00	/* 1 stop bits */
  #define LCR_SB1_5		 0x04	/* 1.5 stop bits , only valid with 5 bit words*/
  #define LCR_SB1_5		 0x04	/* 2 stop bits */

  #define LCR_PN		 0x00	/* Parity NONE */
  #define LCR_PE		 0x0C	/* Parity EVEN */
  #define LCR_PO		 0x08	/* Parity ODD */
  #define LCR_PM		 0x28	/* Forced "mark" parity */
  #define LCR_PS		 0x38	/* Forced "space" parity */

  #define LCR_DL		 0x80	/* Enable baudrate latch */

/*---------------------------------------------------------------------------+
| Modem control Register.
+---------------------------------------------------------------------------*/
  unsigned char MCR;	/* 0x04 */
  #define MCR_DTR		 0x01
  #define MCR_RTS		 0x02
  #define MCR_INT		 0x08	/* Enable interrupts */
  #define MCR_LOOP		 0x10	/* Loopback mode */

/*---------------------------------------------------------------------------+
| Line status Register.
+---------------------------------------------------------------------------*/
  unsigned char LSR;	/* 0x05 */
  #define LSR_RSR		 0x01
  #define LSR_OE		 0x02
  #define LSR_PE		 0x04
  #define LSR_FE		 0x08
  #define LSR_BI		 0x10
  #define LSR_THE		 0x20
  #define LSR_TEMT		 0x40
  #define LSR_FIE		 0x80

/*---------------------------------------------------------------------------+
| Modem status Register.
+---------------------------------------------------------------------------*/
  unsigned char MSR;	/* 0x06 */
  #define UART_MSR_DCTS		 0x01
  #define UART_MSR_DDSR		 0x02
  #define UART_MSR_TERI		 0x04
  #define UART_MSR_DDCD		 0x08
  #define UART_MSR_CTS		 0x10
  #define UART_MSR_DSR		 0x20
  #define UART_MSR_RI		 0x40
  #define UART_MSR_CD		 0x80

/*---------------------------------------------------------------------------+
| Scratch pad Register.
+---------------------------------------------------------------------------*/
  unsigned char SCR;	/* 0x07 */
};


#define TTY0_USE_UART 1 /* 0=UART0 1=UART1 */
#define TTY0_UART_INTERNAL_CLOCK_DIVISOR 16
#define TTY0_USE_INTERRUPT


typedef volatile struct ttyasync *tty0pasync;
static const tty0pasync tty0port = (tty0pasync)(0xEF600300   + (TTY0_USE_UART*0x100));	/* 0xEF600300 - port A,  0xEF600400 - port B */

static void *tty0ttyp;         /* handle for termios */


static int
tty0_round(double x)
{
  return (int)((int)((x-(int)x)*1000)>500 ? x+1 : x);
}

static void
tty0BaudSet(uint32_t baudrate)
{
  uint32_t   tmp;

  tmp = tty0_round( (double)bsp_serial_per_sec / (baudrate * 16) );

  tty0port->LCR = tty0port->LCR | LCR_DL;

  tty0port->DLL = tmp & 0xff;
  tty0port->DLM = tmp >> 8;

  tty0port->LCR = tty0port->LCR & ~LCR_DL;
}

/*
 * Hardware-dependent portion of tcsetattr().
 */
static int
tty0SetAttributes (int minor, const struct termios *t)
{
  int baud;

  /* FIXME: check c_cflag & CRTSCTS for hardware flow control */
  /* FIXME: check and IMPLEMENT XON/XOFF                     */
  switch (t->c_cflag & CBAUD) {
  default:	baud = -1;	break;
  case B50:	baud = 50;	break;
  case B75:	baud = 75;	break;
  case B110:	baud = 110;	break;
  case B134:	baud = 134;	break;
  case B150:	baud = 150;	break;
  case B200:	baud = 200;	break;
  case B300:	baud = 300;	break;
  case B600:	baud = 600;	break;
  case B1200:	baud = 1200;	break;
  case B1800:	baud = 1800;	break;
  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;
  case B230400:	baud = 230400;	break;
  case B460800:	baud = 460800;	break;
  }
  if (baud > 0) {
    tty0BaudSet(baud);
  }
  return 0;
}

#ifndef TTY0_USE_INTERRUPT
static int
tty0PollRead (int minor)
{

  /* Wait for character */
  while ((tty0port->LSR & LSR_RSR)==0);

  return tty0port->RBR;
}


static ssize_t
tty0PollWrite(int minor, const char *buf, size_t len)
{

  while (len-- > 0) {
    while (!(tty0port->LSR & LSR_THE));
    tty0port->THR = *buf++;
  }
  return 0;
}
#endif

/* ================ Termios support  =================*/

static ssize_t tty0InterruptWrite (int minor, const char *buf, size_t len)
{

  if(len <= 0)
    {
      return 0;
    }

  /* Write character */

  tty0port->THR = (*buf &0xff);
  tty0port->IER |= IER_XMT;     /* always enable tx interrupt */

  return 0;

}

static rtems_isr tty0serial_ISR(rtems_vector_number v)
{
  char buf[128];
  int      off, ret, vect;

  off = 0;

  for(;;)
    {
	  vect = tty0port->ISR & 0x0f;
	  if(vect & 1)
	  {
		  /* no more interrupts */
          if(off > 0) {
                  /* Update rx buffer */
  		  rtems_termios_enqueue_raw_characters(tty0ttyp, buf, off );

  	          tty0port->IER |= IER_RCV;     /* always enable rx interrupt */
		 /*rtems_termios_rxirq_occured(tty0ttyp);*/
	  }
	    return;
	  }

	vect = vect & 0xe; /*mask out all except interrupt pending*/

    switch(vect)
	{

	case ISR_Tx :
	  /*
	   * TX holding empty: we have to disable these interrupts
	   * if there is nothing more to send.
	   */

	  /* If nothing else to send disable interrupts */
	  ret = rtems_termios_dequeue_characters(tty0ttyp, 1);

          if ( ret == 0 ) {
   	    tty0port->IER &= ~IER_XMT;
          }

	  break;
	case ISR_RxTO:
	case ISR_Rx :

              /* disable interrupts and notify termios */
	       tty0port->IER &= ~IER_RCV;

	      /* read all bytes in fifo*/
  	       while (( off < sizeof(buf) ) && (  tty0port->LSR & LSR_RSR ))
			  {
				buf[off++] = tty0port->RBR;
			  }

	  break;
	case ISR_LS:
	  /* RX error: eat character */
	  /* printk("********* Error **********\n"); */
 	  break;
	default:
	  /* Should not happen */
	  /* printk("error vect=%x",vect); */
	  return;
	}
    }

}


/*
 *
 * deinit TTY0
 *
 */
static void tty0DeInit(void)
{
  /*
   * disable interrupts for serial tty0port
   * set it to state to work with polling boot monitor, if any...
   */

  /* set up baud rate to original state */
  tty0BaudSet(bsp_serial_rate);

  tty0port->IER = 0;

}

/*
 *
 * init SPI
 *
 */
static rtems_status_code
tty0Initialize(void)
{
  register unsigned tmp;
  rtems_isr_entry previous_isr; /* this is a dummy */
  unsigned char _ier;
  unsigned char _tmp;

  /*
   * Initialise the serial tty0port
   */

  /*
   * Select clock source and set uart internal clock divisor
   */

  __asm__ volatile ("mfdcr %0, 0x0b1" : "=r" (tmp)); /* CPC_CR0 0x0b1 */

  /* UART0 bit 24 0x80, UART1 bit 25 0x40 */
  tmp |= (bsp_serial_external_clock ?  (TTY0_USE_UART ? 0x40 : 0x80) : 0);

  tmp |= (bsp_serial_external_clock ?  0: ((TTY0_UART_INTERNAL_CLOCK_DIVISOR -1) << 1));

  __asm__ volatile ("mtdcr 0x0b1, %0" : "=r" (tmp) : "0" (tmp)); /* CPC_CR0 0x0b1*/

  /* Disable tty0port interrupts while changing hardware */
  _ier = tty0port->IER;
  (void) _ier; /* avoid set but not used warning */
  tty0port->IER = 0;

  /* set up tty0port control: 8 bit,1 stop,no parity */
  tty0port->LCR = LCR_WL8 | LCR_SB1 | LCR_PN;

  /* set up baud rate */
  tty0BaudSet(bsp_serial_rate);


#ifdef TTY0_USE_INTERRUPT

    /* add rx/tx isr to vector table */

    if (TTY0_USE_UART==0)
	ictrl_set_vector(tty0serial_ISR,PPC_IRQ_EXT_UART0,&previous_isr);
    else
	ictrl_set_vector(tty0serial_ISR,PPC_IRQ_EXT_UART1,&previous_isr);

    /* Enable and clear FIFO */
    tty0port->FCR = FCR_FE | FCR_CRF | FCR_CTF | FCR_RT14;

    /* Read status to clear them */
    _tmp = tty0port->LSR;
    _tmp = tty0port->RBR;
    _tmp = tty0port->MSR;
    (void) _tmp; /* avoid set but not used warning */

    /* Enable recive interrupts, don't enable TxInt yet */
    tty0port->IER=IER_RCV;

#else

    tty0port->IER=_ier;

#endif

  atexit(tty0DeInit);

  return RTEMS_SUCCESSFUL;
}

/*
 ***************
 * BOILERPLATE *
 ***************
 */

/*  console_initialize
 *
 *  This routine initializes the console IO driver.
 *
 *  Input parameters: NONE
 *
 *  Output parameters:  NONE
 *
 *  Return values:
 */

rtems_device_driver tty0_initialize(
  rtems_device_major_number  major,
  rtems_device_minor_number  minor,
  void                      *arg
)
{
  rtems_status_code status;


  /*
   * Set up TERMIOS
   */
  rtems_termios_initialize ();

  /*
   * Do device-specific initialization
   */

  /*tty0Initialize ();   Moved this to open instead */

  /*
   * Register the device
   */
  status = rtems_io_register_name ("/dev/ttyS0", major, 0);
  if (status != RTEMS_SUCCESSFUL)
    rtems_fatal_error_occurred (status);
  return RTEMS_SUCCESSFUL;
}


/*
 *  Open entry point
 */

rtems_device_driver tty0_open(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void                    * arg
)
{
  rtems_status_code sc;

#ifdef TTY0_USE_INTERRUPT

  static const rtems_termios_callbacks intrCallbacks = {
    NULL,		/* firstOpen */
    NULL,		/* lastClose */
    NULL, 		/* pollRead */
    tty0InterruptWrite,	/* write */
    tty0SetAttributes,  /* setAttributes */
    NULL,	        /* stopRemoteTx */
    NULL,	        /* startRemoteTx */
    TERMIOS_TASK_DRIVEN	/* outputUsesInterrupts */
  };
  rtems_libio_open_close_args_t *args = arg;

    tty0Initialize (); 	/* Initalize hardware */

    sc = rtems_termios_open (major, minor, arg, &intrCallbacks);
    tty0ttyp = args->iop->data1;

#else

  static const rtems_termios_callbacks pollCallbacks = {
    NULL,		/* firstOpen */
    NULL,		/* lastClose */
    tty0PollRead,	/* pollRead */
    tty0PollWrite,	/* write */
    tty0SetAttributes,	/* setAttributes */
    NULL,		/* stopRemoteTx */
    NULL,		/* startRemoteTx */
    0			/* outputUsesInterrupts */
  };

    tty0Initialize (); 	/* Initalize hardware */

    sc = rtems_termios_open (major, minor, arg, &pollCallbacks);

#endif

    return sc;
}

/*
 *  Close entry point
 */

rtems_device_driver tty0_close(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void                    * arg
)
{
  return rtems_termios_close (arg);
}

/*
 * read bytes from the serial port. We only have stdin.
 */

rtems_device_driver tty0_read(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void                    * arg
)
{
  return rtems_termios_read (arg);
}

/*
 * write bytes to the serial port. Stdout and stderr are the same.
 */

rtems_device_driver tty0_write(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void                    * arg
)
{
  return rtems_termios_write (arg);
}

/*
 *  IO Control entry point
 */

rtems_device_driver tty0_control(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void                    * arg
)
{
  return rtems_termios_ioctl (arg);
}