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