plan9fox/sys/src/9/ppc/uartsmc.c
2011-03-30 19:35:09 +03:00

586 lines
9.5 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "imm.h"
#include "../port/error.h"
#include "../ppc/uartsmc.h"
/*
* PowerPC 8260 SMC UART
*/
enum {
/* SMC Mode Registers */
Clen = 0x7800, /* Character length */
Sl = 0x0400, /* Stop length, 0: one stop bit, 1: two */
Pen = 0x0200, /* Parity enable */
Pm = 0x0100, /* Parity mode, 0 is odd */
Sm = 0x0030, /* SMC mode, two bits */
SMUart = 0x0020, /* SMC mode, 0b10 is uart */
Dm = 0x000c, /* Diagnostic mode, 00 is normal */
Ten = 0x0002, /* Transmit enable, 1 is enabled */
Ren = 0x0001, /* Receive enable, 1 is enabled */
/* SMC Event/Mask Registers */
ce_Brke = 0x0040, /* Break end */
ce_Br = 0x0020, /* Break character received */
ce_Bsy = 0x0004, /* Busy condition */
ce_Txb = 0x0002, /* Tx buffer */
ce_Rxb = 0x0001, /* Rx buffer */
/* Receive/Transmit Buffer Descriptor Control bits */
BDContin= 1<<9,
BDIdle= 1<<8,
BDPreamble= 1<<8,
BDBreak= 1<<5,
BDFrame= 1<<4,
BDParity= 1<<3,
BDOverrun= 1<<1,
/* Tx and Rx buffer sizes (32 bytes) */
Rxsize= CACHELINESZ,
Txsize= CACHELINESZ,
};
extern PhysUart smcphysuart;
Uart smcuart[Nuart] = {
{
.name = "SMC1",
.baud = 115200,
.bits = 8,
.stop = 1,
.parity = 'n',
.phys = &smcphysuart,
.special = 0,
},
/* Only configure SMC1 for now
{
.name = "SMC2",
.baud = 115200,
.bits = 8,
.stop = 1,
.parity = 'n',
.phys = &smcphysuart,
.special = 0,
},
*/
};
int uartinited = 0;
static void smcinterrupt(Ureg*, void*);
static void smcputc(Uart *uart, int c);
int
baudgen(int baud)
{
int d;
d = ((m->brghz+(baud>>1))/baud)>>4;
if(d >= (1<<12))
return ((d+15)>>3)|1;
return d<<1;
}
static Uart*
smcpnp(void)
{
int i;
for (i = 0; i < nelem(smcuart) - 1; i++)
smcuart[i].next = smcuart + i + 1;
return smcuart;
}
void
smcinit(Uart *uart)
{
Uartsmc *p;
SMC *smc;
UartData *ud;
ulong lcr;
int bits;
ud = uart->regs;
if (ud->initialized)
return;
smcsetup(uart); /* Steps 1 through 4, PPC-dependent */
p = ud->usmc;
smc = ud->smc;
/* step 5: set up buffer descriptors */
/* setup my uart structure */
if (ud->rxb == nil)
ud->rxb = bdalloc(1);
if (ud->txb == nil)
ud->txb = bdalloc(1);
p->rbase = ((ulong)ud->rxb) - (ulong)IMMR;
p->tbase = ((ulong)ud->txb) - (ulong)IMMR;
/* step 8: receive buffer size */
p->mrblr = Rxsize;
/* step 9: */
p->maxidl = 15;
/* step 10: */
p->brkln = 0;
p->brkec = 0;
/* step 11: */
p->brkcr = 0;
/* step 12: setup receive buffer */
ud->rxb->status = BDEmpty|BDWrap|BDInt;
ud->rxb->length = 0;
ud->rxbuf = xspanalloc(Rxsize, 0, CACHELINESZ);
ud->rxb->addr = PADDR(ud->rxbuf);
/* step 13: step transmit buffer */
ud->txb->status = BDWrap|BDInt;
ud->txb->length = 0;
ud->txbuf = xspanalloc(Txsize, 0, CACHELINESZ);
ud->txb->addr = PADDR(ud->txbuf);
/* step 14: clear events */
smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
/*
* step 15: enable interrupts (done later)
* smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
*/
/* step 17: set parity, no of bits, UART mode, ... */
lcr = SMUart;
bits = uart->bits + 1;
switch(uart->parity){
case 'e':
lcr |= (Pen|Pm);
bits +=1;
break;
case 'o':
lcr |= Pen;
bits +=1;
break;
case 'n':
default:
break;
}
if(uart->stop == 2){
lcr |= Sl;
bits += 1;
}
/* Set new value and reenable if device was previously enabled */
smc->smcmr = lcr | bits <<11 | 0x3;
ud->initialized = 1;
}
static void
smcenable(Uart *uart, int intenb)
{
UartData *ud;
SMC *smc;
int nr;
nr = uart - smcuart;
if (nr < 0 || nr > Nuart)
panic("No SMC %d", nr);
ud = uartdata + nr;
ud->smcno = nr;
uart->regs = ud;
if (ud->initialized == 0)
smcinit(uart);
if (ud->enabled || intenb == 0)
return;
smc = ud->smc;
/* clear events */
smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
/* enable interrupts */
smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
intrenable(VecSMC1 + ud->smcno, smcinterrupt, uart, uart->name);
ud->enabled = 1;
}
static long
smcstatus(Uart* uart, void* buf, long n, long offset)
{
SMC *sp;
char p[128];
sp = ((UartData*)uart->regs)->smc;
snprint(p, sizeof p, "b%d c%d e%d l%d m0 p%c s%d i1\n"
"dev(%d) type(%d) framing(%d) overruns(%d)\n",
uart->baud,
uart->hup_dcd,
uart->hup_dsr,
((sp->smcmr & Clen) >>11) - ((sp->smcmr&Pen) ? 1 : 0) - ((sp->smcmr&Sl) ? 2 : 1),
(sp->smcmr & Pen) ? ((sp->smcmr & Pm) ? 'e': 'o'): 'n',
(sp->smcmr & Sl) ? 2: 1,
uart->dev,
uart->type,
uart->ferr,
uart->oerr
);
n = readstr(offset, buf, n, p);
free(p);
return n;
}
static void
smcfifo(Uart*, int)
{
/*
* Toggle FIFOs:
* if none, do nothing;
* reset the Rx and Tx FIFOs;
* empty the Rx buffer and clear any interrupt conditions;
* if enabling, try to turn them on.
*/
return;
}
static void
smcdtr(Uart*, int)
{
}
static void
smcrts(Uart*, int)
{
}
static void
smcmodemctl(Uart*, int)
{
}
static int
smcparity(Uart* uart, int parity)
{
int lcr;
SMC *sp;
sp = ((UartData*)uart->regs)->smc;
lcr = sp->smcmr & ~(Pen|Pm);
/* Disable transmitter/receiver. */
sp->smcmr &= ~(Ren | Ten);
switch(parity){
case 'e':
lcr |= (Pen|Pm);
break;
case 'o':
lcr |= Pen;
break;
case 'n':
default:
break;
}
/* Set new value and reenable if device was previously enabled */
sp->smcmr = lcr;
uart->parity = parity;
return 0;
}
static int
smcstop(Uart* uart, int stop)
{
int lcr, bits;
SMC *sp;
sp = ((UartData*)uart->regs)->smc;
lcr = sp->smcmr & ~(Sl | Clen);
/* Disable transmitter/receiver. */
sp->smcmr &= ~(Ren | Ten);
switch(stop){
case 1:
break;
case 2:
lcr |= Sl;
break;
default:
return -1;
}
bits = uart->bits + ((lcr & Pen) ? 1 : 0) + ((lcr & Sl) ? 2 : 1);
lcr |= bits<<11;
/* Set new value and reenable if device was previously enabled */
sp->smcmr = lcr;
uart->stop = stop;
return 0;
}
static int
smcbits(Uart* uart, int bits)
{
int lcr, b;
SMC *sp;
if (bits < 5 || bits > 14)
return -1;
sp = ((UartData*)uart->regs)->smc;
lcr = sp->smcmr & ~Clen;
b = bits + ((sp->smcmr & Pen) ? 1 : 0) + ((sp->smcmr & Sl) ? 2 : 1);
if (b > 15)
return -1;
/* Disable transmitter/receiver */
sp->smcmr &= ~(Ren | Ten);
/* Set new value and reenable if device was previously enabled */
sp->smcmr = lcr | b<<11;
uart->bits = bits;
return 0;
}
static int
smcbaud(Uart* uart, int baud)
{
int i;
SMC *sp;
if (uart->enabled){
sp = ((UartData*)uart->regs)->smc;
if(uart->freq == 0 || baud <= 0)
return -1;
i = sp - imm->smc;
imm->brgc[i] = (((m->brghz >> 4) / baud) << 1) | 0x00010000;
}
uart->baud = baud;
return 0;
}
static void
smcbreak(Uart*, int)
{
}
static void
smckick(Uart *uart)
{
BD *txb;
UartData *ud;
int i;
if(uart->blocked)
return;
ud = uart->regs;
txb = ud->txb;
if (txb->status & BDReady)
return; /* Still busy */
for(i = 0; i < Txsize; i++){
if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
break;
ud->txbuf[i] = *(uart->op++);
}
if (i == 0)
return;
dcflush(ud->txbuf, Txsize);
txb->length = i;
sync();
txb->status |= BDReady|BDInt;
}
static void
smcinterrupt(Ureg*, void* u)
{
int i, nc;
char *buf;
BD *rxb;
UartData *ud;
Uart *uart;
uchar events;
uart = u;
if (uart == nil)
panic("uart is nil");
ud = uart->regs;
if (ud == nil)
panic("ud is nil");
events = ud->smc->smce;
ud->smc->smce = events; /* Clear events */
if (events & 0x10)
iprint("smc%d: break\n", ud->smcno);
if (events & 0x4)
uart->oerr++;
if (events & 0x1){
/* Receive characters
*/
rxb = ud->rxb;
buf = ud->rxbuf;
dczap(buf, Rxsize); /* invalidate data cache before copying */
if ((rxb->status & BDEmpty) == 0){
nc = rxb->length;
for (i=0; i<nc; i++)
uartrecv(uart, *buf++);
sync();
rxb->status |= BDEmpty;
}else{
iprint("uartsmc: unexpected receive event\n");
}
}
if (events & 0x2){
if ((ud->txb->status & BDReady) == 0)
uartkick(uart);
}
}
static void
smcdisable(Uart* uart)
{
SMC *sp;
sp = ((UartData*)uart->regs)->smc;
sp->smcmr &= ~(Ren | Ten);
}
static int
getchars(Uart *uart, uchar *cbuf)
{
int i, nc;
char *buf;
BD *rxb;
UartData *ud;
ud = uart->regs;
rxb = ud->rxb;
/* Wait for character to show up.
*/
buf = ud->rxbuf;
while (rxb->status & BDEmpty)
;
nc = rxb->length;
for (i=0; i<nc; i++)
*cbuf++ = *buf++;
sync();
rxb->status |= BDEmpty;
return(nc);
}
static int
smcgetc(Uart *uart)
{
static uchar buf[128], *p;
static int cnt;
char c;
if (cnt <= 0) {
cnt = getchars(uart, buf);
p = buf;
}
c = *p++;
cnt--;
return(c);
}
static void
smcputc(Uart *uart, int c)
{
BD *txb;
UartData *ud;
SMC *smc;
ud = uart->regs;
txb = ud->txb;
smc = ud->smc;
smc->smcm = 0;
/* Wait for last character to go.
*/
while (txb->status & BDReady)
;
ud->txbuf[0] = c;
dcflush(ud->txbuf, 1);
txb->length = 1;
sync();
txb->status |= BDReady;
while (txb->status & BDReady)
;
}
PhysUart smcphysuart = {
.name = "smc",
.pnp = smcpnp,
.enable = smcenable,
.disable = smcdisable,
.kick = smckick,
.dobreak = smcbreak,
.baud = smcbaud,
.bits = smcbits,
.stop = smcstop,
.parity = smcparity,
.modemctl = smcmodemctl,
.rts = smcrts,
.dtr = smcdtr,
.status = smcstatus,
.fifo = smcfifo,
.getc = smcgetc,
.putc = smcputc,
};
void
console(void)
{
Uart *uart;
int n;
char *cmd, *p;
if((p = getconf("console")) == nil)
return;
n = strtoul(p, &cmd, 0);
if(p == cmd)
return;
if(n < 0 || n >= nelem(smcuart))
return;
uart = smcuart + n;
/* uartctl(uart, "b115200 l8 pn s1"); */
if(*cmd != '\0')
uartctl(uart, cmd);
(*uart->phys->enable)(uart, 0);
consuart = uart;
uart->console = 1;
}