- change all sync char drivers into async drivers; - retire support for the sync protocol in libchardev; - remove async dev style, as this is now the default; - remove dev_status from VFS; - clean up now-unused protocol messages. Change-Id: I6aacff712292f6b29f2ccd51bc1e7d7003723e87
		
			
				
	
	
		
			829 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			829 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include <minix/config.h>
 | 
						|
#include <minix/drivers.h>
 | 
						|
#include <minix/vm.h>
 | 
						|
#include <minix/type.h>
 | 
						|
#include <minix/board.h>
 | 
						|
#include <sys/mman.h>
 | 
						|
#include <assert.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <termios.h>
 | 
						|
#include "omap_serial.h"
 | 
						|
#include "tty.h"
 | 
						|
 | 
						|
#if NR_RS_LINES > 0
 | 
						|
 | 
						|
#define UART_FREQ       48000000L	/* timer frequency */
 | 
						|
#if 0
 | 
						|
#define DFLT_BAUD	TSPEED_DEF		/* default baud rate */
 | 
						|
#else
 | 
						|
#define DFLT_BAUD	B115200		/* default baud rate */
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#define RS_IBUFSIZE          40960	/* RS232 input buffer size */
 | 
						|
#define RS_OBUFSIZE          40960	/* RS232 output buffer size */
 | 
						|
 | 
						|
/* Input buffer watermarks.
 | 
						|
 * The external device is asked to stop sending when the buffer
 | 
						|
 * exactly reaches high water, or when TTY requests it.  Sending restarts
 | 
						|
 * when the input buffer empties below the low watermark.
 | 
						|
 */
 | 
						|
#define RS_ILOWWATER   (1 * RS_IBUFSIZE / 4)
 | 
						|
#define RS_IHIGHWATER  (3 * RS_IBUFSIZE / 4)
 | 
						|
 | 
						|
/* Output buffer low watermark.
 | 
						|
 * TTY is notified when the output buffer empties below the low watermark, so
 | 
						|
 * it may continue filling the buffer if doing a large write.
 | 
						|
 */
 | 
						|
#define RS_OLOWWATER   (1 * RS_OBUFSIZE / 4)
 | 
						|
 | 
						|
/* Macros to handle flow control.
 | 
						|
 * Interrupts must be off when they are used.
 | 
						|
 * Time is critical - already the function call for outb() is annoying.
 | 
						|
 * If outb() can be done in-line, tests to avoid it can be dropped.
 | 
						|
 * istart() tells external device we are ready by raising RTS.
 | 
						|
 * istop() tells external device we are not ready by dropping RTS.
 | 
						|
 * DTR is kept high all the time (it probably should be raised by open and
 | 
						|
 * dropped by close of the device).
 | 
						|
 * OUT2 is also kept high all the time.
 | 
						|
 */
 | 
						|
#define istart(rs) \
 | 
						|
	(serial_out((rs), OMAP3_MCR, UART_MCR_OUT2|UART_MCR_RTS|UART_MCR_DTR),\
 | 
						|
		(rs)->idevready = TRUE)
 | 
						|
#define istop(rs) \
 | 
						|
	(serial_out((rs), OMAP3_MCR, UART_MCR_OUT2|UART_MCR_DTR), \
 | 
						|
		(rs)->idevready = FALSE)
 | 
						|
 | 
						|
/* Macro to tell if device is ready.  The rs->cts field is set to UART_MSR_CTS
 | 
						|
 * if CLOCAL is in effect for a line without a CTS wire.
 | 
						|
 */
 | 
						|
#define devready(rs) ((serial_in(rs, OMAP3_MSR) | rs->cts) & UART_MSR_CTS)
 | 
						|
 | 
						|
/* Macro to tell if transmitter is ready. */
 | 
						|
#define txready(rs) (serial_in(rs, OMAP3_LSR) & UART_LSR_THRE)
 | 
						|
 | 
						|
/* RS232 device structure, one per device. */
 | 
						|
typedef struct rs232 {
 | 
						|
  tty_t *tty;			/* associated TTY structure */
 | 
						|
 | 
						|
  int icount;			/* number of bytes in the input buffer */
 | 
						|
  char *ihead;			/* next free spot in input buffer */
 | 
						|
  char *itail;			/* first byte to give to TTY */
 | 
						|
  char idevready;		/* nonzero if we are ready to receive (RTS) */
 | 
						|
  char cts;			/* normally 0, but MS_CTS if CLOCAL is set */
 | 
						|
 | 
						|
  unsigned char ostate;		/* combination of flags: */
 | 
						|
#define ODONE          1	/* output completed (< output enable bits) */
 | 
						|
#define ORAW           2	/* raw mode for xoff disable (< enab. bits) */
 | 
						|
#define OWAKEUP        4	/* tty_wakeup() pending (asm code only) */
 | 
						|
#define ODEVREADY UART_MSR_CTS	/* external device hardware ready (CTS) */
 | 
						|
#define OQUEUED     0x20	/* output buffer not empty */
 | 
						|
#define OSWREADY    0x40	/* external device software ready (no xoff) */
 | 
						|
#define ODEVHUP  UART_MSR_DCD	/* external device has dropped carrier */
 | 
						|
#define OSOFTBITS  (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY)
 | 
						|
				/* user-defined bits */
 | 
						|
#if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS
 | 
						|
				/* a weak sanity check */
 | 
						|
#error				/* bits are not unique */
 | 
						|
#endif
 | 
						|
  unsigned char oxoff;		/* char to stop output */
 | 
						|
  char inhibited;		/* output inhibited? (follows tty_inhibited) */
 | 
						|
  char drain;			/* if set drain output and reconfigure line */
 | 
						|
  int ocount;			/* number of bytes in the output buffer */
 | 
						|
  char *ohead;			/* next free spot in output buffer */
 | 
						|
  char *otail;			/* next char to output */
 | 
						|
 | 
						|
  phys_bytes phys_base;		/* UART physical base address (I/O map) */
 | 
						|
  unsigned int reg_offset;	/* UART register offset */
 | 
						|
  unsigned int ier;		/* copy of ier register */
 | 
						|
  unsigned int scr;		/* copy of scr register */
 | 
						|
  unsigned int fcr;		/* copy of fcr register */
 | 
						|
  unsigned int dll;		/* copy of dll register */
 | 
						|
  unsigned int dlh;		/* copy of dlh register */
 | 
						|
  unsigned int uartclk;		/* UART clock rate */
 | 
						|
 | 
						|
  unsigned char lstatus;	/* last line status */
 | 
						|
  int rx_overrun_events;
 | 
						|
 | 
						|
  int irq;			/* irq for this line */
 | 
						|
  int irq_hook_id;		/* interrupt hook */
 | 
						|
  int irq_hook_kernel_id;	/* id as returned from sys_irqsetpolicy */
 | 
						|
 | 
						|
  char ibuf[RS_IBUFSIZE];	/* input buffer */
 | 
						|
  char obuf[RS_OBUFSIZE];	/* output buffer */
 | 
						|
} rs232_t;
 | 
						|
 | 
						|
static rs232_t rs_lines[NR_RS_LINES];
 | 
						|
 | 
						|
typedef struct uart_port {
 | 
						|
	phys_bytes base_addr;
 | 
						|
	int irq;
 | 
						|
} uart_port_t;
 | 
						|
 | 
						|
/* OMAP3 UART base addresses. */
 | 
						|
static uart_port_t dm37xx_ports[] = {
 | 
						|
  { OMAP3_UART1_BASE, 72},	/* UART1 */
 | 
						|
  { OMAP3_UART2_BASE, 73},	/* UART2 */
 | 
						|
  { OMAP3_UART3_BASE, 74},	/* UART3 */
 | 
						|
  { 0, 0 }
 | 
						|
};
 | 
						|
 | 
						|
static uart_port_t am335x_ports[] = {
 | 
						|
  {  0x44E09000 , 72 },	/* UART0 */
 | 
						|
  { 0, 0 },
 | 
						|
  { 0, 0 },
 | 
						|
  { 0, 0 }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static int rs_write(tty_t *tp, int try);
 | 
						|
static void rs_echo(tty_t *tp, int c);
 | 
						|
static int rs_ioctl(tty_t *tp, int try);
 | 
						|
static void rs_config(rs232_t *rs);
 | 
						|
static int rs_read(tty_t *tp, int try);
 | 
						|
static int rs_icancel(tty_t *tp, int try);
 | 
						|
static int rs_ocancel(tty_t *tp, int try);
 | 
						|
static void rs_ostart(rs232_t *rs);
 | 
						|
static int rs_break(tty_t *tp, int try);
 | 
						|
static int rs_close(tty_t *tp, int try);
 | 
						|
static int rs_open(tty_t *tp, int try);
 | 
						|
static void rs232_handler(rs232_t *rs);
 | 
						|
static void rs_reset(rs232_t *rs);
 | 
						|
static unsigned int check_modem_status(rs232_t *rs);
 | 
						|
static int termios_baud_rate(struct termios *term);
 | 
						|
 | 
						|
static inline unsigned int readw(vir_bytes addr);
 | 
						|
static inline unsigned int serial_in(rs232_t *rs, int offset);
 | 
						|
static inline void serial_out(rs232_t *rs, int offset, int val);
 | 
						|
static inline void writew(vir_bytes addr, int val);
 | 
						|
static void write_chars(rs232_t *rs);
 | 
						|
static void read_chars(rs232_t *rs, unsigned int status);
 | 
						|
 | 
						|
static inline unsigned int
 | 
						|
readw(vir_bytes addr)
 | 
						|
{
 | 
						|
	return *((volatile unsigned int *) addr);
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
writew(vir_bytes addr, int val)
 | 
						|
{
 | 
						|
	*((volatile unsigned int *) addr) = val;
 | 
						|
}
 | 
						|
 | 
						|
static inline unsigned int
 | 
						|
serial_in(rs232_t *rs, int offset)
 | 
						|
{
 | 
						|
	offset <<= rs->reg_offset;
 | 
						|
	return readw(rs->phys_base + offset);
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
serial_out(rs232_t *rs, int offset, int val)
 | 
						|
{
 | 
						|
	offset <<= rs->reg_offset;
 | 
						|
	writew(rs->phys_base + offset, val);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
rs_reset(rs232_t *rs)
 | 
						|
{
 | 
						|
	u32_t syss;
 | 
						|
 | 
						|
	serial_out(rs, OMAP3_SYSC, UART_SYSC_SOFTRESET);
 | 
						|
 | 
						|
	/* Poll until done */
 | 
						|
	do {
 | 
						|
		syss = serial_in(rs, OMAP3_SYSS);
 | 
						|
	} while (!(syss & UART_SYSS_RESETDONE));
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
rs_write(register tty_t *tp, int try)
 | 
						|
{
 | 
						|
/* (*devwrite)() routine for RS232. */
 | 
						|
 | 
						|
	rs232_t *rs = tp->tty_priv;
 | 
						|
	int r, count, ocount;
 | 
						|
 | 
						|
	if (rs->inhibited != tp->tty_inhibited) {
 | 
						|
		/* Inhibition state has changed. */
 | 
						|
		rs->ostate |= OSWREADY;
 | 
						|
		if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
 | 
						|
		rs->inhibited = tp->tty_inhibited;
 | 
						|
	}
 | 
						|
 | 
						|
	if (rs->drain) {
 | 
						|
		/* Wait for the line to drain then reconfigure and continue
 | 
						|
		 * output. */
 | 
						|
		if (rs->ocount > 0) return 0;
 | 
						|
		rs->drain = FALSE;
 | 
						|
		rs_config(rs);
 | 
						|
	}
 | 
						|
	/* While there is something to do. */
 | 
						|
	for (;;) {
 | 
						|
		ocount = buflen(rs->obuf) - rs->ocount;
 | 
						|
		count = bufend(rs->obuf) - rs->ohead;
 | 
						|
		if (count > ocount) count = ocount;
 | 
						|
		if (count > tp->tty_outleft) count = tp->tty_outleft;
 | 
						|
		if (count == 0 || tp->tty_inhibited) {
 | 
						|
			if (try) return 0;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (try) return 1;
 | 
						|
 | 
						|
		/* Copy from user space to the RS232 output buffer. */
 | 
						|
		if (tp->tty_outcaller == KERNEL) {
 | 
						|
			/* We're trying to print on kernel's behalf */
 | 
						|
			memcpy(rs->ohead,
 | 
						|
				(void *) tp->tty_outgrant + tp->tty_outoffset,
 | 
						|
				count);
 | 
						|
		} else {
 | 
						|
			if ((r = sys_safecopyfrom(tp->tty_outcaller,
 | 
						|
				tp->tty_outgrant, tp->tty_outoffset,
 | 
						|
				(vir_bytes) rs->ohead, count)) != OK) {
 | 
						|
				return 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* Perform output processing on the output buffer. */
 | 
						|
		out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count,
 | 
						|
		    &ocount);
 | 
						|
		if (count == 0) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Assume echoing messed up by output. */
 | 
						|
		tp->tty_reprint = TRUE;
 | 
						|
 | 
						|
		/* Bookkeeping. */
 | 
						|
		rs->ocount += ocount;
 | 
						|
		rs_ostart(rs);
 | 
						|
		if ((rs->ohead += ocount) >= bufend(rs->obuf))
 | 
						|
			rs->ohead -= buflen(rs->obuf);
 | 
						|
		tp->tty_outoffset += count;
 | 
						|
		tp->tty_outcum += count;
 | 
						|
		if ((tp->tty_outleft -= count) == 0) {
 | 
						|
			/* Output is finished, reply to the writer. */
 | 
						|
			tty_reply(DEV_REVIVE, tp->tty_outcaller,
 | 
						|
				tp->tty_outproc, tp->tty_outgrant,
 | 
						|
				tp->tty_outcum);
 | 
						|
			tp->tty_outcum = 0;
 | 
						|
			tp->tty_outgrant = GRANT_INVALID;
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
	if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
 | 
						|
		/* Oops, the line has hung up. */
 | 
						|
		tty_reply(DEV_REVIVE, tp->tty_outcaller, tp->tty_outproc,
 | 
						|
			tp->tty_outgrant, EIO);
 | 
						|
		tp->tty_outleft = tp->tty_outcum = 0;
 | 
						|
		tp->tty_outgrant = GRANT_INVALID;
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
rs_echo(tty_t *tp, int character)
 | 
						|
{
 | 
						|
/* Echo one character.  (Like rs_write, but only one character, optionally.) */
 | 
						|
 | 
						|
	rs232_t *rs = tp->tty_priv;
 | 
						|
	int count, ocount;
 | 
						|
 | 
						|
	ocount = buflen(rs->obuf) - rs->ocount;
 | 
						|
	if (ocount == 0) return;		/* output buffer full */
 | 
						|
	count = 1;
 | 
						|
	*rs->ohead = character;			/* add one character */
 | 
						|
 | 
						|
	out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
 | 
						|
	if (count == 0) return;
 | 
						|
 | 
						|
	rs->ocount += ocount;
 | 
						|
	rs_ostart(rs);
 | 
						|
	if ((rs->ohead += ocount) >= bufend(rs->obuf))
 | 
						|
		rs->ohead -= buflen(rs->obuf);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
rs_ioctl(tty_t *tp, int UNUSED(dummy))
 | 
						|
{
 | 
						|
/* Reconfigure the line as soon as the output has drained. */
 | 
						|
	rs232_t *rs = tp->tty_priv;
 | 
						|
 | 
						|
	rs->drain = TRUE;
 | 
						|
	return 0;	/* dummy */
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int
 | 
						|
omap_get_divisor(rs232_t *rs, unsigned int baud)
 | 
						|
{
 | 
						|
/* Calculate divisor value. The 16750 has two oversampling modes to reach
 | 
						|
 * high baud rates with little error rate (see table 17-1 in OMAP TRM).
 | 
						|
 * Baud rates 460800, 921600, 1843200, and 3686400 use 13x oversampling,
 | 
						|
 * the other rates 16x. The baud rate is calculated as follows:
 | 
						|
 * baud rate = (functional clock / oversampling) / divisor.
 | 
						|
 */
 | 
						|
 | 
						|
	unsigned int oversampling;
 | 
						|
	assert(baud != 0);
 | 
						|
 | 
						|
	switch(baud) {
 | 
						|
	case B460800:	/* Fall through */
 | 
						|
	case B921600:	/* Fall through */
 | 
						|
	case B1843200:	/* Fall through */
 | 
						|
	case B3686400:	oversampling = 13; break;
 | 
						|
	default:	oversampling = 16;
 | 
						|
	}
 | 
						|
 | 
						|
	return (rs->uartclk / oversampling) / baud;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
termios_baud_rate(struct termios *term)
 | 
						|
{
 | 
						|
	int baud;
 | 
						|
	switch(term->c_ospeed) {
 | 
						|
	case B0: term->c_ospeed = DFLT_BAUD; baud = termios_baud_rate(term);
 | 
						|
	case B300: baud = 300; break;
 | 
						|
	case B600: baud = 600; break;
 | 
						|
	case B1200: baud = 1200; break;
 | 
						|
	case B2400: baud = 2400; break;
 | 
						|
	case B4800: baud = 4800; break;
 | 
						|
	case B9600: baud = 9600; break;
 | 
						|
	case B38400: baud = 38400; break;
 | 
						|
	case B57600: baud = 57600; break;
 | 
						|
	case B115200: baud = 115200; break;
 | 
						|
	default: term->c_ospeed = DFLT_BAUD; baud = termios_baud_rate(term);
 | 
						|
	}
 | 
						|
 | 
						|
	return baud;
 | 
						|
}
 | 
						|
static void rs_config(rs232_t *rs)
 | 
						|
{
 | 
						|
/* Set various line control parameters for RS232 I/O.  */
 | 
						|
	tty_t *tp = rs->tty;
 | 
						|
	unsigned int divisor, efr, lcr, mcr, baud;
 | 
						|
 | 
						|
	/* Fifo and DMA settings */
 | 
						|
	/* See OMAP35x TRM 17.5.1.1.2 */
 | 
						|
	lcr = serial_in(rs, OMAP3_LCR);				/* 1a */
 | 
						|
	serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B);	/* 1b */
 | 
						|
	efr = serial_in(rs, OMAP3_EFR);				/* 2a */
 | 
						|
	serial_out(rs, OMAP3_EFR, efr | UART_EFR_ECB);		/* 2b */
 | 
						|
	serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_A);	/* 3  */
 | 
						|
	mcr = serial_in(rs, OMAP3_MCR);				/* 4a */
 | 
						|
	serial_out(rs, OMAP3_MCR, mcr | UART_MCR_TCRTLR);	/* 4b */
 | 
						|
	/* Set up FIFO  */
 | 
						|
	rs->fcr = 0;
 | 
						|
	/* Set FIFO interrupt trigger levels high */
 | 
						|
	rs->fcr |= (0x3 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT);
 | 
						|
	rs->fcr |= (0x3 << OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT);
 | 
						|
	rs->fcr |= UART_FCR_ENABLE_FIFO;
 | 
						|
	serial_out(rs, OMAP3_FCR, rs->fcr);			/* 5  */
 | 
						|
	serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B);	/* 6  */
 | 
						|
	/* DMA triggers, not supported by this driver */	/* 7  */
 | 
						|
	rs->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK;
 | 
						|
	serial_out(rs, OMAP3_SCR, rs->scr);			/* 8  */
 | 
						|
	serial_out(rs, OMAP3_EFR, efr);				/* 9  */
 | 
						|
	serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_A);	/* 10 */
 | 
						|
	serial_out(rs, OMAP3_MCR, mcr);				/* 11 */
 | 
						|
	serial_out(rs, OMAP3_LCR, lcr);				/* 12 */
 | 
						|
	
 | 
						|
	/* RS232 needs to know the xoff character, and if CTS works. */
 | 
						|
	rs->oxoff = tp->tty_termios.c_cc[VSTOP];
 | 
						|
	rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? UART_MSR_CTS : 0;
 | 
						|
	baud = termios_baud_rate(&tp->tty_termios);
 | 
						|
 | 
						|
	/* Look up the 16750 rate divisor from the output speed. */
 | 
						|
	divisor = omap_get_divisor(rs, baud);
 | 
						|
	rs->dll = divisor & 0xFF;
 | 
						|
	rs->dlh = divisor >> 8;
 | 
						|
 | 
						|
	/* Compute line control flag bits. */
 | 
						|
	lcr = 0;
 | 
						|
	if (tp->tty_termios.c_cflag & PARENB) {
 | 
						|
		lcr |= UART_LCR_PARITY;
 | 
						|
		if (!(tp->tty_termios.c_cflag & PARODD)) lcr |= UART_LCR_EPAR;
 | 
						|
  	}
 | 
						|
  	if (tp->tty_termios.c_cflag & CSTOPB) lcr |= UART_LCR_STOP;
 | 
						|
  	switch(tp->tty_termios.c_cflag & CSIZE) {
 | 
						|
  	case CS5:
 | 
						|
  		lcr |= UART_LCR_WLEN5;
 | 
						|
  		break;
 | 
						|
  	case CS6:
 | 
						|
  		lcr |= UART_LCR_WLEN6;
 | 
						|
  		break;
 | 
						|
  	case CS7:
 | 
						|
  		lcr |= UART_LCR_WLEN7;
 | 
						|
  		break;
 | 
						|
  	default:
 | 
						|
  	case CS8:
 | 
						|
  		lcr |= UART_LCR_WLEN8;
 | 
						|
  		break;
 | 
						|
  	}
 | 
						|
 | 
						|
	/* Lock out interrupts while setting the speed. The receiver register
 | 
						|
	 * is going to be hidden by the div_low register, but the input
 | 
						|
	 * interrupt handler relies on reading it to clear the interrupt and
 | 
						|
	 * avoid looping forever.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (sys_irqdisable(&rs->irq_hook_kernel_id) != OK)
 | 
						|
		panic("unable to disable interrupts");
 | 
						|
 | 
						|
	/* Select the baud rate divisor registers and change the rate. */
 | 
						|
	/* See OMAP35x TRM 17.5.1.1.3 */
 | 
						|
	serial_out(rs, OMAP3_MDR1, OMAP_MDR1_DISABLE);		/* 1  */
 | 
						|
	serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B);	/* 2  */
 | 
						|
	efr = serial_in(rs, OMAP3_EFR);				/* 3a */
 | 
						|
	serial_out(rs, OMAP3_EFR, efr | UART_EFR_ECB);		/* 3b */
 | 
						|
	serial_out(rs, OMAP3_LCR, 0);				/* 4  */
 | 
						|
	serial_out(rs, OMAP3_IER, 0);				/* 5  */
 | 
						|
	serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B);	/* 6  */
 | 
						|
	serial_out(rs, OMAP3_DLL, rs->dll);			/* 7  */
 | 
						|
	serial_out(rs, OMAP3_DLH, rs->dlh);			/* 7  */
 | 
						|
	serial_out(rs, OMAP3_LCR, 0);				/* 8  */
 | 
						|
	serial_out(rs, OMAP3_IER, rs->ier);			/* 9  */
 | 
						|
	serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B);	/* 10 */
 | 
						|
	serial_out(rs, OMAP3_EFR, efr);				/* 11 */
 | 
						|
	serial_out(rs, OMAP3_LCR, lcr);				/* 12 */
 | 
						|
	if (baud > 230400 && baud != 3000000)
 | 
						|
		serial_out(rs, OMAP3_MDR1, OMAP_MDR1_MODE13X);	/* 13 */
 | 
						|
	else
 | 
						|
		serial_out(rs, OMAP3_MDR1, OMAP_MDR1_MODE16X);
 | 
						|
	
 | 
						|
	rs->ostate = devready(rs) | ORAW | OSWREADY;	/* reads MSR */
 | 
						|
	if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
 | 
						|
		rs->ostate &= ~ORAW;
 | 
						|
	(void) serial_in(rs, OMAP3_IIR);
 | 
						|
 | 
						|
	if (sys_irqenable(&rs->irq_hook_kernel_id) != OK)
 | 
						|
		panic("unable to enable interrupts");
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rs_init(tty_t *tp)
 | 
						|
{
 | 
						|
/* Initialize RS232 for one line. */
 | 
						|
	register rs232_t *rs;
 | 
						|
	int line;
 | 
						|
	uart_port_t this_omap3;
 | 
						|
	char l[10];
 | 
						|
	struct minix_mem_range mr;
 | 
						|
	struct machine machine;
 | 
						|
 | 
						|
	/* Associate RS232 and TTY structures. */
 | 
						|
	line = tp - &tty_table[NR_CONS];
 | 
						|
 | 
						|
	/* See if kernel debugging is enabled; if so, don't initialize this
 | 
						|
	 * serial line, making tty not look at the irq and returning ENXIO
 | 
						|
	 * for all requests on it from userland. (The kernel will use it.)
 | 
						|
	 */
 | 
						|
	if(env_get_param(SERVARNAME, l, sizeof(l)-1) == OK && atoi(l) == line){
 | 
						|
		printf("TTY: rs232 line %d not initialized (used by kernel)\n",
 | 
						|
			line);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	rs = tp->tty_priv = &rs_lines[line];
 | 
						|
	rs->tty = tp;
 | 
						|
 | 
						|
	/* Set up input queue. */
 | 
						|
	rs->ihead = rs->itail = rs->ibuf;
 | 
						|
 | 
						|
	sys_getmachine(&machine);
 | 
						|
	
 | 
						|
	if (BOARD_IS_BBXM(machine.board_id)){
 | 
						|
		this_omap3 = dm37xx_ports[line];
 | 
						|
	} else if (BOARD_IS_BB(machine.board_id)){
 | 
						|
		this_omap3 = am335x_ports[line];
 | 
						|
	} else {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (this_omap3.base_addr == 0) return;
 | 
						|
 | 
						|
	/* Configure memory access */
 | 
						|
	mr.mr_base = rs->phys_base;
 | 
						|
	mr.mr_limit = rs->phys_base + 0x100;
 | 
						|
	if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) {
 | 
						|
		panic("Unable to request access to UART memory");
 | 
						|
	}
 | 
						|
	rs->phys_base = (vir_bytes) vm_map_phys(SELF,
 | 
						|
					(void *) this_omap3.base_addr, 0x100);
 | 
						|
	
 | 
						|
	if (rs->phys_base ==  (vir_bytes) MAP_FAILED) {
 | 
						|
		panic("Unable to request access to UART memory");
 | 
						|
	}
 | 
						|
	rs->reg_offset = 2;
 | 
						|
 | 
						|
	rs->uartclk = UART_FREQ;
 | 
						|
	rs->ohead = rs->otail = rs->obuf;
 | 
						|
 | 
						|
	/* Override system default baud rate. We do this because u-boot
 | 
						|
	 * configures the UART for a baud rate of 115200 b/s and the kernel
 | 
						|
	 * directly sends data over serial out upon boot up. If we then
 | 
						|
	 * suddenly change the settings, the output will be garbled during
 | 
						|
	 * booting.
 | 
						|
	 */
 | 
						|
	tp->tty_termios.c_ospeed = DFLT_BAUD;
 | 
						|
 | 
						|
	/* Configure IRQ */
 | 
						|
	rs->irq = this_omap3.irq;
 | 
						|
 | 
						|
	/* callback with irq line number + 1 because using line number 0 
 | 
						|
	   fails eslewhere */
 | 
						|
	rs->irq_hook_kernel_id = rs->irq_hook_id = line + 1;	
 | 
						|
 | 
						|
	/* sys_irqsetpolicy modifies irq_hook_kernel_id. this modified id
 | 
						|
	 * needs to be used in sys_irqenable and similar calls.
 | 
						|
	 */
 | 
						|
	if (sys_irqsetpolicy(rs->irq, 0, &rs->irq_hook_kernel_id) != OK) {
 | 
						|
		printf("RS232: Couldn't obtain hook for irq %d\n", rs->irq);
 | 
						|
	} else {
 | 
						|
		if (sys_irqenable(&rs->irq_hook_kernel_id) != OK)  {
 | 
						|
			printf("RS232: Couldn't enable irq %d (hooked)\n",
 | 
						|
				rs->irq);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* When we get called back we get called back using the original 
 | 
						|
	 * hook_id bit set. e.g. if we register with hook_id 5 the callback
 | 
						|
	 * calls us with the 5 th bit set */
 | 
						|
	rs_irq_set |= (1 << (rs->irq_hook_id ));
 | 
						|
 | 
						|
	/* Enable interrupts */
 | 
						|
	rs_reset(rs);
 | 
						|
	rs->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_MSI;
 | 
						|
	rs_config(rs);
 | 
						|
 | 
						|
	/* Fill in TTY function hooks. */
 | 
						|
	tp->tty_devread = rs_read;
 | 
						|
	tp->tty_devwrite = rs_write;
 | 
						|
	tp->tty_echo = rs_echo;
 | 
						|
	tp->tty_icancel = rs_icancel;
 | 
						|
	tp->tty_ocancel = rs_ocancel;
 | 
						|
	tp->tty_ioctl = rs_ioctl;
 | 
						|
	tp->tty_break = rs_break;
 | 
						|
	tp->tty_open = rs_open;
 | 
						|
	tp->tty_close = rs_close;
 | 
						|
 | 
						|
	/* Tell external device we are ready. */
 | 
						|
	istart(rs);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rs_interrupt(message *m)
 | 
						|
{
 | 
						|
	unsigned long irq_set;
 | 
						|
	int line;
 | 
						|
	rs232_t *rs;
 | 
						|
 | 
						|
	irq_set = m->NOTIFY_ARG;
 | 
						|
	for (line = 0, rs = rs_lines; line < NR_RS_LINES; line++, rs++) {
 | 
						|
		if (irq_set & (1 << rs->irq_hook_id)) {
 | 
						|
			rs232_handler(rs);
 | 
						|
			if (sys_irqenable(&rs->irq_hook_kernel_id) != OK)
 | 
						|
				panic("unable to enable interrupts");
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
rs_icancel(tty_t *tp, int UNUSED(dummy))
 | 
						|
{
 | 
						|
/* Cancel waiting input. */
 | 
						|
	rs232_t *rs = tp->tty_priv;
 | 
						|
 | 
						|
	rs->icount = 0;
 | 
						|
	rs->itail = rs->ihead;
 | 
						|
	istart(rs);
 | 
						|
	return 0;	/* dummy */
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
rs_ocancel(tty_t *tp, int UNUSED(dummy))
 | 
						|
{
 | 
						|
/* Cancel pending output. */
 | 
						|
	rs232_t *rs = tp->tty_priv;
 | 
						|
 | 
						|
	rs->ostate &= ~(ODONE | OQUEUED);
 | 
						|
	rs->ocount = 0;
 | 
						|
	rs->otail = rs->ohead;
 | 
						|
 | 
						|
	return 0;	/* dummy */
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
rs_read(tty_t *tp, int try)
 | 
						|
{
 | 
						|
/* Process characters from the circular input buffer. */
 | 
						|
 | 
						|
	rs232_t *rs = tp->tty_priv;
 | 
						|
	int icount, count, ostate;
 | 
						|
 | 
						|
	if (!(tp->tty_termios.c_cflag & CLOCAL)) {
 | 
						|
		if (try) return 1;
 | 
						|
 | 
						|
		/* Send a SIGHUP if hangup detected. */
 | 
						|
		ostate = rs->ostate;
 | 
						|
		rs->ostate &= ~ODEVHUP;		/* save ostate, clear DEVHUP */
 | 
						|
		if (ostate & ODEVHUP) {
 | 
						|
			sigchar(tp, SIGHUP, 1);
 | 
						|
			tp->tty_termios.c_ospeed = B0;/* Disable further I/O.*/
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (try) {
 | 
						|
		return(rs->icount > 0);
 | 
						|
	}
 | 
						|
 | 
						|
	while ((count = rs->icount) > 0) {
 | 
						|
		icount = bufend(rs->ibuf) - rs->itail;
 | 
						|
		if (count > icount) count = icount;
 | 
						|
 | 
						|
		/* Perform input processing on (part of) the input buffer. */
 | 
						|
		if ((count = in_process(tp, rs->itail, count, -1)) == 0) break;
 | 
						|
		rs->icount -= count;
 | 
						|
		if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
 | 
						|
		if ((rs->itail += count) == bufend(rs->ibuf))
 | 
						|
			rs->itail = rs->ibuf;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
rs_ostart(rs232_t *rs)
 | 
						|
{
 | 
						|
/* Tell RS232 there is something waiting in the output buffer. */
 | 
						|
 | 
						|
	rs->ostate |= OQUEUED;
 | 
						|
	if (txready(rs)) write_chars(rs);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
rs_break(tty_t *tp, int UNUSED(dummy))
 | 
						|
{
 | 
						|
/* Generate a break condition by setting the BREAK bit for 0.4 sec. */
 | 
						|
	rs232_t *rs = tp->tty_priv;
 | 
						|
	unsigned int lsr;
 | 
						|
 | 
						|
	lsr = serial_in(rs, OMAP3_LSR);
 | 
						|
	serial_out(rs, OMAP3_LSR, lsr | UART_LSR_BI);
 | 
						|
	/* XXX */
 | 
						|
	/* milli_delay(400); */				/* ouch */
 | 
						|
  	serial_out(rs, OMAP3_LSR, lsr);
 | 
						|
	return 0;	/* dummy */
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
rs_open(tty_t *tp, int UNUSED(dummy))
 | 
						|
{
 | 
						|
	/* Set the speed to 115200 by default */
 | 
						|
	tp->tty_termios.c_ospeed = DFLT_BAUD;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
rs_close(tty_t *tp, int UNUSED(dummy))
 | 
						|
{
 | 
						|
/* The line is closed; optionally hang up. */
 | 
						|
	rs232_t *rs = tp->tty_priv;
 | 
						|
 | 
						|
	if (tp->tty_termios.c_cflag & HUPCL) {
 | 
						|
		serial_out(rs, OMAP3_MCR, UART_MCR_OUT2|UART_MCR_RTS);
 | 
						|
		if (rs->ier & UART_IER_THRI) {
 | 
						|
			rs->ier &= ~UART_IER_THRI;
 | 
						|
			serial_out(rs, OMAP3_IER, rs->ier);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return 0;	/* dummy */
 | 
						|
}
 | 
						|
 | 
						|
/* Low level (interrupt) routines. */
 | 
						|
 | 
						|
static void
 | 
						|
rs232_handler(struct rs232 *rs)
 | 
						|
{
 | 
						|
/* Handle interrupt of a UART port */
 | 
						|
	unsigned int iir, lsr;
 | 
						|
 | 
						|
	iir = serial_in(rs, OMAP3_IIR);
 | 
						|
	if (iir & UART_IIR_NO_INT)	/* No interrupt */
 | 
						|
		return;
 | 
						|
 | 
						|
	lsr = serial_in(rs, OMAP3_LSR);
 | 
						|
	if (iir & UART_IIR_RDI) {	/* Data ready interrupt */
 | 
						|
		if (lsr & UART_LSR_DR)  {
 | 
						|
			read_chars(rs, lsr);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	check_modem_status(rs);
 | 
						|
	if (iir & UART_IIR_THRI) {
 | 
						|
		if (lsr & UART_LSR_THRE) {
 | 
						|
			/* Ready to send and space available */
 | 
						|
			write_chars(rs);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
read_chars(rs232_t *rs, unsigned int status)
 | 
						|
{
 | 
						|
	unsigned char c;
 | 
						|
 | 
						|
	if(serial_in(rs,OMAP3_LSR) & OMAP3_LSR_RXOE) {
 | 
						|
		rs->rx_overrun_events++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* check the line status to know if there are more chars */
 | 
						|
	while (serial_in(rs, OMAP3_LSR) &  UART_LSR_DR) {
 | 
						|
		c = serial_in(rs, OMAP3_RHR);
 | 
						|
		if (!(rs->ostate & ORAW)) {
 | 
						|
			if (c == rs->oxoff) {
 | 
						|
				rs->ostate &= ~OSWREADY;
 | 
						|
			} else if (!(rs->ostate & OSWREADY)) {
 | 
						|
				rs->ostate = OSWREADY;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (rs->icount == buflen(rs->ibuf)) {
 | 
						|
			/* no buffer space? keep reading */
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (++rs->icount == RS_IHIGHWATER && rs->idevready) {
 | 
						|
			 istop(rs);
 | 
						|
		}
 | 
						|
 | 
						|
		*rs->ihead = c;
 | 
						|
		if (++rs->ihead == bufend(rs->ibuf)) {
 | 
						|
			rs->ihead = rs->ibuf;
 | 
						|
		}
 | 
						|
 | 
						|
		if (rs->icount == 1) {
 | 
						|
			rs->tty->tty_events = 1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
write_chars(rs232_t *rs)
 | 
						|
{
 | 
						|
/* If there is output to do and everything is ready, do it (local device is
 | 
						|
 * known ready).
 | 
						|
 * Notify TTY when the buffer goes empty.
 | 
						|
 */
 | 
						|
 | 
						|
	if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
 | 
						|
		/* Bit test allows ORAW and requires the others. */
 | 
						|
		serial_out(rs, OMAP3_THR, *rs->otail);
 | 
						|
		if (++rs->otail == bufend(rs->obuf))
 | 
						|
			rs->otail = rs->obuf;
 | 
						|
		if (--rs->ocount == 0) {
 | 
						|
			/* Turn on ODONE flag, turn off OQUEUED */
 | 
						|
			rs->ostate ^= (ODONE | OQUEUED); 
 | 
						|
			rs->tty->tty_events = 1;
 | 
						|
			if (rs->ier & UART_IER_THRI) {
 | 
						|
				rs->ier &= ~UART_IER_THRI;
 | 
						|
				serial_out(rs, OMAP3_IER, rs->ier);
 | 
						|
			}
 | 
						|
		} else  {
 | 
						|
			if (rs->icount == RS_OLOWWATER)
 | 
						|
				rs->tty->tty_events = 1;
 | 
						|
			if (!(rs->ier & UART_IER_THRI)) {
 | 
						|
				rs->ier |= UART_IER_THRI;
 | 
						|
				serial_out(rs, OMAP3_IER, rs->ier);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int
 | 
						|
check_modem_status(rs232_t *rs)
 | 
						|
{
 | 
						|
/* Check modem status */
 | 
						|
 | 
						|
	unsigned int msr;
 | 
						|
 | 
						|
	msr = serial_in(rs, OMAP3_MSR); /* Resets modem interrupt */
 | 
						|
	if ((msr & (UART_MSR_DCD|UART_MSR_DDCD)) == UART_MSR_DDCD) {
 | 
						|
		rs->ostate |= ODEVHUP;
 | 
						|
		rs->tty->tty_events = 1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!devready(rs))
 | 
						|
		rs->ostate &= ~ODEVREADY;
 | 
						|
	else
 | 
						|
		rs->ostate |= ODEVREADY;
 | 
						|
 | 
						|
	return msr;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* NR_RS_LINES > 0 */
 | 
						|
 |