shithub: riscv

Download patch

ref: 6a3a3d69c67647db5d5176a7cf9ee68e9fce4352
parent: fe34e52d19aeeb04bc34114fda928b6afeb62253
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Thu Apr 11 09:21:06 EDT 2019

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.

--- a/sys/src/9/bcm/devarch.c
+++ b/sys/src/9/bcm/devarch.c
@@ -174,6 +174,34 @@
 }
 
 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)
 {
 	static int first;
--- a/sys/src/9/bcm/io.h
+++ b/sys/src/9/bcm/io.h
@@ -11,6 +11,7 @@
 	IRQi2c		= 53,
 	IRQspi		= 54,
 	IRQsdhost	= 56,
+	IRQuart		= 57,
 	IRQmmc		= 62,
 
 	IRQbasic	= 64,
--- a/sys/src/9/bcm/uartmini.c
+++ b/sys/src/9/bcm/uartmini.c
@@ -47,7 +47,7 @@
 
 static Uart miniuart = {
 	.regs	= (u32int*)AUXREGS,
-	.name	= "uart0",
+	.name	= "uart1",
 	.freq	= 250000000,
 	.baud	= 115200,
 	.phys	= &miniphysuart,
@@ -100,7 +100,7 @@
 	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 @@
 {
 }
 
-void
+static void
 putc(Uart*, int c)
 {
 	u32int *ap;
@@ -272,7 +272,7 @@
 		;
 }
 
-int
+static int
 getc(Uart*)
 {
 	u32int *ap;
@@ -283,38 +283,8 @@
 	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,
--- /dev/null
+++ b/sys/src/9/bcm/uartpl011.c
@@ -1,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,
+};