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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ enum {
|
|||
IRQi2c = 53,
|
||||
IRQspi = 54,
|
||||
IRQsdhost = 56,
|
||||
IRQuart = 57,
|
||||
IRQmmc = 62,
|
||||
|
||||
IRQbasic = 64,
|
||||
|
|
|
@ -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
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