#ifndef __PL011__UART__H__ #define __PL011__UART__H__ /* * PL011 UART Generic driver implementation. * Copyright Bahadir Balban (C) 2009 * * 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. */ #if defined(VARIANT_USERSPACE) /* FIXME: Take this value in agreement from kernel, or from kernel only */ #define PL011_BASE 0x500000 #endif #if defined(VARIANT_BAREMETAL) #if defined(PLATFORM_PB926) #define PL011_BASE 0x101F1000 #elif defined(PLATFORM_EB) #define PL011_BASE 0x10009000 #elif defined(PLATFORM_PB11MPCORE) #define PL011_BASE 0x10009000 #endif #endif /* 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 0x00 #define PL011_UARTRSR 0x04 #define PL011_UARTECR 0x04 #define PL011_UARTFR 0x18 #define PL011_UARTILPR 0x20 #define PL011_UARTIBRD 0x24 #define PL011_UARTFBRD 0x28 #define PL011_UARTLCR_H 0x2C #define PL011_UARTCR 0x30 #define PL011_UARTIFLS 0x34 #define PL011_UARTIMSC 0x38 #define PL011_UARTRIS 0x3C #define PL011_UARTMIS 0x40 #define PL011_UARTICR 0x44 #define PL011_UARTDMACR 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 { unsigned long base; unsigned int frame_errors; unsigned int parity_errors; unsigned int break_errors; unsigned int overrun_errors; unsigned int rx_timeout_errors; }; int pl011_tx_char(unsigned int base, char c); int pl011_rx_char(unsigned int base, char *c); void pl011_set_baudrate(unsigned int base, unsigned int baud, unsigned int clkrate); void pl011_set_irq_mask(unsigned int base, unsigned int flags); void pl011_clr_irq_mask(unsigned int base, unsigned int flags); void pl011_irq_handler(struct pl011_uart *); void pl011_tx_irq_handler(struct pl011_uart *uart, unsigned int); void pl011_rx_irq_handler(struct pl011_uart *uart, unsigned int); void pl011_error_irq_handler(struct pl011_uart *uart, unsigned int); static inline void pl011_uart_enable(unsigned int base); static inline void pl011_uart_disable(unsigned int base); static inline void pl011_tx_enable(unsigned int base); static inline void pl011_tx_disable(unsigned int base); static inline void pl011_rx_enable(unsigned int base); static inline void pl011_rx_disable(unsigned int base); static inline void pl011_irq_clear(unsigned int base, unsigned int flags); static inline unsigned int pl011_read_irqstat(unsigned int base); static inline unsigned int pl011_read_irqmask(unsigned int base); static inline void pl011_rx_dma_disable(unsigned int base); static inline void pl011_rx_dma_enable(unsigned int base); static inline void pl011_tx_dma_enable(unsigned int base); static inline void pl011_tx_dma_disable(unsigned int base); static inline void pl011_set_irq_fifolevel(unsigned int base, unsigned int xfer, unsigned int level); static inline void pl011_set_word_width(unsigned int base, int size); static inline void pl011_disable_fifos(unsigned int base); static inline void pl011_set_parity_even(unsigned int base); static inline void pl011_parity_enable(unsigned int base); static inline void pl011_set_stopbits(unsigned int base, int stopbits); static inline void pl011_set_parity_odd(unsigned int base); static inline void pl011_enable_fifos(unsigned int base); static inline void pl011_parity_disable(unsigned int base); #define PL011_UARTEN (1 << 0) static inline void pl011_uart_enable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTCR)); val |= PL011_UARTEN; write(val, (base + PL011_UARTCR)); return; } static inline void pl011_uart_disable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTCR)); val &= ~PL011_UARTEN; write(val, (base + PL011_UARTCR)); return; } #define PL011_TXE (1 << 8) static inline void pl011_tx_enable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTCR)); val |= PL011_TXE; write(val, (base + PL011_UARTCR)); return; } static inline void pl011_tx_disable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTCR)); val &= ~PL011_TXE; write(val, (base + PL011_UARTCR)); return; } #define PL011_RXE (1 << 9) static inline void pl011_rx_enable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTCR)); val |= PL011_RXE; write(val, (base + PL011_UARTCR)); return; } static inline void pl011_rx_disable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTCR)); val &= ~PL011_RXE; write(val, (base + PL011_UARTCR)); return; } #define PL011_TWO_STOPBITS_SELECT (1 << 3) static inline void pl011_set_stopbits(unsigned int base, int stopbits) { unsigned int val = 0; read(val, (base + 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, (base + PL011_UARTLCR_H)); return; } #define PL011_PARITY_ENABLE (1 << 1) static inline void pl011_parity_enable(unsigned int base) { unsigned int val = 0; read(val, (base +PL011_UARTLCR_H)); val |= PL011_PARITY_ENABLE; write(val, (base + PL011_UARTLCR_H)); return; } static inline void pl011_parity_disable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTLCR_H)); val &= ~PL011_PARITY_ENABLE; write(val, (base + PL011_UARTLCR_H)); return; } #define PL011_PARITY_EVEN (1 << 2) static inline void pl011_set_parity_even(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTLCR_H)); val |= PL011_PARITY_EVEN; write(val, (base + PL011_UARTLCR_H)); return; } static inline void pl011_set_parity_odd(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTLCR_H)); val &= ~PL011_PARITY_EVEN; write(val, (base + PL011_UARTLCR_H)); return; } #define PL011_ENABLE_FIFOS (1 << 4) static inline void pl011_enable_fifos(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTLCR_H)); val |= PL011_ENABLE_FIFOS; write(val, (base + PL011_UARTLCR_H)); return; } static inline void pl011_disable_fifos(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTLCR_H)); val &= ~PL011_ENABLE_FIFOS; write(val, (base + 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(unsigned int base, int size) { unsigned int val = 0; if(size < 5 || size > 8) /* Default is 8 */ size = 8; /* Clear size field */ read(val, (base + PL011_UARTLCR_H)); val &= ~(0x3 << PL011_WORD_WIDTH_SHIFT); write(val, (base + 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, (base + PL011_UARTLCR_H)); val |= (size - 5) << PL011_WORD_WIDTH_SHIFT; write(val, (base + 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 base, \ unsigned int xfer, unsigned int level) { if(xfer != 1 && xfer != 0) /* Invalid fifo */ return; if(level > 4) /* Invalid level */ return; write(level << (xfer * 3), (base + PL011_UARTIFLS)); return; } /* returns which irqs are masked */ static inline unsigned int pl011_read_irqmask(unsigned int base) { unsigned int flags; read(flags, (base + PL011_UARTIMSC)); return flags; } /* returns masked irq status */ static inline unsigned int pl011_read_irqstat(unsigned int base) { unsigned int irqstatus; read(irqstatus, (base + PL011_UARTMIS)); return irqstatus; } /* Clears the given asserted irqs */ static inline void pl011_irq_clear(unsigned int base, unsigned int flags) { if(flags > 0x3FF) { /* Invalid irq clearing bitvector */ return; } /* Simply write the flags since it's a write-only register */ write(flags, (base + 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 base) { unsigned int val = 0; read(val, (base + PL011_UARTDMACR)); val |= PL011_TXDMAEN; write(val, (base + PL011_UARTDMACR)); return; } /* Disables dma transfers for uart */ static inline void pl011_tx_dma_disable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTDMACR)); val &= ~PL011_TXDMAEN; write(val, (base + PL011_UARTDMACR)); return; } static inline void pl011_rx_dma_enable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTDMACR)); val |= PL011_RXDMAEN; write(val, (base + PL011_UARTDMACR)); return; } static inline void pl011_rx_dma_disable(unsigned int base) { unsigned int val = 0; read(val, (base + PL011_UARTDMACR)); val &= ~PL011_RXDMAEN; write(val, (base +PL011_UARTDMACR)); return; } int pl011_initialise(struct pl011_uart *uart); #endif /* __PL011__UART__ */