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.
This commit is contained in:
cinap_lenrek 2019-04-11 13:21:06 +02:00
parent fe34e52d19
commit 6a3a3d69c6
4 changed files with 337 additions and 35 deletions

View file

@ -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)
{

View file

@ -11,6 +11,7 @@ enum {
IRQi2c = 53,
IRQspi = 54,
IRQsdhost = 56,
IRQuart = 57,
IRQmmc = 62,
IRQbasic = 64,

View file

@ -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,

303
sys/src/9/bcm/uartpl011.c Normal file
View file

@ -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,
};