
#ifndef __PL011__UART__H__
#define __PL011__UART__H__

/* 
 * PL011 UART Generic driver implementation.
 * Copyright Bahadir Balban (C) 2006
 *
 * The particular intention of this code is that it has been carefully 
 * written as decoupled from os-specific code and in a verbose way such
 * that it clearly demonstrates how the device operates, reducing the 
 * amount of time to be spent for understanding the operational model 
 * and implementing a driver from scratch. This is the very first to be
 * such a driver so far, hopefully it will turn out to be useful.
 */

/* Default base address for this chip */
#define PL011_DEFAULT_PHYSICAL_BASE		0x101F1000
#define PL011_BASE		PL011_DEFAULT_PHYSICAL_BASE

/* Architecture specific memory access macros */
#define read(val, address)	val = *((volatile unsigned int *) address)
#define write(val, address)	*((volatile unsigned int *) address) = val

/* Register offsets */
#define PL011_UARTDR		(PL011_BASE + 0x00)
#define PL011_UARTRSR		(PL011_BASE + 0x04)
#define PL011_UARTECR		(PL011_BASE + 0x04)
#define PL011_UARTFR		(PL011_BASE + 0x18)
#define PL011_UARTILPR		(PL011_BASE + 0x20)
#define PL011_UARTIBRD		(PL011_BASE + 0x24)
#define PL011_UARTFBRD		(PL011_BASE + 0x28)
#define PL011_UARTLCR_H		(PL011_BASE + 0x2C)
#define PL011_UARTCR		(PL011_BASE + 0x30)
#define PL011_UARTIFLS		(PL011_BASE + 0x34)
#define PL011_UARTIMSC		(PL011_BASE + 0x38)
#define PL011_UARTRIS		(PL011_BASE + 0x3C)
#define PL011_UARTMIS		(PL011_BASE + 0x40)
#define PL011_UARTICR		(PL011_BASE + 0x44)
#define PL011_UARTDMACR		(PL011_BASE + 0x48)

/* IRQ bits for each uart irq event */
#define PL011_RXIRQ		(1 << 4)
#define PL011_TXIRQ		(1 << 5)
#define PL011_RXTIMEOUTIRQ	(1 << 6)
#define PL011_FEIRQ		(1 << 7)
#define PL011_PEIRQ		(1 << 8)
#define PL011_BEIRQ		(1 << 9)
#define PL011_OEIRQ		(1 << 10)

struct pl011_uart;

int pl011_initialise(struct pl011_uart *);
int pl011_tx_char(char);
int pl011_rx_char(char *);

void pl011_set_baudrate(unsigned int, unsigned int);
void pl011_set_irq_mask(unsigned int);
void pl011_clr_irq_mask(unsigned int);
	
void pl011_irq_handler(struct pl011_uart *);
void pl011_tx_irq_handler(struct pl011_uart *, unsigned int);	
void pl011_rx_irq_handler(struct pl011_uart *, unsigned int);
void pl011_error_irq_handler(struct pl011_uart *, unsigned int);

static inline void pl011_uart_enable(void);
static inline void pl011_uart_disable(void);
static inline void pl011_tx_enable(void);
static inline void pl011_tx_disable(void);
static inline void pl011_rx_enable(void);
static inline void pl011_rx_disable(void);
static inline void pl011_irq_clear(unsigned int flags);
static inline unsigned int pl011_read_irqstat(void);
static inline unsigned int pl011_read_irqmask(void);
static inline void pl011_rx_dma_disable(void);
static inline void pl011_rx_dma_enable(void);
static inline void pl011_tx_dma_enable(void);
static inline void pl011_tx_dma_disable(void);
static inline void pl011_set_irq_fifolevel(unsigned int xfer, 
					   unsigned int level);
static inline void pl011_set_word_width(int size);
static inline void pl011_disable_fifos(void);
static inline void pl011_set_parity_even(void);
static inline void pl011_parity_enable(void);
static inline void pl011_set_stopbits(int stopbits);

static inline void pl011_set_parity_odd(void);
static inline void pl011_enable_fifos(void);
static inline void pl011_parity_disable(void);

struct pl011_uart_ops {
	int  (*initialise)(struct pl011_uart *);
	
	int  (*tx_char)(char);
	int  (*rx_char)(char *);
	
	void (*set_baudrate)(unsigned int, unsigned int);
	void (*set_irq_mask)(unsigned int);
	void (*clr_irq_mask)(unsigned int);
	
	void (*irq_handler)(struct pl011_uart *);
	void (*tx_irq_handler)(struct pl011_uart *, unsigned int);	
	void (*rx_irq_handler)(struct pl011_uart *, unsigned int);
	void (*error_irq_handler)(struct pl011_uart *, unsigned int);
};

struct pl011_uart {
	const unsigned int base;
	struct pl011_uart_ops ops;
	unsigned int frame_errors;
	unsigned int parity_errors;
	unsigned int break_errors;
	unsigned int overrun_errors;
	unsigned int rx_timeout_errors;
}; 



#define PL011_UARTEN	(1 << 0)
static inline void pl011_uart_enable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTCR);
	val |= PL011_UARTEN;
	write(val, PL011_UARTCR);
		
	return; 
}

static inline void pl011_uart_disable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTCR);
	val &= ~PL011_UARTEN;
	write(val, PL011_UARTCR);
	
	return;
}

#define PL011_TXE	(1 << 8)
static inline void pl011_tx_enable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTCR);
	val |= PL011_TXE;
	write(val, PL011_UARTCR);
	return;
}

static inline void pl011_tx_disable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTCR);
	val &= ~PL011_TXE;
	write(val, PL011_UARTCR);
	return;
}


#define PL011_RXE	(1 << 9)
static inline void pl011_rx_enable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTCR);
	val |= PL011_RXE;
	write(val, PL011_UARTCR);
	return;
}

static inline void pl011_rx_disable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTCR);
	val &= ~PL011_RXE;
	write(val, PL011_UARTCR);
	return;
}

#define PL011_TWO_STOPBITS_SELECT	(1 << 3)
static inline void pl011_set_stopbits(int stopbits)
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTLCR_H);
	
	if(stopbits == 2) {			/* Set to two bits */
		val |= PL011_TWO_STOPBITS_SELECT;
	} else {				/* Default is 1 */
		val &= ~PL011_TWO_STOPBITS_SELECT;
	}
	write(val, PL011_UARTLCR_H);
	return;		
}

#define PL011_PARITY_ENABLE	(1 << 1)
static inline void pl011_parity_enable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTLCR_H);
	val |= PL011_PARITY_ENABLE;
	write(val, PL011_UARTLCR_H);
	return;
}

static inline void pl011_parity_disable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTLCR_H);
	val &= ~PL011_PARITY_ENABLE;
	write(val, PL011_UARTLCR_H);
	return;
}

#define PL011_PARITY_EVEN	(1 << 2)
static inline void pl011_set_parity_even()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTLCR_H);
	val |= PL011_PARITY_EVEN;
	write(val, PL011_UARTLCR_H);
	return;
}

static inline void pl011_set_parity_odd()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTLCR_H);
	val &= ~PL011_PARITY_EVEN;
	write(val, PL011_UARTLCR_H);
	return;
}

#define	PL011_ENABLE_FIFOS	(1 << 4)
static inline void pl011_enable_fifos()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTLCR_H);
	val |= PL011_ENABLE_FIFOS;
	write(val, PL011_UARTLCR_H);
	return;
}

static inline void pl011_disable_fifos()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTLCR_H);
	val &= ~PL011_ENABLE_FIFOS;
	write(val, PL011_UARTLCR_H);
	return;
}


#define PL011_WORD_WIDTH_SHIFT	(5)
/* Sets the transfer word width for the data register. */
static inline void pl011_set_word_width(int size)
{
	unsigned int val;
	val = 0;
	
	if(size < 5 || size > 8)	/* Default is 8 */
		size = 8;
	
	/* Clear size field */
	read(val, PL011_UARTLCR_H);
	val &= ~(0x3 << PL011_WORD_WIDTH_SHIFT);
	write(val, PL011_UARTLCR_H);

	/* The formula is to write 5 less of size given:
	 * 11 = 8 bits
	 * 10 = 7 bits
	 * 01 = 6 bits
	 * 00 = 5 bits
	 */
	read(val, PL011_UARTLCR_H);
	val |= (size - 5) << PL011_WORD_WIDTH_SHIFT;
	write(val, PL011_UARTLCR_H);
	
	return;
}

/* 
 * Defines at which level of fifo fullness an irq will be generated.
 * @xfer: tx fifo = 0, rx fifo = 1
 * @level: Generate irq if:
 * 0	rxfifo >= 1/8 full  	txfifo <= 1/8 full
 * 1	rxfifo >= 1/4 full  	txfifo <= 1/4 full
 * 2	rxfifo >= 1/2 full  	txfifo <= 1/2 full
 * 3	rxfifo >= 3/4 full	txfifo <= 3/4 full
 * 4	rxfifo >= 7/8 full	txfifo <= 7/8 full
 * 5-7	reserved		reserved
 */
static inline void pl011_set_irq_fifolevel(unsigned int xfer, unsigned int level)
{
	if(xfer != 1 && xfer != 0)	/* Invalid fifo */
		return;

	if(level > 4)			/* Invalid level */
		return;			
	
	write(level << (xfer * 3), PL011_UARTIFLS);
	return;
}

/* returns which irqs are masked */
static inline unsigned int pl011_read_irqmask(void)
{
	unsigned int flags;
	read(flags, PL011_UARTIMSC);
	return flags;
}

/* returns masked irq status */
static inline unsigned int pl011_read_irqstat(void)
{
	unsigned int irqstatus;
	read(irqstatus, PL011_UARTMIS);
	return irqstatus;	
}
/* Clears the given asserted irqs */
static inline void pl011_irq_clear(unsigned int flags)
{
	if(flags > 0x3FF) {	/* Invalid irq clearing bitvector */
		return;
	}
	/* Simply write the flags since it's a write-only register */
	write(flags, PL011_UARTICR);
	return;
}

#define PL011_TXDMAEN	(1 << 1)
#define PL011_RXDMAEN	(1 << 0)

/* Enables dma transfers for uart. The dma controller
 * must be initialised, set-up and enabled separately. 
 */
static inline void pl011_tx_dma_enable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTDMACR);
	val |= PL011_TXDMAEN;
	write(val, PL011_UARTDMACR);
	return;
}

/* Disables dma transfers for uart */
static inline void pl011_tx_dma_disable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTDMACR);
	val &= ~PL011_TXDMAEN;
	write(val, PL011_UARTDMACR);
	return;
}

static inline void pl011_rx_dma_enable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTDMACR);
	val |= PL011_RXDMAEN;
	write(val, PL011_UARTDMACR);
	return;
}

static inline void pl011_rx_dma_disable()
{
	unsigned int val;
	val = 0;
	
	read(val, PL011_UARTDMACR);
	val &= ~PL011_RXDMAEN;
	write(val, PL011_UARTDMACR);
	return;
}


#endif /* __PL011__UART__ */

