From 6a3a3d69c67647db5d5176a7cf9ee68e9fce4352 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Thu, 11 Apr 2019 13:21:06 +0200 Subject: [PATCH] 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. --- sys/src/9/bcm/devarch.c | 28 ++++ sys/src/9/bcm/io.h | 1 + sys/src/9/bcm/uartmini.c | 40 +---- sys/src/9/bcm/uartpl011.c | 303 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 337 insertions(+), 35 deletions(-) create mode 100644 sys/src/9/bcm/uartpl011.c diff --git a/sys/src/9/bcm/devarch.c b/sys/src/9/bcm/devarch.c index f799e33c6..9469a4716 100644 --- a/sys/src/9/bcm/devarch.c +++ b/sys/src/9/bcm/devarch.c @@ -173,6 +173,34 @@ archinit(void) addarchfile("cputemp", 0444, cputempread, nil); } +void +uartconsinit(void) +{ + extern PhysUart *physuart[]; + char *p, *cmd; + Uart *uart; + int i, n; + + if((p = getconf("console")) == nil) + return; + i = strtoul(p, &cmd, 0); + if(p == cmd) + return; + /* we only have two possible uarts, the pl011 and aux */ + for(n = 0; physuart[n] != nil; n++) + ; + if(i < 0 || i >= n) + return; + uart = physuart[i]->pnp(); + if(!uart->enabled) + (*uart->phys->enable)(uart, 0); + uartctl(uart, "l8 pn s1"); + if(*cmd != '\0') + uartctl(uart, cmd); + consuart = uart; + uart->console = 1; +} + void okay(int on) { diff --git a/sys/src/9/bcm/io.h b/sys/src/9/bcm/io.h index a077ef69d..c98f3ea25 100644 --- a/sys/src/9/bcm/io.h +++ b/sys/src/9/bcm/io.h @@ -11,6 +11,7 @@ enum { IRQi2c = 53, IRQspi = 54, IRQsdhost = 56, + IRQuart = 57, IRQmmc = 62, IRQbasic = 64, diff --git a/sys/src/9/bcm/uartmini.c b/sys/src/9/bcm/uartmini.c index 480d454fb..9236d2ae9 100644 --- a/sys/src/9/bcm/uartmini.c +++ b/sys/src/9/bcm/uartmini.c @@ -47,7 +47,7 @@ extern PhysUart miniphysuart; static Uart miniuart = { .regs = (u32int*)AUXREGS, - .name = "uart0", + .name = "uart1", .freq = 250000000, .baud = 115200, .phys = &miniphysuart, @@ -100,7 +100,7 @@ enable(Uart *uart, int ie) ap[MuCntl] = TxEn|RxEn; baud(uart, uart->baud); if(ie){ - intrenable(IRQaux, interrupt, uart, 0, "uart"); + intrenable(IRQaux, interrupt, uart, 0, uart->name); ap[MuIer] = RxIen|TxIen; }else ap[MuIer] = 0; @@ -259,7 +259,7 @@ donothing(Uart*, int) { } -void +static void putc(Uart*, int c) { u32int *ap; @@ -272,7 +272,7 @@ putc(Uart*, int c) ; } -int +static int getc(Uart*) { u32int *ap; @@ -283,38 +283,8 @@ getc(Uart*) return ap[MuIo] & 0xFF; } -void -uartconsinit(void) -{ - Uart *uart; - int n; - char *p, *cmd; - - if((p = getconf("console")) == nil) - return; - n = strtoul(p, &cmd, 0); - if(p == cmd) - return; - switch(n){ - default: - return; - case 0: - uart = &miniuart; - break; - } - - if(!uart->enabled) - (*uart->phys->enable)(uart, 0); - uartctl(uart, "l8 pn s1"); - if(*cmd != '\0') - uartctl(uart, cmd); - - consuart = uart; - uart->console = 1; -} - PhysUart miniphysuart = { - .name = "miniuart", + .name = "mini", .pnp = pnp, .enable = enable, .disable = disable, 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, +};