585 lines
9.5 KiB
C
585 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;
|
|
}
|