diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-05-03 21:34:20 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-05-03 21:34:20 +0200 |
commit | 3ca395a36c3c1b098433c7a47106e76f00aee324 (patch) | |
tree | 500d4a2849669dfb2b8c744dd7fa665268fee83f | |
parent | 4032db00a7a813e8f36b2755204b6863b4545906 (diff) |
uartpl011: dont touch line control while uart is enabled
-rw-r--r-- | sys/src/9/bcm/uartpl011.c | 108 |
1 files changed, 81 insertions, 27 deletions
diff --git a/sys/src/9/bcm/uartpl011.c b/sys/src/9/bcm/uartpl011.c index fadb57289..a27ec182f 100644 --- a/sys/src/9/bcm/uartpl011.c +++ b/sys/src/9/bcm/uartpl011.c @@ -82,35 +82,90 @@ interrupt(Ureg*, void *arg) u32int *reg = (u32int*)uart->regs; coherence(); - if((reg[FR] & TXFE) == 0) - uartkick(uart); while((reg[FR] & RXFE) == 0) uartrecv(uart, reg[DR] & 0xFF); + if((reg[FR] & TXFF) == 0) + uartkick(uart); + reg[ICR] = 1<<5 | 1<<6 | 1<<7 | 1<<8 | 1<<9 | 1<<10; + coherence(); +} + +static void +disable(Uart *uart) +{ + u32int *reg = (u32int*)uart->regs; + + /* disable interrupt */ + reg[IMSC] = 0; + coherence(); + + /* clear interrupt */ + reg[ICR] = 1<<5 | 1<<6 | 1<<7 | 1<<8 | 1<<9 | 1<<10; coherence(); + /* wait for last transmission to complete */ + while((reg[FR] & BUSY) != 0) + delay(1); + + /* disable uart */ + reg[CR] = 0; + coherence(); + + /* flush rx fifo */ + reg[LCRH] &= ~FEN; + coherence(); } static void -enable(Uart *uart, int ie) +uartoff(Uart *uart) { u32int *reg = (u32int*)uart->regs; + u32int im; + im = reg[IMSC]; + disable(uart); + reg[IMSC] = im; +} + +static void +uarton(Uart *uart) +{ + u32int *reg = (u32int*)uart->regs; + + /* enable fifo */ + reg[LCRH] |= FEN; + coherence(); + + /* enable uart */ reg[CR] = UARTEN | RXE | TXE; + coherence(); +} + +static void +enable(Uart *uart, int ie) +{ + u32int *reg = (u32int*)uart->regs; + + disable(uart); if(ie){ intrenable(IRQuart, interrupt, uart, 0, uart->name); reg[IMSC] = TXIM|RXIM; - } else { - reg[IMSC] = 0; } + uarton(uart); } static void -disable(Uart *uart) +linectl(Uart *uart, u32int set, u32int clr) { u32int *reg = (u32int*)uart->regs; - reg[IMSC] = 0; - reg[CR] = 0; + if(uart->enabled) + uartoff(uart); + + reg[LCRH] = set | (reg[LCRH] & ~clr); + + if(uart->enabled) + uarton(uart); } static void @@ -132,11 +187,9 @@ kick(Uart *uart) static void dobreak(Uart *uart, int ms) { - u32int *reg = (u32int*)uart->regs; - - reg[LCRH] |= BRK; + linectl(uart, BRK, 0); delay(ms); - reg[LCRH] &= ~BRK; + linectl(uart, 0, BRK); } static int @@ -147,8 +200,15 @@ baud(Uart *uart, int n) if(uart->freq <= 0 || n <= 0) return -1; + if(uart->enabled) + uartoff(uart); + reg[IBRD] = (uart->freq >> 4) / n; reg[FBRD] = (uart->freq >> 4) % n; + + if(uart->enabled) + uarton(uart); + uart->baud = n; return 0; } @@ -156,20 +216,18 @@ baud(Uart *uart, int n) static int bits(Uart *uart, int n) { - u32int *reg = (u32int*)uart->regs; - switch(n){ case 8: - reg[LCRH] = (reg[LCRH] & ~WLENM) | WLEN8; + linectl(uart, WLEN8, WLENM); break; case 7: - reg[LCRH] = (reg[LCRH] & ~WLENM) | WLEN7; + linectl(uart, WLEN7, WLENM); break; case 6: - reg[LCRH] = (reg[LCRH] & ~WLENM) | WLEN6; + linectl(uart, WLEN6, WLENM); break; case 5: - reg[LCRH] = (reg[LCRH] & ~WLENM) | WLEN5; + linectl(uart, WLEN5, WLENM); break; default: return -1; @@ -181,14 +239,12 @@ bits(Uart *uart, int n) static int stop(Uart *uart, int n) { - u32int *reg = (u32int*)uart->regs; - switch(n){ case 1: - reg[LCRH] &= ~STP2; + linectl(uart, 0, STP2); break; case 2: - reg[LCRH] |= STP2; + linectl(uart, STP2, 0); break; default: return -1; @@ -200,17 +256,15 @@ stop(Uart *uart, int n) static int parity(Uart *uart, int n) { - u32int *reg = (u32int*)uart->regs; - switch(n){ case 'n': - reg[LCRH] &= ~PEN; + linectl(uart, 0, PEN); break; case 'e': - reg[LCRH] |= EPS | PEN; + linectl(uart, EPS|PEN, 0); break; case 'o': - reg[LCRH] = (reg[LCRH] & ~EPS) | PEN; + linectl(uart, PEN, EPS); break; default: return -1; |