plan9front/sys/src/9/kw/uartkw.c

363 lines
5.5 KiB
C

/*
* marvell kirkwood uart (supposed to be a 16550)
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
// #include "../port/uart.h"
enum {
UartFREQ = 0, // xxx
IERrx = 1<<0,
IERtx = 1<<1,
IRRintrmask = (1<<4)-1,
IRRnointr = 1,
IRRthrempty = 2,
IRRrxdata = 4,
IRRrxstatus = 6,
IRRtimeout = 12,
IRRfifomask = 3<<6,
IRRfifoenable = 3<<6,
FCRenable = 1<<0,
FCRrxreset = 1<<1,
FCRtxreset = 1<<2,
/* reserved */
FCRrxtriggermask = 3<<6,
FCRrxtrigger1 = 0<<6,
FCRrxtrigger4 = 1<<6,
FCRrxtrigger8 = 2<<6,
FCRrxtrigger14 = 3<<6,
LCRbpcmask = 3<<0,
LCRbpc5 = 0<<0,
LCRbpc6 = 1<<0,
LCRbpc7 = 2<<0,
LCRbpc8 = 3<<0,
LCRstop2b = 1<<2,
LCRparity = 1<<3,
LCRparityeven = 1<<4,
LCRbreak = 1<<6,
LCRdivlatch = 1<<7,
LSRrx = 1<<0,
LSRrunerr = 1<<1,
LSRparerr = 1<<2,
LSRframeerr = 1<<3,
LSRbi = 1<<4,
LSRthre = 1<<5,
LSRtxempty = 1<<6,
LSRfifoerr = 1<<7,
};
extern PhysUart kwphysuart;
typedef struct UartReg UartReg;
struct UartReg
{
union {
ulong thr;
ulong dll;
ulong rbr;
};
union {
ulong ier;
ulong dlh;
};
union {
ulong iir;
ulong fcr;
};
ulong lcr;
ulong mcr;
ulong lsr;
ulong scr;
};
typedef struct Ctlr Ctlr;
struct Ctlr {
UartReg*regs;
int irq;
Lock;
};
static Ctlr kirkwoodctlr[] = {
{
.regs = nil, /* filled in below */
.irq = IRQ1uart0, },
};
static Uart kirkwooduart[] = {
{
.regs = &kirkwoodctlr[0],
.name = "eia0",
.freq = UartFREQ,
.phys = &kwphysuart,
.special= 0,
.console= 1,
.next = nil, },
};
static void
kw_read(Uart *uart)
{
Ctlr *ctlr = uart->regs;
UartReg *regs = ctlr->regs;
ulong lsr;
char c;
while ((lsr = regs->lsr) & LSRrx) {
if(lsr&LSRrunerr)
uart->oerr++;
if(lsr&LSRparerr)
uart->perr++;
if(lsr&LSRframeerr)
uart->ferr++;
c = regs->rbr;
if((lsr & (LSRbi|LSRframeerr|LSRparerr)) == 0)
uartrecv(uart, c);
}
}
static void
kw_intr(Ureg*, void *arg)
{
Uart *uart = arg;
Ctlr *ctlr = uart->regs;
UartReg *regs = ctlr->regs;
ulong v;
if(regs == 0) {
kirkwoodctlr[0].regs = (UartReg *)soc.uart[0];
regs = (UartReg *)soc.uart[0]; /* caution */
coherence();
}
v = regs->iir;
if(v & IRRthrempty)
uartkick(uart);
if(v & IRRrxdata)
kw_read(uart);
intrclear(Irqhi, ctlr->irq);
}
static Uart*
kw_pnp(void)
{
kirkwoodctlr[0].regs = (UartReg *)soc.uart[0];
coherence();
return kirkwooduart;
}
static void
kw_enable(Uart* uart, int ie)
{
Ctlr *ctlr = uart->regs;
UartReg *regs = ctlr->regs;
if(regs == 0) {
kirkwoodctlr[0].regs = (UartReg *)soc.uart[0];
regs = (UartReg *)soc.uart[0]; /* caution */
coherence();
}
USED(ie);
regs->fcr = FCRenable|FCRrxtrigger4;
regs->ier = IERrx|IERtx;
intrenable(Irqhi, ctlr->irq, kw_intr, uart, uart->name);
(*uart->phys->dtr)(uart, 1);
(*uart->phys->rts)(uart, 1);
}
static void
kw_disable(Uart* uart)
{
Ctlr *ctlr = uart->regs;
(*uart->phys->dtr)(uart, 0);
(*uart->phys->rts)(uart, 0);
(*uart->phys->fifo)(uart, 0);
intrdisable(Irqhi, ctlr->irq, kw_intr, uart, uart->name);
}
static void
kw_kick(Uart* uart)
{
Ctlr *ctlr = uart->regs;
UartReg *regs = ctlr->regs;
int i;
if(uart->cts == 0 || uart->blocked)
return;
for(i = 0; i < 16; i++) {
if((regs->lsr & LSRthre) == 0 ||
uart->op >= uart->oe && uartstageoutput(uart) == 0)
break;
regs->thr = *uart->op++;
}
}
static void
kw_break(Uart* uart, int ms)
{
USED(uart, ms);
}
static int
kw_baud(Uart* uart, int baud)
{
USED(uart, baud);
return 0;
}
static int
kw_bits(Uart* uart, int bits)
{
USED(uart, bits);
return 0;
}
static int
kw_stop(Uart* uart, int stop)
{
USED(uart, stop);
return 0;
}
static int
kw_parity(Uart* uart, int parity)
{
USED(uart, parity);
return 0;
}
static void
kw_modemctl(Uart* uart, int on)
{
USED(uart, on);
}
static void
kw_rts(Uart* uart, int on)
{
USED(uart, on);
}
static void
kw_dtr(Uart* uart, int on)
{
USED(uart, on);
}
static long
kw_status(Uart* uart, void* buf, long n, long offset)
{
USED(uart, buf, n, offset);
return 0;
}
static void
kw_fifo(Uart* uart, int level)
{
USED(uart, level);
}
static int
kw_getc(Uart *uart)
{
Ctlr *ctlr = uart->regs;
UartReg *regs = ctlr->regs;
while((regs->lsr&LSRrx) == 0)
;
return regs->rbr;
}
static void
kw_putc(Uart *uart, int c)
{
Ctlr *ctlr = uart->regs;
UartReg *regs = ctlr->regs;
/* can be called from iprint, among many other places */
if(regs == 0) {
kirkwoodctlr[0].regs = (UartReg *)soc.uart[0];
regs = (UartReg *)soc.uart[0]; /* caution */
coherence();
}
while((regs->lsr&LSRthre) == 0)
;
regs->thr = c;
coherence();
}
PhysUart kwphysuart = {
.name = "kirkwood",
.pnp = kw_pnp,
.enable = kw_enable,
.disable = kw_disable,
.kick = kw_kick,
.dobreak = kw_break,
.baud = kw_baud,
.bits = kw_bits,
.stop = kw_stop,
.parity = kw_parity,
.modemctl = kw_modemctl,
.rts = kw_rts,
.dtr = kw_dtr,
.status = kw_status,
.fifo = kw_fifo,
.getc = kw_getc,
.putc = kw_putc,
};
void
uartkirkwoodconsole(void)
{
Uart *uart;
uart = &kirkwooduart[0];
(*uart->phys->enable)(uart, 0);
uartctl(uart, "b115200 l8 pn s1 i1");
uart->console = 1;
consuart = uart;
//serialputs("uart0 kirkwood\n", strlen("uart0 kirkwood\n"));
}
void
serialputc(int c)
{
int cnt, s;
UartReg *regs = (UartReg *)soc.uart[0];
s = splhi();
cnt = m->cpuhz;
if (cnt <= 0) /* cpuhz not set yet? */
cnt = 1000000;
while((regs->lsr & LSRthre) == 0 && --cnt > 0)
;
regs->thr = c;
coherence();
delay(1);
splx(s);
}
void
serialputs(char *p, int len)
{
while(--len >= 0) {
if(*p == '\n')
serialputc('\r');
serialputc(*p++);
}
}