ref: c33fc43408cb22d606d2f621c34a5748cb0e2bc5
dir: /sys/src/9/ppc/uartsmc.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;
}