xref: /plan9-contrib/sys/src/9/bcm/uartmini.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier  * bcm2835 mini uart (UART1)
35d9de2d3SDavid du Colombier  */
45d9de2d3SDavid du Colombier 
55d9de2d3SDavid du Colombier #include "u.h"
65d9de2d3SDavid du Colombier #include "../port/lib.h"
75d9de2d3SDavid du Colombier #include "../port/error.h"
85d9de2d3SDavid du Colombier #include "mem.h"
95d9de2d3SDavid du Colombier #include "dat.h"
105d9de2d3SDavid du Colombier #include "fns.h"
115d9de2d3SDavid du Colombier #include "io.h"
125d9de2d3SDavid du Colombier 
135d9de2d3SDavid du Colombier #define AUXREGS		(VIRTIO+0x215000)
145d9de2d3SDavid du Colombier #define	OkLed		16
155d9de2d3SDavid du Colombier #define	TxPin		14
165d9de2d3SDavid du Colombier #define	RxPin		15
175d9de2d3SDavid du Colombier 
185d9de2d3SDavid du Colombier /* AUX regs */
195d9de2d3SDavid du Colombier enum {
205d9de2d3SDavid du Colombier 	Irq	= 0x00>>2,
215d9de2d3SDavid du Colombier 		UartIrq	= 1<<0,
225d9de2d3SDavid du Colombier 	Enables	= 0x04>>2,
235d9de2d3SDavid du Colombier 		UartEn	= 1<<0,
245d9de2d3SDavid du Colombier 	MuIo	= 0x40>>2,
255d9de2d3SDavid du Colombier 	MuIer	= 0x44>>2,
265d9de2d3SDavid du Colombier 		RxIen	= 1<<0,
275d9de2d3SDavid du Colombier 		TxIen	= 1<<1,
285d9de2d3SDavid du Colombier 	MuIir	= 0x48>>2,
295d9de2d3SDavid du Colombier 	MuLcr	= 0x4c>>2,
305d9de2d3SDavid du Colombier 		Bitsmask= 3<<0,
315d9de2d3SDavid du Colombier 		Bits7	= 2<<0,
325d9de2d3SDavid du Colombier 		Bits8	= 3<<0,
335d9de2d3SDavid du Colombier 	MuMcr	= 0x50>>2,
345d9de2d3SDavid du Colombier 		RtsN	= 1<<1,
355d9de2d3SDavid du Colombier 	MuLsr	= 0x54>>2,
365d9de2d3SDavid du Colombier 		TxDone	= 1<<6,
375d9de2d3SDavid du Colombier 		TxRdy	= 1<<5,
385d9de2d3SDavid du Colombier 		RxRdy	= 1<<0,
395d9de2d3SDavid du Colombier 	MuCntl	= 0x60>>2,
405d9de2d3SDavid du Colombier 		CtsFlow	= 1<<3,
415d9de2d3SDavid du Colombier 		TxEn	= 1<<1,
425d9de2d3SDavid du Colombier 		RxEn	= 1<<0,
435d9de2d3SDavid du Colombier 	MuBaud	= 0x68>>2,
445d9de2d3SDavid du Colombier };
455d9de2d3SDavid du Colombier 
465d9de2d3SDavid du Colombier extern PhysUart miniphysuart;
475d9de2d3SDavid du Colombier 
485d9de2d3SDavid du Colombier static Uart miniuart = {
495d9de2d3SDavid du Colombier 	.regs	= (u32int*)AUXREGS,
505d9de2d3SDavid du Colombier 	.name	= "uart0",
515d9de2d3SDavid du Colombier 	.freq	= 250000000,
52*5c47fe09SDavid du Colombier 	.baud	= 115200,
535d9de2d3SDavid du Colombier 	.phys	= &miniphysuart,
545d9de2d3SDavid du Colombier };
555d9de2d3SDavid du Colombier 
56*5c47fe09SDavid du Colombier static int baud(Uart*, int);
575d9de2d3SDavid du Colombier 
585d9de2d3SDavid du Colombier static void
interrupt(Ureg *,void * arg)595d9de2d3SDavid du Colombier interrupt(Ureg*, void *arg)
605d9de2d3SDavid du Colombier {
615d9de2d3SDavid du Colombier 	Uart *uart;
625d9de2d3SDavid du Colombier 	u32int *ap;
635d9de2d3SDavid du Colombier 
645d9de2d3SDavid du Colombier 	uart = arg;
655d9de2d3SDavid du Colombier 	ap = (u32int*)uart->regs;
665d9de2d3SDavid du Colombier 
675d9de2d3SDavid du Colombier 	coherence();
685d9de2d3SDavid du Colombier 	if(0 && (ap[Irq] & UartIrq) == 0)
695d9de2d3SDavid du Colombier 		return;
705d9de2d3SDavid du Colombier 	if(ap[MuLsr] & TxRdy)
715d9de2d3SDavid du Colombier 		uartkick(uart);
725d9de2d3SDavid du Colombier 	if(ap[MuLsr] & RxRdy){
73b4d1cf41SDavid du Colombier 		if(uart->console){
745d9de2d3SDavid du Colombier 			if(uart->opens == 1)
755d9de2d3SDavid du Colombier 				uart->putc = kbdcr2nl;
765d9de2d3SDavid du Colombier 			else
775d9de2d3SDavid du Colombier 				uart->putc = nil;
78b4d1cf41SDavid du Colombier 		}
795d9de2d3SDavid du Colombier 		do{
805d9de2d3SDavid du Colombier 			uartrecv(uart, ap[MuIo] & 0xFF);
815d9de2d3SDavid du Colombier 		}while(ap[MuLsr] & RxRdy);
825d9de2d3SDavid du Colombier 	}
835d9de2d3SDavid du Colombier 	coherence();
845d9de2d3SDavid du Colombier }
855d9de2d3SDavid du Colombier 
865d9de2d3SDavid du Colombier static Uart*
pnp(void)875d9de2d3SDavid du Colombier pnp(void)
885d9de2d3SDavid du Colombier {
895d9de2d3SDavid du Colombier 	Uart *uart;
905d9de2d3SDavid du Colombier 
915d9de2d3SDavid du Colombier 	uart = &miniuart;
925d9de2d3SDavid du Colombier 	if(uart->console == 0)
935d9de2d3SDavid du Colombier 		kbdq = qopen(8*1024, 0, nil, nil);
945d9de2d3SDavid du Colombier 	return uart;
955d9de2d3SDavid du Colombier }
965d9de2d3SDavid du Colombier 
975d9de2d3SDavid du Colombier static void
enable(Uart * uart,int ie)985d9de2d3SDavid du Colombier enable(Uart *uart, int ie)
995d9de2d3SDavid du Colombier {
1005d9de2d3SDavid du Colombier 	u32int *ap;
1015d9de2d3SDavid du Colombier 
1025d9de2d3SDavid du Colombier 	ap = (u32int*)uart->regs;
1035d9de2d3SDavid du Colombier 	delay(10);
1045d9de2d3SDavid du Colombier 	gpiosel(TxPin, Alt5);
1055d9de2d3SDavid du Colombier 	gpiosel(RxPin, Alt5);
1065d9de2d3SDavid du Colombier 	gpiopulloff(TxPin);
107*5c47fe09SDavid du Colombier 	gpiopullup(RxPin);
1085d9de2d3SDavid du Colombier 	ap[Enables] |= UartEn;
1095d9de2d3SDavid du Colombier 	ap[MuIir] = 6;
1105d9de2d3SDavid du Colombier 	ap[MuLcr] = Bits8;
1115d9de2d3SDavid du Colombier 	ap[MuCntl] = TxEn|RxEn;
112*5c47fe09SDavid du Colombier 	baud(uart, uart->baud);
1135d9de2d3SDavid du Colombier 	if(ie){
1145d9de2d3SDavid du Colombier 		intrenable(IRQaux, interrupt, uart, 0, "uart");
1155d9de2d3SDavid du Colombier 		ap[MuIer] = RxIen|TxIen;
1165d9de2d3SDavid du Colombier 	}else
1175d9de2d3SDavid du Colombier 		ap[MuIer] = 0;
1185d9de2d3SDavid du Colombier }
1195d9de2d3SDavid du Colombier 
1205d9de2d3SDavid du Colombier static void
disable(Uart * uart)1215d9de2d3SDavid du Colombier disable(Uart *uart)
1225d9de2d3SDavid du Colombier {
1235d9de2d3SDavid du Colombier 	u32int *ap;
1245d9de2d3SDavid du Colombier 
1255d9de2d3SDavid du Colombier 	ap = (u32int*)uart->regs;
1265d9de2d3SDavid du Colombier 	ap[MuCntl] = 0;
1275d9de2d3SDavid du Colombier 	ap[MuIer] = 0;
1285d9de2d3SDavid du Colombier }
1295d9de2d3SDavid du Colombier 
1305d9de2d3SDavid du Colombier static void
kick(Uart * uart)1315d9de2d3SDavid du Colombier kick(Uart *uart)
1325d9de2d3SDavid du Colombier {
1335d9de2d3SDavid du Colombier 	u32int *ap;
1345d9de2d3SDavid du Colombier 
1355d9de2d3SDavid du Colombier 	ap = (u32int*)uart->regs;
1365d9de2d3SDavid du Colombier 	if(uart->blocked)
1375d9de2d3SDavid du Colombier 		return;
1385d9de2d3SDavid du Colombier 	coherence();
1395d9de2d3SDavid du Colombier 	while(ap[MuLsr] & TxRdy){
1405d9de2d3SDavid du Colombier 		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
1415d9de2d3SDavid du Colombier 			break;
1425d9de2d3SDavid du Colombier 		ap[MuIo] = *(uart->op++);
1435d9de2d3SDavid du Colombier 	}
1445d9de2d3SDavid du Colombier 	if(ap[MuLsr] & TxDone)
1455d9de2d3SDavid du Colombier 		ap[MuIer] &= ~TxIen;
1465d9de2d3SDavid du Colombier 	else
1475d9de2d3SDavid du Colombier 		ap[MuIer] |= TxIen;
1485d9de2d3SDavid du Colombier 	coherence();
1495d9de2d3SDavid du Colombier }
1505d9de2d3SDavid du Colombier 
1515d9de2d3SDavid du Colombier /* TODO */
1525d9de2d3SDavid du Colombier static void
dobreak(Uart * uart,int ms)1535d9de2d3SDavid du Colombier dobreak(Uart *uart, int ms)
1545d9de2d3SDavid du Colombier {
1555d9de2d3SDavid du Colombier 	USED(uart, ms);
1565d9de2d3SDavid du Colombier }
1575d9de2d3SDavid du Colombier 
1585d9de2d3SDavid du Colombier static int
baud(Uart * uart,int n)1595d9de2d3SDavid du Colombier baud(Uart *uart, int n)
1605d9de2d3SDavid du Colombier {
1615d9de2d3SDavid du Colombier 	u32int *ap;
1625d9de2d3SDavid du Colombier 
1635d9de2d3SDavid du Colombier 	ap = (u32int*)uart->regs;
1645d9de2d3SDavid du Colombier 	if(uart->freq == 0 || n <= 0)
1655d9de2d3SDavid du Colombier 		return -1;
1665d9de2d3SDavid du Colombier 	ap[MuBaud] = (uart->freq + 4*n - 1) / (8 * n) - 1;
1675d9de2d3SDavid du Colombier 	uart->baud = n;
1685d9de2d3SDavid du Colombier 	return 0;
1695d9de2d3SDavid du Colombier }
1705d9de2d3SDavid du Colombier 
1715d9de2d3SDavid du Colombier static int
bits(Uart * uart,int n)1725d9de2d3SDavid du Colombier bits(Uart *uart, int n)
1735d9de2d3SDavid du Colombier {
1745d9de2d3SDavid du Colombier 	u32int *ap;
1755d9de2d3SDavid du Colombier 	int set;
1765d9de2d3SDavid du Colombier 
1775d9de2d3SDavid du Colombier 	ap = (u32int*)uart->regs;
1785d9de2d3SDavid du Colombier 	switch(n){
1795d9de2d3SDavid du Colombier 	case 7:
1805d9de2d3SDavid du Colombier 		set = Bits7;
1815d9de2d3SDavid du Colombier 		break;
1825d9de2d3SDavid du Colombier 	case 8:
1835d9de2d3SDavid du Colombier 		set = Bits8;
1845d9de2d3SDavid du Colombier 		break;
1855d9de2d3SDavid du Colombier 	default:
1865d9de2d3SDavid du Colombier 		return -1;
1875d9de2d3SDavid du Colombier 	}
1885d9de2d3SDavid du Colombier 	ap[MuLcr] = (ap[MuLcr] & ~Bitsmask) | set;
1895d9de2d3SDavid du Colombier 	uart->bits = n;
1905d9de2d3SDavid du Colombier 	return 0;
1915d9de2d3SDavid du Colombier }
1925d9de2d3SDavid du Colombier 
1935d9de2d3SDavid du Colombier static int
stop(Uart * uart,int n)1945d9de2d3SDavid du Colombier stop(Uart *uart, int n)
1955d9de2d3SDavid du Colombier {
1965d9de2d3SDavid du Colombier 	if(n != 1)
1975d9de2d3SDavid du Colombier 		return -1;
1985d9de2d3SDavid du Colombier 	uart->stop = n;
1995d9de2d3SDavid du Colombier 	return 0;
2005d9de2d3SDavid du Colombier }
2015d9de2d3SDavid du Colombier 
2025d9de2d3SDavid du Colombier static int
parity(Uart * uart,int n)2035d9de2d3SDavid du Colombier parity(Uart *uart, int n)
2045d9de2d3SDavid du Colombier {
2055d9de2d3SDavid du Colombier 	if(n != 'n')
2065d9de2d3SDavid du Colombier 		return -1;
2075d9de2d3SDavid du Colombier 	uart->parity = n;
2085d9de2d3SDavid du Colombier 	return 0;
2095d9de2d3SDavid du Colombier }
2105d9de2d3SDavid du Colombier 
2115d9de2d3SDavid du Colombier /*
2125d9de2d3SDavid du Colombier  * cts/rts flow control
2135d9de2d3SDavid du Colombier  *   need to bring signals to gpio pins before enabling this
2145d9de2d3SDavid du Colombier  */
2155d9de2d3SDavid du Colombier 
2165d9de2d3SDavid du Colombier static void
modemctl(Uart * uart,int on)2175d9de2d3SDavid du Colombier modemctl(Uart *uart, int on)
2185d9de2d3SDavid du Colombier {
2195d9de2d3SDavid du Colombier 	u32int *ap;
2205d9de2d3SDavid du Colombier 
2215d9de2d3SDavid du Colombier 	ap = (u32int*)uart->regs;
2225d9de2d3SDavid du Colombier 	if(on)
2235d9de2d3SDavid du Colombier 		ap[MuCntl] |= CtsFlow;
2245d9de2d3SDavid du Colombier 	else
2255d9de2d3SDavid du Colombier 		ap[MuCntl] &= ~CtsFlow;
2265d9de2d3SDavid du Colombier 	uart->modem = on;
2275d9de2d3SDavid du Colombier }
2285d9de2d3SDavid du Colombier 
2295d9de2d3SDavid du Colombier static void
rts(Uart * uart,int on)2305d9de2d3SDavid du Colombier rts(Uart *uart, int on)
2315d9de2d3SDavid du Colombier {
2325d9de2d3SDavid du Colombier 	u32int *ap;
2335d9de2d3SDavid du Colombier 
2345d9de2d3SDavid du Colombier 	ap = (u32int*)uart->regs;
2355d9de2d3SDavid du Colombier 	if(on)
2365d9de2d3SDavid du Colombier 		ap[MuMcr] &= ~RtsN;
2375d9de2d3SDavid du Colombier 	else
2385d9de2d3SDavid du Colombier 		ap[MuMcr] |= RtsN;
2395d9de2d3SDavid du Colombier }
2405d9de2d3SDavid du Colombier 
2415d9de2d3SDavid du Colombier static long
status(Uart * uart,void * buf,long n,long offset)2425d9de2d3SDavid du Colombier status(Uart *uart, void *buf, long n, long offset)
2435d9de2d3SDavid du Colombier {
2445d9de2d3SDavid du Colombier 	char *p;
2455d9de2d3SDavid du Colombier 
2465d9de2d3SDavid du Colombier 	p = malloc(READSTR);
2475d9de2d3SDavid du Colombier 	if(p == nil)
2485d9de2d3SDavid du Colombier 		error(Enomem);
2495d9de2d3SDavid du Colombier 	snprint(p, READSTR,
2505d9de2d3SDavid du Colombier 		"b%d\n"
2515d9de2d3SDavid du Colombier 		"dev(%d) type(%d) framing(%d) overruns(%d) "
2525d9de2d3SDavid du Colombier 		"berr(%d) serr(%d)\n",
2535d9de2d3SDavid du Colombier 
2545d9de2d3SDavid du Colombier 		uart->baud,
2555d9de2d3SDavid du Colombier 		uart->dev,
2565d9de2d3SDavid du Colombier 		uart->type,
2575d9de2d3SDavid du Colombier 		uart->ferr,
2585d9de2d3SDavid du Colombier 		uart->oerr,
2595d9de2d3SDavid du Colombier 		uart->berr,
2605d9de2d3SDavid du Colombier 		uart->serr
2615d9de2d3SDavid du Colombier 	);
2625d9de2d3SDavid du Colombier 	n = readstr(offset, buf, n, p);
2635d9de2d3SDavid du Colombier 	free(p);
2645d9de2d3SDavid du Colombier 
2655d9de2d3SDavid du Colombier 	return n;
2665d9de2d3SDavid du Colombier }
2675d9de2d3SDavid du Colombier 
2685d9de2d3SDavid du Colombier static void
donothing(Uart *,int)2695d9de2d3SDavid du Colombier donothing(Uart*, int)
2705d9de2d3SDavid du Colombier {
2715d9de2d3SDavid du Colombier }
2725d9de2d3SDavid du Colombier 
2735d9de2d3SDavid du Colombier void
putc(Uart *,int c)2745d9de2d3SDavid du Colombier putc(Uart*, int c)
2755d9de2d3SDavid du Colombier {
2765d9de2d3SDavid du Colombier 	u32int *ap;
2775d9de2d3SDavid du Colombier 
2785d9de2d3SDavid du Colombier 	ap = (u32int*)AUXREGS;
2795d9de2d3SDavid du Colombier 	while((ap[MuLsr] & TxRdy) == 0)
2805d9de2d3SDavid du Colombier 		;
2815d9de2d3SDavid du Colombier 	ap[MuIo] = c;
2825d9de2d3SDavid du Colombier 	while((ap[MuLsr] & TxRdy) == 0)
2835d9de2d3SDavid du Colombier 		;
2845d9de2d3SDavid du Colombier }
2855d9de2d3SDavid du Colombier 
2865d9de2d3SDavid du Colombier int
getc(Uart *)2875d9de2d3SDavid du Colombier getc(Uart*)
2885d9de2d3SDavid du Colombier {
2895d9de2d3SDavid du Colombier 	u32int *ap;
2905d9de2d3SDavid du Colombier 
2915d9de2d3SDavid du Colombier 	ap = (u32int*)AUXREGS;
2925d9de2d3SDavid du Colombier 	while((ap[MuLsr] & RxRdy) == 0)
2935d9de2d3SDavid du Colombier 		;
2945d9de2d3SDavid du Colombier 	return ap[MuIo] & 0xFF;
2955d9de2d3SDavid du Colombier }
2965d9de2d3SDavid du Colombier 
2975d9de2d3SDavid du Colombier void
uartconsinit(void)2985d9de2d3SDavid du Colombier uartconsinit(void)
2995d9de2d3SDavid du Colombier {
3005d9de2d3SDavid du Colombier 	Uart *uart;
3015d9de2d3SDavid du Colombier 	int n;
3025d9de2d3SDavid du Colombier 	char *p, *cmd;
3035d9de2d3SDavid du Colombier 
3045d9de2d3SDavid du Colombier 	if((p = getconf("console")) == nil)
3055d9de2d3SDavid du Colombier 		return;
3065d9de2d3SDavid du Colombier 	n = strtoul(p, &cmd, 0);
3075d9de2d3SDavid du Colombier 	if(p == cmd)
3085d9de2d3SDavid du Colombier 		return;
3095d9de2d3SDavid du Colombier 	switch(n){
3105d9de2d3SDavid du Colombier 	default:
3115d9de2d3SDavid du Colombier 		return;
3125d9de2d3SDavid du Colombier 	case 0:
3135d9de2d3SDavid du Colombier 		uart = &miniuart;
3145d9de2d3SDavid du Colombier 		break;
3155d9de2d3SDavid du Colombier 	}
3165d9de2d3SDavid du Colombier 
3175d9de2d3SDavid du Colombier 	if(!uart->enabled)
3185d9de2d3SDavid du Colombier 		(*uart->phys->enable)(uart, 0);
319*5c47fe09SDavid du Colombier 	uartctl(uart, "l8 pn s1");
3205d9de2d3SDavid du Colombier 	if(*cmd != '\0')
3215d9de2d3SDavid du Colombier 		uartctl(uart, cmd);
3225d9de2d3SDavid du Colombier 
3235d9de2d3SDavid du Colombier 	consuart = uart;
3245d9de2d3SDavid du Colombier 	uart->console = 1;
3255d9de2d3SDavid du Colombier }
3265d9de2d3SDavid du Colombier 
3275d9de2d3SDavid du Colombier PhysUart miniphysuart = {
3285d9de2d3SDavid du Colombier 	.name		= "miniuart",
3295d9de2d3SDavid du Colombier 	.pnp		= pnp,
3305d9de2d3SDavid du Colombier 	.enable		= enable,
3315d9de2d3SDavid du Colombier 	.disable	= disable,
3325d9de2d3SDavid du Colombier 	.kick		= kick,
3335d9de2d3SDavid du Colombier 	.dobreak	= dobreak,
3345d9de2d3SDavid du Colombier 	.baud		= baud,
3355d9de2d3SDavid du Colombier 	.bits		= bits,
3365d9de2d3SDavid du Colombier 	.stop		= stop,
3375d9de2d3SDavid du Colombier 	.parity		= parity,
3385d9de2d3SDavid du Colombier 	.modemctl	= donothing,
3395d9de2d3SDavid du Colombier 	.rts		= rts,
3405d9de2d3SDavid du Colombier 	.dtr		= donothing,
3415d9de2d3SDavid du Colombier 	.status		= status,
3425d9de2d3SDavid du Colombier 	.fifo		= donothing,
3435d9de2d3SDavid du Colombier 	.getc		= getc,
3445d9de2d3SDavid du Colombier 	.putc		= putc,
3455d9de2d3SDavid du Colombier };
3465d9de2d3SDavid du Colombier 
3475d9de2d3SDavid du Colombier void
okay(int on)3485d9de2d3SDavid du Colombier okay(int on)
3495d9de2d3SDavid du Colombier {
3505d9de2d3SDavid du Colombier 	static int first;
351*5c47fe09SDavid du Colombier 	static int okled, polarity;
352*5c47fe09SDavid du Colombier 	char *p;
3535d9de2d3SDavid du Colombier 
354*5c47fe09SDavid du Colombier 	if(!first++){
355*5c47fe09SDavid du Colombier 		p = getconf("bcm2709.disk_led_gpio");
356*5c47fe09SDavid du Colombier 		if(p == nil)
357*5c47fe09SDavid du Colombier 			p = getconf("bcm2708.disk_led_gpio");
358*5c47fe09SDavid du Colombier 		if(p != nil)
359*5c47fe09SDavid du Colombier 			okled = strtol(p, 0, 0);
360*5c47fe09SDavid du Colombier 		else
361*5c47fe09SDavid du Colombier 			okled = 'v';
362*5c47fe09SDavid du Colombier 		p = getconf("bcm2709.disk_led_active_low");
363*5c47fe09SDavid du Colombier 		if(p == nil)
364*5c47fe09SDavid du Colombier 			p = getconf("bcm2708.disk_led_active_low");
365*5c47fe09SDavid du Colombier 		polarity = (p == nil || *p == '1');
366*5c47fe09SDavid du Colombier 		if(okled != 'v')
367*5c47fe09SDavid du Colombier 			gpiosel(okled, Output);
368*5c47fe09SDavid du Colombier 	}
369*5c47fe09SDavid du Colombier 	if(okled == 'v')
370*5c47fe09SDavid du Colombier 		vgpset(0, on);
371*5c47fe09SDavid du Colombier 	else if(okled != 0)
372*5c47fe09SDavid du Colombier 		gpioout(okled, on^polarity);
3735d9de2d3SDavid du Colombier }
374