plan9fox/sys/src/9/bcm/uartpl011.c
cinap_lenrek 811b80cae1 bcm, bcm64: make irq.$O optional and add intrdisable(), use intrenable()
the raspberry pi 4 has a new interrupt controller and
pci support, so get rid of intrenable() macro and
properly make intrenable function with tbdf argument.
2019-07-25 08:58:58 +02:00

358 lines
5 KiB
C

/*
* 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();
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
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, BUSUNKNOWN, uart->name);
reg[IMSC] = TXIM|RXIM;
}
uarton(uart);
}
static void
linectl(Uart *uart, u32int set, u32int clr)
{
u32int *reg = (u32int*)uart->regs;
if(uart->enabled)
uartoff(uart);
reg[LCRH] = set | (reg[LCRH] & ~clr);
if(uart->enabled)
uarton(uart);
}
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)
{
linectl(uart, BRK, 0);
delay(ms);
linectl(uart, 0, BRK);
}
static int
baud(Uart *uart, int n)
{
u32int *reg = (u32int*)uart->regs;
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;
}
static int
bits(Uart *uart, int n)
{
switch(n){
case 8:
linectl(uart, WLEN8, WLENM);
break;
case 7:
linectl(uart, WLEN7, WLENM);
break;
case 6:
linectl(uart, WLEN6, WLENM);
break;
case 5:
linectl(uart, WLEN5, WLENM);
break;
default:
return -1;
}
uart->bits = n;
return 0;
}
static int
stop(Uart *uart, int n)
{
switch(n){
case 1:
linectl(uart, 0, STP2);
break;
case 2:
linectl(uart, STP2, 0);
break;
default:
return -1;
}
uart->stop = n;
return 0;
}
static int
parity(Uart *uart, int n)
{
switch(n){
case 'n':
linectl(uart, 0, PEN);
break;
case 'e':
linectl(uart, EPS|PEN, 0);
break;
case 'o':
linectl(uart, PEN, EPS);
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,
};