diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-04-11 13:21:06 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-04-11 13:21:06 +0200 |
commit | 6a3a3d69c67647db5d5176a7cf9ee68e9fce4352 (patch) | |
tree | 349781e2abd5c79cae9a2d1c94880afd86b319ed /sys/src/9/bcm/uartpl011.c | |
parent | fe34e52d19aeeb04bc34114fda928b6afeb62253 (diff) |
bcm: add pl011 uart driver
the raspi has two uarts, the pl011 and the mini. only one
can be used at a time due to pin muxing. the bcm kernel
uses the mini by default.
Diffstat (limited to 'sys/src/9/bcm/uartpl011.c')
-rw-r--r-- | sys/src/9/bcm/uartpl011.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/sys/src/9/bcm/uartpl011.c b/sys/src/9/bcm/uartpl011.c new file mode 100644 index 000000000..fadb57289 --- /dev/null +++ b/sys/src/9/bcm/uartpl011.c @@ -0,0 +1,303 @@ +/* + * bcm2835 PL011 uart + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum { + DR = 0x00>>2, + RSRECR = 0x04>>2, + FR = 0x18>>2, + TXFE = 1<<7, + RXFF = 1<<6, + TXFF = 1<<5, + RXFE = 1<<4, + BUSY = 1<<3, + + ILPR = 0x20>>2, + IBRD = 0x24>>2, + FBRD = 0x28>>2, + LCRH = 0x2c>>2, + WLENM = 3<<5, + WLEN8 = 3<<5, + WLEN7 = 2<<5, + WLEN6 = 1<<5, + WLEN5 = 0<<5, + FEN = 1<<4, /* fifo enable */ + STP2 = 1<<3, /* 2 stop bits */ + EPS = 1<<2, /* even parity select */ + PEN = 1<<1, /* parity enabled */ + BRK = 1<<0, /* send break */ + + CR = 0x30>>2, + CTSEN = 1<<15, + RTSEN = 1<<14, + RTS = 1<<11, + RXE = 1<<9, + TXE = 1<<8, + LBE = 1<<7, + UARTEN = 1<<0, + + IFLS = 0x34>>2, + IMSC = 0x38>>2, + TXIM = 1<<5, + RXIM = 1<<4, + + RIS = 0x3c>>2, + MIS = 0x40>>2, + ICR = 0x44>>2, + DMACR = 0x48>>2, + ITCR = 0x80>>2, + ITIP = 0x84>>2, + ITOP = 0x88>>2, + TDR = 0x8c>>2, +}; + +extern PhysUart pl011physuart; + +static Uart pl011uart = { + .regs = (u32int*)(VIRTIO+0x201000), + .name = "uart0", + .freq = 250000000, + .baud = 115200, + .phys = &pl011physuart, +}; + +static Uart* +pnp(void) +{ + return &pl011uart; +} + +static void +interrupt(Ureg*, void *arg) +{ + Uart *uart = arg; + u32int *reg = (u32int*)uart->regs; + + coherence(); + if((reg[FR] & TXFE) == 0) + uartkick(uart); + while((reg[FR] & RXFE) == 0) + uartrecv(uart, reg[DR] & 0xFF); + coherence(); + +} + +static void +enable(Uart *uart, int ie) +{ + u32int *reg = (u32int*)uart->regs; + + reg[CR] = UARTEN | RXE | TXE; + if(ie){ + intrenable(IRQuart, interrupt, uart, 0, uart->name); + reg[IMSC] = TXIM|RXIM; + } else { + reg[IMSC] = 0; + } +} + +static void +disable(Uart *uart) +{ + u32int *reg = (u32int*)uart->regs; + + reg[IMSC] = 0; + reg[CR] = 0; +} + +static void +kick(Uart *uart) +{ + u32int *reg = (u32int*)uart->regs; + + if(uart->blocked) + return; + coherence(); + while((reg[FR] & TXFF) == 0){ + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + reg[DR] = *(uart->op++); + } + coherence(); +} + +static void +dobreak(Uart *uart, int ms) +{ + u32int *reg = (u32int*)uart->regs; + + reg[LCRH] |= BRK; + delay(ms); + reg[LCRH] &= ~BRK; +} + +static int +baud(Uart *uart, int n) +{ + u32int *reg = (u32int*)uart->regs; + + if(uart->freq <= 0 || n <= 0) + return -1; + + reg[IBRD] = (uart->freq >> 4) / n; + reg[FBRD] = (uart->freq >> 4) % n; + uart->baud = n; + return 0; +} + +static int +bits(Uart *uart, int n) +{ + u32int *reg = (u32int*)uart->regs; + + switch(n){ + case 8: + reg[LCRH] = (reg[LCRH] & ~WLENM) | WLEN8; + break; + case 7: + reg[LCRH] = (reg[LCRH] & ~WLENM) | WLEN7; + break; + case 6: + reg[LCRH] = (reg[LCRH] & ~WLENM) | WLEN6; + break; + case 5: + reg[LCRH] = (reg[LCRH] & ~WLENM) | WLEN5; + break; + default: + return -1; + } + uart->bits = n; + return 0; +} + +static int +stop(Uart *uart, int n) +{ + u32int *reg = (u32int*)uart->regs; + + switch(n){ + case 1: + reg[LCRH] &= ~STP2; + break; + case 2: + reg[LCRH] |= STP2; + break; + default: + return -1; + } + uart->stop = n; + return 0; +} + +static int +parity(Uart *uart, int n) +{ + u32int *reg = (u32int*)uart->regs; + + switch(n){ + case 'n': + reg[LCRH] &= ~PEN; + break; + case 'e': + reg[LCRH] |= EPS | PEN; + break; + case 'o': + reg[LCRH] = (reg[LCRH] & ~EPS) | PEN; + break; + default: + return -1; + } + uart->parity = n; + return 0; +} + +static void +modemctl(Uart *uart, int on) +{ + uart->modem = on; +} + +static void +rts(Uart*, int) +{ +} + +static long +status(Uart *uart, void *buf, long n, long offset) +{ + char *p; + + p = malloc(READSTR); + if(p == nil) + error(Enomem); + snprint(p, READSTR, + "b%d\n" + "dev(%d) type(%d) framing(%d) overruns(%d) " + "berr(%d) serr(%d)\n", + + uart->baud, + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + uart->berr, + uart->serr + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +donothing(Uart*, int) +{ +} + +static void +putc(Uart *uart, int c) +{ + u32int *reg = (u32int*)uart->regs; + + while((reg[FR] & TXFF) != 0) + ; + reg[DR] = c & 0xFF; +} + +static int +getc(Uart *uart) +{ + u32int *reg = (u32int*)uart->regs; + + while((reg[FR] & RXFE) != 0) + ; + return reg[DR] & 0xFF; +} + +PhysUart pl011physuart = { + .name = "pl011", + .pnp = pnp, + .enable = enable, + .disable = disable, + .kick = kick, + .dobreak = dobreak, + .baud = baud, + .bits = bits, + .stop = stop, + .parity = parity, + .modemctl = donothing, + .rts = rts, + .dtr = donothing, + .status = status, + .fifo = donothing, + .getc = getc, + .putc = putc, +}; |