Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * bcm2835 mini uart (UART1)
 */

#include "u.h"
#include "../port/lib.h"
#include "../port/error.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

#define GPIOREGS        (VIRTIO+0x200000)
#define AUXREGS         (VIRTIO+0x215000)
#define OkLed           16
#define TxPin           14
#define RxPin           15

/* GPIO regs */
enum {
        Fsel0   = 0x00>>2,
                FuncMask= 0x7,
                Input   = 0x0,
                Output  = 0x1,
                Alt0    = 0x4,
                Alt1    = 0x5,
                Alt2    = 0x6,
                Alt3    = 0x7,
                Alt4    = 0x3,
                Alt5    = 0x2,
        Set0    = 0x1c>>2,
        Clr0    = 0x28>>2,
        Lev0    = 0x34>>2,
        PUD     = 0x94>>2,
                Off     = 0x0,
                Pulldown= 0x1,
                Pullup  = 0x2,
        PUDclk0 = 0x98>>2,
        PUDclk1 = 0x9c>>2,
};

/* AUX regs */
enum {
        Irq     = 0x00>>2,
                UartIrq = 1<<0,
        Enables = 0x04>>2,
                UartEn  = 1<<0,
        MuIo    = 0x40>>2,
        MuIer   = 0x44>>2,
                RxIen   = 1<<0,
                TxIen   = 1<<1,
        MuIir   = 0x48>>2,
        MuLcr   = 0x4c>>2,
                Bitsmask= 3<<0,
                Bits7   = 2<<0,
                Bits8   = 3<<0,
        MuMcr   = 0x50>>2,
                RtsN    = 1<<1,
        MuLsr   = 0x54>>2,
                TxDone  = 1<<6,
                TxRdy   = 1<<5,
                RxRdy   = 1<<0,
        MuCntl  = 0x60>>2,
                CtsFlow = 1<<3,
                TxEn    = 1<<1,
                RxEn    = 1<<0,
        MuBaud  = 0x68>>2,
};

extern PhysUart miniphysuart;

static Uart miniuart = {
        .regs   = (u32int*)AUXREGS,
        .name   = "uart0",
        .freq   = 250000000,
        .phys   = &miniphysuart,
};

void
gpiosel(uint pin, int func)
{       
        u32int *gp, *fsel;
        int off;

        gp = (u32int*)GPIOREGS;
        fsel = &gp[Fsel0 + pin/10];
        off = (pin % 10) * 3;
        *fsel = (*fsel & ~(FuncMask<<off)) | func<<off;
}

void
gpiopulloff(uint pin)
{
        u32int *gp, *reg;
        u32int mask;

        gp = (u32int*)GPIOREGS;
        reg = &gp[PUDclk0 + pin/32];
        mask = 1 << (pin % 32);
        gp[PUD] = Off;
        microdelay(1);
        *reg = mask;
        microdelay(1);
        *reg = 0;
}

void
gpioout(uint pin, int set)
{
        u32int *gp;
        int v;

        gp = (u32int*)GPIOREGS;
        v = set? Set0 : Clr0;
        gp[v + pin/32] = 1 << (pin % 32);
}

int
gpioin(uint pin)
{
        u32int *gp;

        gp = (u32int*)GPIOREGS;
        return (gp[Lev0 + pin/32] & (1 << (pin % 32))) != 0;
}

static void
interrupt(Ureg*, void *arg)
{
        Uart *uart;
        u32int *ap;

        uart = arg;
        ap = (u32int*)uart->regs;

        coherence();
        if(0 && (ap[Irq] & UartIrq) == 0)
                return;
        if(ap[MuLsr] & TxRdy)
                uartkick(uart);
        if(ap[MuLsr] & RxRdy){
                if(uart->console){
                        if(uart->opens == 1)
                                uart->putc = kbdcr2nl;
                        else
                                uart->putc = nil;
                }
                do{
                        uartrecv(uart, ap[MuIo] & 0xFF);
                }while(ap[MuLsr] & RxRdy);
        }
        coherence();
}

static Uart*
pnp(void)
{
        Uart *uart;

        uart = &miniuart;
        if(uart->console == 0)
                kbdq = qopen(8*1024, 0, nil, nil);
        return uart;
}

static void
enable(Uart *uart, int ie)
{
        u32int *ap;

        ap = (u32int*)uart->regs;
        delay(10);
        gpiosel(TxPin, Alt5);
        gpiosel(RxPin, Alt5);
        gpiopulloff(TxPin);
        gpiopulloff(RxPin);
        ap[Enables] |= UartEn;
        ap[MuIir] = 6;
        ap[MuLcr] = Bits8;
        ap[MuCntl] = TxEn|RxEn;
        ap[MuBaud] = 250000000/(115200*8) - 1;
        if(ie){
                intrenable(IRQaux, interrupt, uart, 0, "uart");
                ap[MuIer] = RxIen|TxIen;
        }else
                ap[MuIer] = 0;
}

static void
disable(Uart *uart)
{
        u32int *ap;

        ap = (u32int*)uart->regs;
        ap[MuCntl] = 0;
        ap[MuIer] = 0;
}

static void
kick(Uart *uart)
{
        u32int *ap;

        ap = (u32int*)uart->regs;
        if(uart->blocked)
                return;
        coherence();
        while(ap[MuLsr] & TxRdy){
                if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
                        break;
                ap[MuIo] = *(uart->op++);
        }
        if(ap[MuLsr] & TxDone)
                ap[MuIer] &= ~TxIen;
        else
                ap[MuIer] |= TxIen;
        coherence();
}

/* TODO */
static void
dobreak(Uart *uart, int ms)
{
        USED(uart, ms);
}

static int
baud(Uart *uart, int n)
{
        u32int *ap;

        ap = (u32int*)uart->regs;
        if(uart->freq == 0 || n <= 0)
                return -1;
        ap[MuBaud] = (uart->freq + 4*n - 1) / (8 * n) - 1;
        uart->baud = n;
        return 0;
}

static int
bits(Uart *uart, int n)
{
        u32int *ap;
        int set;

        ap = (u32int*)uart->regs;
        switch(n){
        case 7:
                set = Bits7;
                break;
        case 8:
                set = Bits8;
                break;
        default:
                return -1;
        }
        ap[MuLcr] = (ap[MuLcr] & ~Bitsmask) | set;
        uart->bits = n;
        return 0;
}

static int
stop(Uart *uart, int n)
{
        if(n != 1)
                return -1;
        uart->stop = n;
        return 0;
}

static int
parity(Uart *uart, int n)
{
        if(n != 'n')
                return -1;
        uart->parity = n;
        return 0;
}

/*
 * cts/rts flow control
 *   need to bring signals to gpio pins before enabling this
 */

static void
modemctl(Uart *uart, int on)
{
        u32int *ap;

        ap = (u32int*)uart->regs;
        if(on)
                ap[MuCntl] |= CtsFlow;
        else
                ap[MuCntl] &= ~CtsFlow;
        uart->modem = on;
}

static void
rts(Uart *uart, int on)
{
        u32int *ap;

        ap = (u32int*)uart->regs;
        if(on)
                ap[MuMcr] &= ~RtsN;
        else
                ap[MuMcr] |= RtsN;
}

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)
{
}

void
putc(Uart*, int c)
{
        u32int *ap;

        ap = (u32int*)AUXREGS;
        while((ap[MuLsr] & TxRdy) == 0)
                ;
        ap[MuIo] = c;
        while((ap[MuLsr] & TxRdy) == 0)
                ;
}

int
getc(Uart*)
{
        u32int *ap;

        ap = (u32int*)AUXREGS;
        while((ap[MuLsr] & RxRdy) == 0)
                ;
        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, "b9600 l8 pn s1");
        if(*cmd != '\0')
                uartctl(uart, cmd);

        consuart = uart;
        uart->console = 1;
}

PhysUart miniphysuart = {
        .name           = "miniuart",
        .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,
};

void
okay(int on)
{
        static int first;

        if(!first++)
                gpiosel(OkLed, Output);
        gpioout(OkLed, !on);
}