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:
parent
fe34e52d19
commit
6a3a3d69c6
4 changed files with 337 additions and 35 deletions
|
@ -173,6 +173,34 @@ archinit(void)
|
||||||
addarchfile("cputemp", 0444, cputempread, nil);
|
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
|
void
|
||||||
okay(int on)
|
okay(int on)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,7 @@ enum {
|
||||||
IRQi2c = 53,
|
IRQi2c = 53,
|
||||||
IRQspi = 54,
|
IRQspi = 54,
|
||||||
IRQsdhost = 56,
|
IRQsdhost = 56,
|
||||||
|
IRQuart = 57,
|
||||||
IRQmmc = 62,
|
IRQmmc = 62,
|
||||||
|
|
||||||
IRQbasic = 64,
|
IRQbasic = 64,
|
||||||
|
|
|
@ -47,7 +47,7 @@ extern PhysUart miniphysuart;
|
||||||
|
|
||||||
static Uart miniuart = {
|
static Uart miniuart = {
|
||||||
.regs = (u32int*)AUXREGS,
|
.regs = (u32int*)AUXREGS,
|
||||||
.name = "uart0",
|
.name = "uart1",
|
||||||
.freq = 250000000,
|
.freq = 250000000,
|
||||||
.baud = 115200,
|
.baud = 115200,
|
||||||
.phys = &miniphysuart,
|
.phys = &miniphysuart,
|
||||||
|
@ -100,7 +100,7 @@ enable(Uart *uart, int ie)
|
||||||
ap[MuCntl] = TxEn|RxEn;
|
ap[MuCntl] = TxEn|RxEn;
|
||||||
baud(uart, uart->baud);
|
baud(uart, uart->baud);
|
||||||
if(ie){
|
if(ie){
|
||||||
intrenable(IRQaux, interrupt, uart, 0, "uart");
|
intrenable(IRQaux, interrupt, uart, 0, uart->name);
|
||||||
ap[MuIer] = RxIen|TxIen;
|
ap[MuIer] = RxIen|TxIen;
|
||||||
}else
|
}else
|
||||||
ap[MuIer] = 0;
|
ap[MuIer] = 0;
|
||||||
|
@ -259,7 +259,7 @@ donothing(Uart*, int)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
putc(Uart*, int c)
|
putc(Uart*, int c)
|
||||||
{
|
{
|
||||||
u32int *ap;
|
u32int *ap;
|
||||||
|
@ -272,7 +272,7 @@ putc(Uart*, int c)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
getc(Uart*)
|
getc(Uart*)
|
||||||
{
|
{
|
||||||
u32int *ap;
|
u32int *ap;
|
||||||
|
@ -283,38 +283,8 @@ getc(Uart*)
|
||||||
return ap[MuIo] & 0xFF;
|
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 = {
|
PhysUart miniphysuart = {
|
||||||
.name = "miniuart",
|
.name = "mini",
|
||||||
.pnp = pnp,
|
.pnp = pnp,
|
||||||
.enable = enable,
|
.enable = enable,
|
||||||
.disable = disable,
|
.disable = disable,
|
||||||
|
|
303
sys/src/9/bcm/uartpl011.c
Normal file
303
sys/src/9/bcm/uartpl011.c
Normal 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,
|
||||||
|
};
|
Loading…
Reference in a new issue