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

473 lines
7 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "msaturn.h"
enum{
UartAoffs = Saturn + 0x0a00,
UartBoffs = Saturn + 0x0b00,
Nuart = 2,
Baudfreq = 14745600 / 16,
Lcr_div = RBIT(1, uchar),
Lcr_peven = RBIT(3, uchar),
Lcr_pen = RBIT(4, uchar),
Lcr_stop = RBIT(5, uchar),
Lcr_wrdlenmask = RBIT(6, uchar) | RBIT(7, uchar),
Lcr_wrdlenshift = 0,
Lsr_tbre = RBIT(2, uchar),
Fcr_txreset = RBIT(5, uchar),
Fcr_rxreset = RBIT(6, uchar),
Iir_txempty = RBIT(5, uchar),
Iir_rxfull = RBIT(6, uchar),
Iir_rxerr = RBIT(7, uchar),
Ier_rxerr = RBIT(5, uchar),
Ier_txempty = RBIT(6, uchar),
Ier_rxfull = RBIT(7, uchar),
Lsr_rxavail = RBIT(7, uchar),
Txsize = 16,
Rxsize = 16,
};
typedef struct Saturnuart Saturnuart;
struct Saturnuart {
uchar rxb;
#define txb rxb
#define dll rxb
uchar ier; // Interrupt enable, divisor latch
#define dlm ier
uchar iir; // Interrupt identification, fifo control
#define fcr iir
uchar lcr; // Line control register
uchar f1;
uchar lsr; // Line status register
ushort f2;
};
typedef struct UartData UartData;
struct UartData {
int suno; /* saturn uart number: 0 or 1 */
Saturnuart *su;
char *rxbuf;
char *txbuf;
int initialized;
int enabled;
} uartdata[Nuart];
extern PhysUart saturnphysuart;
Uart suart[Nuart] = {
{
.name = "SaturnUart1",
.baud = 19200,
.bits = 8,
.stop = 1,
.parity = 'n',
.phys = &saturnphysuart,
.special = 0,
},
{
.name = "SaturnUart2",
.baud = 115200,
.bits = 8,
.stop = 1,
.parity = 'n',
.phys = &saturnphysuart,
.special = 0,
},
};
static void suinterrupt(Ureg*, void*);
static Uart*
supnp(void)
{
int i;
for (i = 0; i < nelem(suart)-1; i++)
suart[i].next = &suart[i + 1];
suart[nelem(suart)-1].next=nil;
return suart;
}
static void
suinit(Uart*uart)
{
UartData *ud;
Saturnuart *su;
ud = uart->regs;
su = ud->su;
su->fcr=Fcr_txreset|Fcr_rxreset;
ud->initialized=1;
}
static void
suenable(Uart*uart, int ie)
{
Saturnuart *su;
UartData *ud;
int nr;
nr = uart - suart;
if (nr < 0 || nr > Nuart)
panic("No uart %d", nr);
ud = uartdata + nr;
ud->suno = nr;
su=ud->su = (Saturnuart*)((nr == 0)? UartAoffs: UartBoffs);
uart->regs = ud;
if(ud->initialized==0)
suinit(uart);
if(!ud->enabled && ie){
intrenable(Vecuart0+nr , suinterrupt, uart, uart->name);
su->ier=Ier_txempty|Ier_rxfull;
ud->enabled=1;
}
}
static long
sustatus(Uart* uart, void* buf, long n, long offset)
{
Saturnuart *su;
char p[128];
su = ((UartData*)uart->regs)->su;
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,
Txsize,
(su->lcr & Lcr_pen)? ((su->lcr & Lcr_peven) ? 'e': 'o'): 'n',
(su->lcr & Lcr_stop)? 2: 1,
uart->dev,
uart->type,
uart->ferr,
uart->oerr);
n = readstr(offset, buf, n, p);
free(p);
return n;
}
static void
sufifo(Uart*, int)
{}
static void
sudtr(Uart*, int)
{}
static void
surts(Uart*, int)
{}
static void
sumodemctl(Uart*, int)
{}
static int
suparity(Uart*uart, int parity)
{
int lcr;
Saturnuart *su;
su = ((UartData*)uart->regs)->su;
lcr = su->lcr & ~(Lcr_pen|Lcr_peven);
switch(parity){
case 'e':
lcr |= (Lcr_pen|Lcr_peven);
break;
case 'o':
lcr |= Lcr_pen;
break;
case 'n':
default:
break;
}
su->lcr = lcr;
uart->parity = parity;
return 0;
}
static int
sustop(Uart* uart, int stop)
{
int lcr;
Saturnuart *su;
su = ((UartData*)uart->regs)->su;
lcr = su->lcr & ~Lcr_stop;
switch(stop){
case 1:
break;
case 2:
lcr |= Lcr_stop;
break;
default:
return -1;
}
/* Set new value and reenable if device was previously enabled */
su->lcr = lcr;
uart->stop = stop;
return 0;
}
static int
subits(Uart*uart, int n)
{
Saturnuart *su;
uchar lcr;
su = ((UartData*)uart->regs)->su;
if(n<5||n>8)
return -1;
lcr = su->lcr & ~Lcr_wrdlenmask;
lcr |= (n-5) << Lcr_wrdlenshift;
su->lcr = lcr;
return 0;
}
static int
subaud(Uart* uart, int baud)
{
ushort v;
Saturnuart *su;
if (uart->enabled){
su = ((UartData*)uart->regs)->su;
if(baud <= 0)
return -1;
v = Baudfreq / baud;
su->lcr |= Lcr_div;
su->dll = v;
su->dlm = v >> 8;
su->lcr &= ~Lcr_div;
}
uart->baud = baud;
return 0;
}
static void
subreak(Uart*, int)
{}
static void
sukick(Uart *uart)
{
Saturnuart *su;
int i;
if(uart->blocked)
return;
su = ((UartData*)uart->regs)->su;
if((su->iir & Iir_txempty) == 0)
return;
for(i = 0; i < Txsize; i++){
if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
break;
su->txb = *(uart->op++);
su->ier |= Ier_txempty;
break;
}
}
static void
suputc(Uart *uart, int c)
{
Saturnuart *su;
su = ((UartData*)uart->regs)->su;
while((su->lsr&Lsr_tbre) == 0)
;
su->txb=c;
while((su->lsr&Lsr_tbre) == 0)
;
}
static int
getchars(Uart *uart, uchar *cbuf)
{
int nc;
UartData *ud;
Saturnuart *su;
ud = uart->regs;
su = ud->su;
while((su->lsr&Lsr_rxavail) == 0)
;
*cbuf++ = su->rxb;
nc = 1;
while(su->lsr&Lsr_rxavail){
*cbuf++ = su->rxb;
nc++;
}
return nc;
}
static int
sugetc(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
suinterrupt(Ureg*, void*u)
{
Saturnuart *su;
Uart *uart;
uchar iir;
uart = u;
if (uart == nil)
panic("uart is nil");
su = ((UartData*)uart->regs)->su;
iir = su->iir;
if(iir&Iir_rxfull)
while(su->lsr&Lsr_rxavail)
uartrecv(uart, su->rxb);
if(iir & Iir_txempty){
su->ier&=~Ier_txempty;
uartkick(uart);
}
if (iir & Iir_rxerr)
uart->oerr++;
intack();
}
static void
sudisable(Uart* uart)
{
Saturnuart *su;
su = ((UartData*)uart->regs)->su;
su->ier&=~(Ier_txempty|Ier_rxfull);
}
PhysUart saturnphysuart = {
.name = "su",
.pnp = supnp,
.enable = suenable,
.disable = sudisable,
.kick = sukick,
.dobreak = subreak,
.baud = subaud,
.bits = subits,
.stop = sustop,
.parity = suparity,
.modemctl = sumodemctl,
.rts = surts,
.dtr = sudtr,
.status = sustatus,
.fifo = sufifo,
.getc = sugetc,
.putc = suputc,
};
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(suart))
return;
uart = suart + n;
/* uartctl(uart, "b115200 l8 pn s1"); */
if(*cmd != '\0')
uartctl(uart, cmd);
(*uart->phys->enable)(uart, 0);
consuart = uart;
uart->console = 1;
}
Saturnuart*uart = (Saturnuart*)UartAoffs;
void
dbgputc(int c)
{
while((uart->lsr&Lsr_tbre) == 0)
;
uart->txb=c;
while((uart->lsr&Lsr_tbre) == 0)
;
}
void
dbgputs(char*s)
{
while(*s)
dbgputc(*s++);
}
void
dbgputx(ulong x)
{
int i;
char c;
for(i=0; i < sizeof(ulong) * 2; i++){
c = ((x >> (28 - i * 4))) & 0xf;
if(c >= 0 && c <= 9)
c += '0';
else
c += 'a' - 10;
while((uart->lsr&Lsr_tbre) == 0)
;
uart->txb=c;
}
while((uart->lsr&Lsr_tbre) == 0)
;
uart->txb='\n';
while((uart->lsr&Lsr_tbre) == 0)
;
}