mirror of
https://github.com/drasko/codezero.git
synced 2026-01-12 10:53:16 +01:00
A 16-bit device number or id further distinguishes a device on the system in addition to the device type. This is meant to be used for the very first identification of the device for further probing. Any further info is available by userspace mapping and probing.
382 lines
9.8 KiB
C
382 lines
9.8 KiB
C
|
|
#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
|
|
#elif defined(PLATFORM_PBA8)
|
|
#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 int 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__ */
|
|
|