xref: /plan9/sys/src/9/ppc/uartsmc.c (revision 5d9de2d38d2503efca29e12e0e32036368a7a75f)
1458db832SDavid du Colombier #include "u.h"
2458db832SDavid du Colombier #include "../port/lib.h"
3458db832SDavid du Colombier #include "mem.h"
4458db832SDavid du Colombier #include "dat.h"
5458db832SDavid du Colombier #include "fns.h"
6458db832SDavid du Colombier #include "io.h"
74de34a7eSDavid du Colombier #include "imm.h"
8458db832SDavid du Colombier #include "../port/error.h"
94de34a7eSDavid du Colombier #include "../ppc/uartsmc.h"
10458db832SDavid du Colombier 
11458db832SDavid du Colombier /*
12458db832SDavid du Colombier  * PowerPC 8260 SMC UART
13458db832SDavid du Colombier  */
14458db832SDavid du Colombier 
15458db832SDavid du Colombier enum {
16458db832SDavid du Colombier 	/* SMC Mode Registers */
17458db832SDavid du Colombier 	Clen		= 0x7800,	/* Character length */
18458db832SDavid du Colombier 	Sl		= 0x0400,	/* Stop length, 0: one stop bit, 1: two */
19458db832SDavid du Colombier 	Pen		= 0x0200,	/* Parity enable */
20458db832SDavid du Colombier 	Pm		= 0x0100,	/* Parity mode, 0 is odd */
21458db832SDavid du Colombier 	Sm		= 0x0030,	/* SMC mode, two bits */
22458db832SDavid du Colombier 	SMUart	= 0x0020,	/* SMC mode, 0b10 is uart */
23458db832SDavid du Colombier 	Dm		= 0x000c,	/* Diagnostic mode, 00 is normal */
24458db832SDavid du Colombier 	Ten		= 0x0002,	/* Transmit enable, 1 is enabled */
25458db832SDavid du Colombier 	Ren		= 0x0001,	/* Receive enable, 1 is enabled */
26458db832SDavid du Colombier 
27458db832SDavid du Colombier 	/* SMC Event/Mask Registers */
28458db832SDavid du Colombier 	ce_Brke	= 0x0040,	/* Break end */
29458db832SDavid du Colombier 	ce_Br	= 0x0020,	/* Break character received */
30458db832SDavid du Colombier 	ce_Bsy	= 0x0004,	/* Busy condition */
31458db832SDavid du Colombier 	ce_Txb	= 0x0002,	/* Tx buffer */
32458db832SDavid du Colombier 	ce_Rxb	= 0x0001,	/* Rx buffer */
33458db832SDavid du Colombier 
34458db832SDavid du Colombier 	/* Receive/Transmit Buffer Descriptor Control bits */
35458db832SDavid du Colombier 	BDContin=	1<<9,
36458db832SDavid du Colombier 	BDIdle=		1<<8,
37458db832SDavid du Colombier 	BDPreamble=	1<<8,
38458db832SDavid du Colombier 	BDBreak=	1<<5,
39458db832SDavid du Colombier 	BDFrame=	1<<4,
40458db832SDavid du Colombier 	BDParity=	1<<3,
41458db832SDavid du Colombier 	BDOverrun=	1<<1,
42458db832SDavid du Colombier 
43458db832SDavid du Colombier 	/* Tx and Rx buffer sizes (32 bytes) */
44458db832SDavid du Colombier 	Rxsize=		CACHELINESZ,
45458db832SDavid du Colombier 	Txsize=		CACHELINESZ,
46458db832SDavid du Colombier };
47458db832SDavid du Colombier 
48458db832SDavid du Colombier extern PhysUart smcphysuart;
49458db832SDavid du Colombier 
50458db832SDavid du Colombier Uart smcuart[Nuart] = {
51458db832SDavid du Colombier 	{
52458db832SDavid du Colombier 		.name = "SMC1",
53458db832SDavid du Colombier 		.baud = 115200,
54458db832SDavid du Colombier 		.bits = 8,
55458db832SDavid du Colombier 		.stop = 1,
56458db832SDavid du Colombier 		.parity = 'n',
57458db832SDavid du Colombier 		.phys = &smcphysuart,
58458db832SDavid du Colombier 		.special = 0,
59458db832SDavid du Colombier 	},
60458db832SDavid du Colombier /*	Only configure SMC1 for now
61458db832SDavid du Colombier 	{
62458db832SDavid du Colombier 		.name = "SMC2",
63458db832SDavid du Colombier 		.baud = 115200,
64458db832SDavid du Colombier 		.bits = 8,
65458db832SDavid du Colombier 		.stop = 1,
66458db832SDavid du Colombier 		.parity = 'n',
67458db832SDavid du Colombier 		.phys = &smcphysuart,
68458db832SDavid du Colombier 		.special = 0,
69458db832SDavid du Colombier 	},
70458db832SDavid du Colombier */
71458db832SDavid du Colombier };
72458db832SDavid du Colombier 
73458db832SDavid du Colombier int uartinited = 0;
74458db832SDavid du Colombier 
75458db832SDavid du Colombier static void smcinterrupt(Ureg*, void*);
76458db832SDavid du Colombier static void smcputc(Uart *uart, int c);
77458db832SDavid du Colombier 
784de34a7eSDavid du Colombier int
baudgen(int baud)79458db832SDavid du Colombier baudgen(int baud)
80458db832SDavid du Colombier {
81458db832SDavid du Colombier 	int d;
82458db832SDavid du Colombier 
83458db832SDavid du Colombier 	d = ((m->brghz+(baud>>1))/baud)>>4;
84458db832SDavid du Colombier 	if(d >= (1<<12))
85458db832SDavid du Colombier 		return ((d+15)>>3)|1;
86458db832SDavid du Colombier 	return d<<1;
87458db832SDavid du Colombier }
88458db832SDavid du Colombier 
89458db832SDavid du Colombier static Uart*
smcpnp(void)90458db832SDavid du Colombier smcpnp(void)
91458db832SDavid du Colombier {
92458db832SDavid du Colombier 	int i;
93458db832SDavid du Colombier 
94458db832SDavid du Colombier 	for (i = 0; i < nelem(smcuart) - 1; i++)
95458db832SDavid du Colombier 		smcuart[i].next = smcuart + i + 1;
96458db832SDavid du Colombier 	return smcuart;
97458db832SDavid du Colombier }
98458db832SDavid du Colombier 
994de34a7eSDavid du Colombier void
smcsetup(Uart * uart)100*5d9de2d3SDavid du Colombier smcsetup(Uart *uart)
101*5d9de2d3SDavid du Colombier {
102*5d9de2d3SDavid du Colombier 	Uartsmc *p;
103*5d9de2d3SDavid du Colombier 	SMC *smc;
104*5d9de2d3SDavid du Colombier 	UartData *ud;
105*5d9de2d3SDavid du Colombier 
106*5d9de2d3SDavid du Colombier 	ud = uart->regs;
107*5d9de2d3SDavid du Colombier 
108*5d9de2d3SDavid du Colombier 	/* magic addresses */
109*5d9de2d3SDavid du Colombier 
110*5d9de2d3SDavid du Colombier 	p = &m->immr->uartsmc[ud->smcno];
111*5d9de2d3SDavid du Colombier 	smc = imm->smc + ud->smcno;	/* SMC1 */
112*5d9de2d3SDavid du Colombier 	ud->smc = smc;
113*5d9de2d3SDavid du Colombier 	ud->usmc = p;
114*5d9de2d3SDavid du Colombier 
115*5d9de2d3SDavid du Colombier 	/* step 0: disable rx/tx */
116*5d9de2d3SDavid du Colombier 	smc->smcmr &= ~3;
117*5d9de2d3SDavid du Colombier 
118*5d9de2d3SDavid du Colombier 	ioplock();
119*5d9de2d3SDavid du Colombier 
120*5d9de2d3SDavid du Colombier 	/* step 1, Using Port D */
121*5d9de2d3SDavid du Colombier 	if (ud->smcno != 0)
122*5d9de2d3SDavid du Colombier 		panic("Don't know how to set Port D bits");
123*5d9de2d3SDavid du Colombier 	imm->port[SMC1PORT].ppar |= SMRXD1|SMTXD1;
124*5d9de2d3SDavid du Colombier 	imm->port[SMC1PORT].pdir |= SMTXD1;
125*5d9de2d3SDavid du Colombier 	imm->port[SMC1PORT].pdir &= ~SMRXD1;
126*5d9de2d3SDavid du Colombier 	imm->port[SMC1PORT].psor &= ~(SMRXD1|SMTXD1);
127*5d9de2d3SDavid du Colombier 
128*5d9de2d3SDavid du Colombier 	/* step 2: set up brgc1 */
129*5d9de2d3SDavid du Colombier 	imm->brgc[ud->smcno]  = baudgen(uart->baud) | 0x10000;
130*5d9de2d3SDavid du Colombier 
131*5d9de2d3SDavid du Colombier 	/* step 3: route clock to SMC1 */
132*5d9de2d3SDavid du Colombier 	imm->cmxsmr &= (ud->smcno == 0) ? ~0xb0 : ~0xb;	/* clear smcx and smcxcs */
133*5d9de2d3SDavid du Colombier 
134*5d9de2d3SDavid du Colombier 	iopunlock();
135*5d9de2d3SDavid du Colombier 
136*5d9de2d3SDavid du Colombier 	/* step 4: assign a pointer to the SMCparameter RAM */
137*5d9de2d3SDavid du Colombier 	m->immr->param[ud->smcno].smcbase = (ulong)p - IMMR;
138*5d9de2d3SDavid du Colombier 
139*5d9de2d3SDavid du Colombier 	/* step 6: issue command to CP */
140*5d9de2d3SDavid du Colombier 	if (ud->smcno == 0)
141*5d9de2d3SDavid du Colombier 		cpmop(InitRxTx, SMC1ID, 0);
142*5d9de2d3SDavid du Colombier 	else
143*5d9de2d3SDavid du Colombier 		cpmop(InitRxTx, SMC2ID, 0);
144*5d9de2d3SDavid du Colombier 
145*5d9de2d3SDavid du Colombier 	/* step 7: protocol parameters */
146*5d9de2d3SDavid du Colombier 	p->rfcr = 0x30;
147*5d9de2d3SDavid du Colombier 	p->tfcr = 0x30;
148*5d9de2d3SDavid du Colombier }
149*5d9de2d3SDavid du Colombier 
150*5d9de2d3SDavid du Colombier void
smcinit(Uart * uart)151458db832SDavid du Colombier smcinit(Uart *uart)
152458db832SDavid du Colombier {
153458db832SDavid du Colombier 	Uartsmc *p;
154458db832SDavid du Colombier 	SMC *smc;
155458db832SDavid du Colombier 	UartData *ud;
156458db832SDavid du Colombier 	ulong lcr;
157458db832SDavid du Colombier 	int bits;
158458db832SDavid du Colombier 
159458db832SDavid du Colombier 	ud = uart->regs;
160458db832SDavid du Colombier 
161458db832SDavid du Colombier 	if (ud->initialized)
162458db832SDavid du Colombier 		return;
163458db832SDavid du Colombier 
1644de34a7eSDavid du Colombier 	smcsetup(uart);	/* Steps 1 through 4, PPC-dependent */
1654de34a7eSDavid du Colombier 	p = ud->usmc;
1664de34a7eSDavid du Colombier 	smc = ud->smc;
167458db832SDavid du Colombier 
1684de34a7eSDavid du Colombier 	/* step 5: set up buffer descriptors */
169458db832SDavid du Colombier 	/* setup my uart structure */
170458db832SDavid du Colombier 	if (ud->rxb == nil)
171458db832SDavid du Colombier 		ud->rxb = bdalloc(1);
172458db832SDavid du Colombier 	if (ud->txb == nil)
173458db832SDavid du Colombier 		ud->txb = bdalloc(1);
174458db832SDavid du Colombier 
1754de34a7eSDavid du Colombier 	p->rbase = ((ulong)ud->rxb) - (ulong)IMMR;
1764de34a7eSDavid du Colombier 	p->tbase = ((ulong)ud->txb) - (ulong)IMMR;
177458db832SDavid du Colombier 
178458db832SDavid du Colombier 	/* step 8: receive buffer size */
179458db832SDavid du Colombier 	p->mrblr = Rxsize;
180458db832SDavid du Colombier 
181458db832SDavid du Colombier 	/* step 9: */
182458db832SDavid du Colombier 	p->maxidl = 15;
183458db832SDavid du Colombier 
184458db832SDavid du Colombier 	/* step 10: */
185458db832SDavid du Colombier 	p->brkln = 0;
186458db832SDavid du Colombier 	p->brkec = 0;
187458db832SDavid du Colombier 
188458db832SDavid du Colombier 	/* step 11: */
189458db832SDavid du Colombier 	p->brkcr = 0;
190458db832SDavid du Colombier 
191458db832SDavid du Colombier 	/* step 12: setup receive buffer */
192458db832SDavid du Colombier 	ud->rxb->status = BDEmpty|BDWrap|BDInt;
193458db832SDavid du Colombier 	ud->rxb->length = 0;
194458db832SDavid du Colombier 	ud->rxbuf = xspanalloc(Rxsize, 0, CACHELINESZ);
195458db832SDavid du Colombier 	ud->rxb->addr = PADDR(ud->rxbuf);
196458db832SDavid du Colombier 
197458db832SDavid du Colombier 	/* step 13: step transmit buffer */
198458db832SDavid du Colombier 	ud->txb->status = BDWrap|BDInt;
199458db832SDavid du Colombier 	ud->txb->length = 0;
200458db832SDavid du Colombier 	ud->txbuf = xspanalloc(Txsize, 0, CACHELINESZ);
201458db832SDavid du Colombier 	ud->txb->addr = PADDR(ud->txbuf);
202458db832SDavid du Colombier 
203458db832SDavid du Colombier 	/* step 14: clear events */
204458db832SDavid du Colombier 	smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
205458db832SDavid du Colombier 
206458db832SDavid du Colombier 	/*
207458db832SDavid du Colombier 	 * step 15: enable interrupts (done later)
208458db832SDavid du Colombier 	 * smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
209458db832SDavid du Colombier 	 */
210458db832SDavid du Colombier 
211458db832SDavid du Colombier 	/* step 17: set parity, no of bits, UART mode, ... */
212458db832SDavid du Colombier 	lcr = SMUart;
213458db832SDavid du Colombier 	bits = uart->bits + 1;
214458db832SDavid du Colombier 
215458db832SDavid du Colombier 	switch(uart->parity){
216458db832SDavid du Colombier 	case 'e':
217458db832SDavid du Colombier 		lcr |= (Pen|Pm);
218458db832SDavid du Colombier 		bits +=1;
219458db832SDavid du Colombier 		break;
220458db832SDavid du Colombier 	case 'o':
221458db832SDavid du Colombier 		lcr |= Pen;
222458db832SDavid du Colombier 		bits +=1;
223458db832SDavid du Colombier 		break;
224458db832SDavid du Colombier 	case 'n':
225458db832SDavid du Colombier 	default:
226458db832SDavid du Colombier 		break;
227458db832SDavid du Colombier 	}
228458db832SDavid du Colombier 
229458db832SDavid du Colombier 	if(uart->stop == 2){
230458db832SDavid du Colombier 		lcr |= Sl;
231458db832SDavid du Colombier 		bits += 1;
232458db832SDavid du Colombier 	}
233458db832SDavid du Colombier 
234458db832SDavid du Colombier 	/* Set new value and reenable if device was previously enabled */
235458db832SDavid du Colombier 	smc->smcmr = lcr |  bits <<11 | 0x3;
236458db832SDavid du Colombier 
237458db832SDavid du Colombier 	ud->initialized = 1;
238458db832SDavid du Colombier }
239458db832SDavid du Colombier 
240458db832SDavid du Colombier static void
smcenable(Uart * uart,int intenb)241458db832SDavid du Colombier smcenable(Uart *uart, int intenb)
242458db832SDavid du Colombier {
243458db832SDavid du Colombier 	UartData *ud;
244458db832SDavid du Colombier 	SMC *smc;
245458db832SDavid du Colombier 	int nr;
246458db832SDavid du Colombier 
247458db832SDavid du Colombier 	nr = uart - smcuart;
248458db832SDavid du Colombier 	if (nr < 0 || nr > Nuart)
249458db832SDavid du Colombier 		panic("No SMC %d", nr);
250458db832SDavid du Colombier 	ud = uartdata + nr;
251458db832SDavid du Colombier 	ud->smcno = nr;
252458db832SDavid du Colombier 	uart->regs = ud;
253458db832SDavid du Colombier 	if (ud->initialized == 0)
254458db832SDavid du Colombier 		smcinit(uart);
255458db832SDavid du Colombier 	if (ud->enabled || intenb == 0)
256458db832SDavid du Colombier 		return;
257458db832SDavid du Colombier 	smc = ud->smc;
258458db832SDavid du Colombier 	/* clear events */
259458db832SDavid du Colombier 	smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
260458db832SDavid du Colombier 	/* enable interrupts */
261458db832SDavid du Colombier 	smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
2624de34a7eSDavid du Colombier 	intrenable(VecSMC1 + ud->smcno, smcinterrupt, uart, uart->name);
263458db832SDavid du Colombier 	ud->enabled = 1;
264458db832SDavid du Colombier }
265458db832SDavid du Colombier 
266458db832SDavid du Colombier static long
smcstatus(Uart * uart,void * buf,long n,long offset)267458db832SDavid du Colombier smcstatus(Uart* uart, void* buf, long n, long offset)
268458db832SDavid du Colombier {
269458db832SDavid du Colombier 	SMC *sp;
270458db832SDavid du Colombier 	char p[128];
271458db832SDavid du Colombier 
272458db832SDavid du Colombier 	sp = ((UartData*)uart->regs)->smc;
273458db832SDavid du Colombier 	snprint(p, sizeof p, "b%d c%d e%d l%d m0 p%c s%d i1\n"
274458db832SDavid du Colombier 		"dev(%d) type(%d) framing(%d) overruns(%d)\n",
275458db832SDavid du Colombier 
276458db832SDavid du Colombier 		uart->baud,
277458db832SDavid du Colombier 		uart->hup_dcd,
278458db832SDavid du Colombier 		uart->hup_dsr,
279458db832SDavid du Colombier 		((sp->smcmr & Clen) >>11) - ((sp->smcmr&Pen) ? 1 : 0) - ((sp->smcmr&Sl) ? 2 : 1),
280458db832SDavid du Colombier 		(sp->smcmr & Pen) ? ((sp->smcmr & Pm) ? 'e': 'o'): 'n',
281458db832SDavid du Colombier 		(sp->smcmr & Sl) ? 2: 1,
282458db832SDavid du Colombier 
283458db832SDavid du Colombier 		uart->dev,
284458db832SDavid du Colombier 		uart->type,
285458db832SDavid du Colombier 		uart->ferr,
286458db832SDavid du Colombier 		uart->oerr
287458db832SDavid du Colombier 	);
288458db832SDavid du Colombier 	n = readstr(offset, buf, n, p);
289458db832SDavid du Colombier 	free(p);
290458db832SDavid du Colombier 
291458db832SDavid du Colombier 	return n;
292458db832SDavid du Colombier }
293458db832SDavid du Colombier 
294458db832SDavid du Colombier static void
smcfifo(Uart *,int)295458db832SDavid du Colombier smcfifo(Uart*, int)
296458db832SDavid du Colombier {
297458db832SDavid du Colombier 	/*
298458db832SDavid du Colombier 	 * Toggle FIFOs:
299458db832SDavid du Colombier 	 * if none, do nothing;
300458db832SDavid du Colombier 	 * reset the Rx and Tx FIFOs;
301458db832SDavid du Colombier 	 * empty the Rx buffer and clear any interrupt conditions;
302458db832SDavid du Colombier 	 * if enabling, try to turn them on.
303458db832SDavid du Colombier 	 */
304458db832SDavid du Colombier 	return;
305458db832SDavid du Colombier }
306458db832SDavid du Colombier 
307458db832SDavid du Colombier static void
smcdtr(Uart *,int)308458db832SDavid du Colombier smcdtr(Uart*, int)
309458db832SDavid du Colombier {
310458db832SDavid du Colombier }
311458db832SDavid du Colombier 
312458db832SDavid du Colombier static void
smcrts(Uart *,int)313458db832SDavid du Colombier smcrts(Uart*, int)
314458db832SDavid du Colombier {
315458db832SDavid du Colombier }
316458db832SDavid du Colombier 
317458db832SDavid du Colombier static void
smcmodemctl(Uart *,int)318458db832SDavid du Colombier smcmodemctl(Uart*, int)
319458db832SDavid du Colombier {
320458db832SDavid du Colombier }
321458db832SDavid du Colombier 
322458db832SDavid du Colombier static int
smcparity(Uart * uart,int parity)323458db832SDavid du Colombier smcparity(Uart* uart, int parity)
324458db832SDavid du Colombier {
325458db832SDavid du Colombier 	int lcr;
326458db832SDavid du Colombier 	SMC *sp;
327458db832SDavid du Colombier 
328458db832SDavid du Colombier 	sp = ((UartData*)uart->regs)->smc;
329458db832SDavid du Colombier 
330458db832SDavid du Colombier 	lcr = sp->smcmr & ~(Pen|Pm);
331458db832SDavid du Colombier 
332458db832SDavid du Colombier 	/* Disable transmitter/receiver. */
333458db832SDavid du Colombier 	sp->smcmr &= ~(Ren | Ten);
334458db832SDavid du Colombier 
335458db832SDavid du Colombier 	switch(parity){
336458db832SDavid du Colombier 	case 'e':
337458db832SDavid du Colombier 		lcr |= (Pen|Pm);
338458db832SDavid du Colombier 		break;
339458db832SDavid du Colombier 	case 'o':
340458db832SDavid du Colombier 		lcr |= Pen;
341458db832SDavid du Colombier 		break;
342458db832SDavid du Colombier 	case 'n':
343458db832SDavid du Colombier 	default:
344458db832SDavid du Colombier 		break;
345458db832SDavid du Colombier 	}
346458db832SDavid du Colombier 	/* Set new value and reenable if device was previously enabled */
347458db832SDavid du Colombier 	sp->smcmr = lcr;
348458db832SDavid du Colombier 
349458db832SDavid du Colombier 	uart->parity = parity;
350458db832SDavid du Colombier 
351458db832SDavid du Colombier 	return 0;
352458db832SDavid du Colombier }
353458db832SDavid du Colombier 
354458db832SDavid du Colombier static int
smcstop(Uart * uart,int stop)355458db832SDavid du Colombier smcstop(Uart* uart, int stop)
356458db832SDavid du Colombier {
357458db832SDavid du Colombier 	int lcr, bits;
358458db832SDavid du Colombier 	SMC *sp;
359458db832SDavid du Colombier 
360458db832SDavid du Colombier 	sp = ((UartData*)uart->regs)->smc;
361458db832SDavid du Colombier 	lcr = sp->smcmr & ~(Sl | Clen);
362458db832SDavid du Colombier 
363458db832SDavid du Colombier 	/* Disable transmitter/receiver. */
364458db832SDavid du Colombier 	sp->smcmr &= ~(Ren | Ten);
365458db832SDavid du Colombier 
366458db832SDavid du Colombier 	switch(stop){
367458db832SDavid du Colombier 	case 1:
368458db832SDavid du Colombier 		break;
369458db832SDavid du Colombier 	case 2:
370458db832SDavid du Colombier 		lcr |= Sl;
371458db832SDavid du Colombier 		break;
372458db832SDavid du Colombier 	default:
373458db832SDavid du Colombier 		return -1;
374458db832SDavid du Colombier 	}
375458db832SDavid du Colombier 
376458db832SDavid du Colombier 	bits = uart->bits + ((lcr & Pen) ? 1 : 0) + ((lcr & Sl) ? 2 : 1);
377458db832SDavid du Colombier 	lcr |= bits<<11;
378458db832SDavid du Colombier 
379458db832SDavid du Colombier 	/* Set new value and reenable if device was previously enabled */
380458db832SDavid du Colombier 	sp->smcmr = lcr;
381458db832SDavid du Colombier 
382458db832SDavid du Colombier 	uart->stop = stop;
383458db832SDavid du Colombier 
384458db832SDavid du Colombier 	return 0;
385458db832SDavid du Colombier }
386458db832SDavid du Colombier 
387458db832SDavid du Colombier static int
smcbits(Uart * uart,int bits)388458db832SDavid du Colombier smcbits(Uart* uart, int bits)
389458db832SDavid du Colombier {
390458db832SDavid du Colombier 	int lcr, b;
391458db832SDavid du Colombier 	SMC *sp;
392458db832SDavid du Colombier 
393458db832SDavid du Colombier 	if (bits < 5 || bits > 14)
394458db832SDavid du Colombier 		return -1;
395458db832SDavid du Colombier 
396458db832SDavid du Colombier 	sp = ((UartData*)uart->regs)->smc;
397458db832SDavid du Colombier 	lcr = sp->smcmr & ~Clen;
398458db832SDavid du Colombier 
399458db832SDavid du Colombier 	b = bits + ((sp->smcmr & Pen) ? 1 : 0) + ((sp->smcmr & Sl) ? 2 : 1);
400458db832SDavid du Colombier 
401458db832SDavid du Colombier 	if (b > 15)
402458db832SDavid du Colombier 		return -1;
403458db832SDavid du Colombier 
404458db832SDavid du Colombier 	/* Disable transmitter/receiver */
405458db832SDavid du Colombier 	sp->smcmr &= ~(Ren | Ten);
406458db832SDavid du Colombier 
407458db832SDavid du Colombier 	/* Set new value and reenable if device was previously enabled */
408458db832SDavid du Colombier 	sp->smcmr = lcr |  b<<11;
409458db832SDavid du Colombier 
410458db832SDavid du Colombier 	uart->bits = bits;
411458db832SDavid du Colombier 
412458db832SDavid du Colombier 	return 0;
413458db832SDavid du Colombier }
414458db832SDavid du Colombier 
415458db832SDavid du Colombier static int
smcbaud(Uart * uart,int baud)416458db832SDavid du Colombier smcbaud(Uart* uart, int baud)
417458db832SDavid du Colombier {
418458db832SDavid du Colombier 	int i;
419458db832SDavid du Colombier 	SMC *sp;
420458db832SDavid du Colombier 
421458db832SDavid du Colombier 	if (uart->enabled){
422458db832SDavid du Colombier 		sp = ((UartData*)uart->regs)->smc;
423458db832SDavid du Colombier 
424458db832SDavid du Colombier 		if(uart->freq == 0 || baud <= 0)
425458db832SDavid du Colombier 			return -1;
426458db832SDavid du Colombier 
4274de34a7eSDavid du Colombier 		i = sp - imm->smc;
4284de34a7eSDavid du Colombier 		imm->brgc[i] = (((m->brghz >> 4) / baud) << 1) | 0x00010000;
429458db832SDavid du Colombier 	}
430458db832SDavid du Colombier 	uart->baud = baud;
431458db832SDavid du Colombier 
432458db832SDavid du Colombier 	return 0;
433458db832SDavid du Colombier }
434458db832SDavid du Colombier 
435458db832SDavid du Colombier static void
smcbreak(Uart *,int)436458db832SDavid du Colombier smcbreak(Uart*, int)
437458db832SDavid du Colombier {
438458db832SDavid du Colombier }
439458db832SDavid du Colombier 
440458db832SDavid du Colombier static void
smckick(Uart * uart)441458db832SDavid du Colombier smckick(Uart *uart)
442458db832SDavid du Colombier {
443458db832SDavid du Colombier 	BD *txb;
444458db832SDavid du Colombier 	UartData *ud;
445458db832SDavid du Colombier 	int i;
446458db832SDavid du Colombier 
447458db832SDavid du Colombier 	if(uart->blocked)
448458db832SDavid du Colombier 		return;
449458db832SDavid du Colombier 
450458db832SDavid du Colombier 	ud = uart->regs;
451458db832SDavid du Colombier 	txb = ud->txb;
452458db832SDavid du Colombier 
453458db832SDavid du Colombier 	if (txb->status & BDReady)
454458db832SDavid du Colombier 		return;	/* Still busy */
455458db832SDavid du Colombier 
456458db832SDavid du Colombier 	for(i = 0; i < Txsize; i++){
457458db832SDavid du Colombier 		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
458458db832SDavid du Colombier 			break;
459458db832SDavid du Colombier 		ud->txbuf[i] = *(uart->op++);
460458db832SDavid du Colombier 	}
461458db832SDavid du Colombier 	if (i == 0)
462458db832SDavid du Colombier 		return;
463458db832SDavid du Colombier 	dcflush(ud->txbuf, Txsize);
464458db832SDavid du Colombier 	txb->length = i;
465458db832SDavid du Colombier 	sync();
466458db832SDavid du Colombier 	txb->status |= BDReady|BDInt;
467458db832SDavid du Colombier }
468458db832SDavid du Colombier 
469458db832SDavid du Colombier static void
smcinterrupt(Ureg *,void * u)470458db832SDavid du Colombier smcinterrupt(Ureg*, void* u)
471458db832SDavid du Colombier {
472458db832SDavid du Colombier 	int i, nc;
473458db832SDavid du Colombier 	char *buf;
474458db832SDavid du Colombier 	BD *rxb;
475458db832SDavid du Colombier 	UartData *ud;
476458db832SDavid du Colombier 	Uart *uart;
477458db832SDavid du Colombier 	uchar events;
478458db832SDavid du Colombier 
479458db832SDavid du Colombier 	uart = u;
480458db832SDavid du Colombier 	if (uart == nil)
481458db832SDavid du Colombier 		panic("uart is nil");
482458db832SDavid du Colombier 	ud = uart->regs;
483458db832SDavid du Colombier 	if (ud == nil)
484458db832SDavid du Colombier 		panic("ud is nil");
485458db832SDavid du Colombier 
486458db832SDavid du Colombier 	events = ud->smc->smce;
487458db832SDavid du Colombier 	ud->smc->smce = events;	/* Clear events */
488458db832SDavid du Colombier 
489458db832SDavid du Colombier 	if (events & 0x10)
490458db832SDavid du Colombier 		iprint("smc%d: break\n", ud->smcno);
491458db832SDavid du Colombier 	if (events & 0x4)
492458db832SDavid du Colombier 		uart->oerr++;
493458db832SDavid du Colombier 	if (events & 0x1){
494458db832SDavid du Colombier 		/* Receive characters
495458db832SDavid du Colombier 		*/
496458db832SDavid du Colombier 		rxb = ud->rxb;
497458db832SDavid du Colombier 		buf = ud->rxbuf;
498458db832SDavid du Colombier 		dczap(buf, Rxsize);	/* invalidate data cache before copying */
499458db832SDavid du Colombier 		if ((rxb->status & BDEmpty) == 0){
500458db832SDavid du Colombier 			nc = rxb->length;
501458db832SDavid du Colombier 			for (i=0; i<nc; i++)
502458db832SDavid du Colombier 				uartrecv(uart, *buf++);
503458db832SDavid du Colombier 			sync();
504458db832SDavid du Colombier 			rxb->status |= BDEmpty;
505458db832SDavid du Colombier 		}else{
506458db832SDavid du Colombier 			iprint("uartsmc: unexpected receive event\n");
507458db832SDavid du Colombier 		}
508458db832SDavid du Colombier 	}
509458db832SDavid du Colombier 	if (events & 0x2){
510458db832SDavid du Colombier 		if ((ud->txb->status & BDReady) == 0)
511458db832SDavid du Colombier 			uartkick(uart);
512458db832SDavid du Colombier 	}
513458db832SDavid du Colombier }
514458db832SDavid du Colombier 
515458db832SDavid du Colombier static void
smcdisable(Uart * uart)516458db832SDavid du Colombier smcdisable(Uart* uart)
517458db832SDavid du Colombier {
518458db832SDavid du Colombier 	SMC *sp;
519458db832SDavid du Colombier 
520458db832SDavid du Colombier 	sp = ((UartData*)uart->regs)->smc;
521458db832SDavid du Colombier 	sp->smcmr &= ~(Ren | Ten);
522458db832SDavid du Colombier }
523458db832SDavid du Colombier 
524458db832SDavid du Colombier static int
getchars(Uart * uart,uchar * cbuf)525458db832SDavid du Colombier getchars(Uart *uart, uchar *cbuf)
526458db832SDavid du Colombier {
527458db832SDavid du Colombier 	int i, nc;
528458db832SDavid du Colombier 	char *buf;
529458db832SDavid du Colombier 	BD *rxb;
530458db832SDavid du Colombier 	UartData *ud;
531458db832SDavid du Colombier 
532458db832SDavid du Colombier 	ud = uart->regs;
533458db832SDavid du Colombier 	rxb = ud->rxb;
534458db832SDavid du Colombier 
535458db832SDavid du Colombier 	/* Wait for character to show up.
536458db832SDavid du Colombier 	*/
537458db832SDavid du Colombier 	buf = ud->rxbuf;
538458db832SDavid du Colombier 	while (rxb->status & BDEmpty)
539458db832SDavid du Colombier 		;
540458db832SDavid du Colombier 	nc = rxb->length;
541458db832SDavid du Colombier 	for (i=0; i<nc; i++)
542458db832SDavid du Colombier 		*cbuf++ = *buf++;
543458db832SDavid du Colombier 	sync();
544458db832SDavid du Colombier 	rxb->status |= BDEmpty;
545458db832SDavid du Colombier 
546458db832SDavid du Colombier 	return(nc);
547458db832SDavid du Colombier }
548458db832SDavid du Colombier 
549458db832SDavid du Colombier static int
smcgetc(Uart * uart)550458db832SDavid du Colombier smcgetc(Uart *uart)
551458db832SDavid du Colombier {
552458db832SDavid du Colombier 	static uchar buf[128], *p;
553458db832SDavid du Colombier 	static int cnt;
554458db832SDavid du Colombier 	char	c;
555458db832SDavid du Colombier 
556458db832SDavid du Colombier 	if (cnt <= 0) {
557458db832SDavid du Colombier 		cnt = getchars(uart, buf);
558458db832SDavid du Colombier 		p = buf;
559458db832SDavid du Colombier 	}
560458db832SDavid du Colombier 	c = *p++;
561458db832SDavid du Colombier 	cnt--;
562458db832SDavid du Colombier 
563458db832SDavid du Colombier 	return(c);
564458db832SDavid du Colombier }
565458db832SDavid du Colombier 
566458db832SDavid du Colombier static void
smcputc(Uart * uart,int c)567458db832SDavid du Colombier smcputc(Uart *uart, int c)
568458db832SDavid du Colombier {
569458db832SDavid du Colombier 	BD *txb;
570458db832SDavid du Colombier 	UartData *ud;
571458db832SDavid du Colombier 	SMC *smc;
572458db832SDavid du Colombier 
573458db832SDavid du Colombier 	ud = uart->regs;
574458db832SDavid du Colombier 	txb = ud->txb;
575458db832SDavid du Colombier 	smc = ud->smc;
576458db832SDavid du Colombier 	smc->smcm = 0;
577458db832SDavid du Colombier 
578458db832SDavid du Colombier 	/* Wait for last character to go.
579458db832SDavid du Colombier 	*/
580458db832SDavid du Colombier 	while (txb->status & BDReady)
581458db832SDavid du Colombier 		;
582458db832SDavid du Colombier 
583458db832SDavid du Colombier 	ud->txbuf[0] = c;
584458db832SDavid du Colombier 	dcflush(ud->txbuf, 1);
585458db832SDavid du Colombier 	txb->length = 1;
586458db832SDavid du Colombier 	sync();
587458db832SDavid du Colombier 	txb->status |= BDReady;
588458db832SDavid du Colombier 
589458db832SDavid du Colombier 	while (txb->status & BDReady)
590458db832SDavid du Colombier 		;
591458db832SDavid du Colombier }
592458db832SDavid du Colombier 
593458db832SDavid du Colombier PhysUart smcphysuart = {
594458db832SDavid du Colombier 	.name		= "smc",
595458db832SDavid du Colombier 	.pnp			= smcpnp,
596458db832SDavid du Colombier 	.enable		= smcenable,
597458db832SDavid du Colombier 	.disable		= smcdisable,
598458db832SDavid du Colombier 	.kick			= smckick,
599458db832SDavid du Colombier 	.dobreak		= smcbreak,
600458db832SDavid du Colombier 	.baud		= smcbaud,
601458db832SDavid du Colombier 	.bits			= smcbits,
602458db832SDavid du Colombier 	.stop			= smcstop,
603458db832SDavid du Colombier 	.parity		= smcparity,
604458db832SDavid du Colombier 	.modemctl	= smcmodemctl,
605458db832SDavid du Colombier 	.rts			= smcrts,
606458db832SDavid du Colombier 	.dtr			= smcdtr,
607458db832SDavid du Colombier 	.status		= smcstatus,
608458db832SDavid du Colombier 	.fifo			= smcfifo,
609458db832SDavid du Colombier 	.getc			= smcgetc,
610458db832SDavid du Colombier 	.putc			= smcputc,
611458db832SDavid du Colombier };
612458db832SDavid du Colombier 
613458db832SDavid du Colombier void
console(void)614458db832SDavid du Colombier console(void)
615458db832SDavid du Colombier {
616458db832SDavid du Colombier 	Uart *uart;
617458db832SDavid du Colombier 	int n;
618458db832SDavid du Colombier 	char *cmd, *p;
619458db832SDavid du Colombier 
620458db832SDavid du Colombier 	if((p = getconf("console")) == nil)
621458db832SDavid du Colombier 		return;
622458db832SDavid du Colombier 	n = strtoul(p, &cmd, 0);
623458db832SDavid du Colombier 	if(p == cmd)
624458db832SDavid du Colombier 		return;
625458db832SDavid du Colombier 	if(n < 0 || n >= nelem(smcuart))
626458db832SDavid du Colombier 		return;
627458db832SDavid du Colombier 	uart = smcuart + n;
628458db832SDavid du Colombier 
629458db832SDavid du Colombier /*	uartctl(uart, "b115200 l8 pn s1"); */
630458db832SDavid du Colombier 	if(*cmd != '\0')
631458db832SDavid du Colombier 		uartctl(uart, cmd);
632458db832SDavid du Colombier 	(*uart->phys->enable)(uart, 0);
633458db832SDavid du Colombier 
634458db832SDavid du Colombier 	consuart = uart;
635458db832SDavid du Colombier 	uart->console = 1;
636458db832SDavid du Colombier }
637