xref: /inferno-os/os/boot/mpc/i2c.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1*74a4d8c2SCharles.Forsyth #include "boot.h"
2*74a4d8c2SCharles.Forsyth 
3*74a4d8c2SCharles.Forsyth /*
4*74a4d8c2SCharles.Forsyth  * basic read/write interface to mpc8xx I2C bus (master mode)
5*74a4d8c2SCharles.Forsyth  */
6*74a4d8c2SCharles.Forsyth 
7*74a4d8c2SCharles.Forsyth typedef struct I2C I2C;
8*74a4d8c2SCharles.Forsyth 
9*74a4d8c2SCharles.Forsyth struct I2C {
10*74a4d8c2SCharles.Forsyth 	uchar	i2mod;
11*74a4d8c2SCharles.Forsyth 	uchar	rsv12a[3];
12*74a4d8c2SCharles.Forsyth 	uchar	i2add;
13*74a4d8c2SCharles.Forsyth 	uchar	rsv12b[3];
14*74a4d8c2SCharles.Forsyth 	uchar	i2brg;
15*74a4d8c2SCharles.Forsyth 	uchar	rsv12c[3];
16*74a4d8c2SCharles.Forsyth 	uchar	i2com;
17*74a4d8c2SCharles.Forsyth 	uchar	rsv12d[3];
18*74a4d8c2SCharles.Forsyth 	uchar	i2cer;
19*74a4d8c2SCharles.Forsyth 	uchar	rsv12e[3];
20*74a4d8c2SCharles.Forsyth 	uchar	i2cmr;
21*74a4d8c2SCharles.Forsyth };
22*74a4d8c2SCharles.Forsyth 
23*74a4d8c2SCharles.Forsyth enum {
24*74a4d8c2SCharles.Forsyth 	/* i2c-specific BD flags */
25*74a4d8c2SCharles.Forsyth 	RxeOV=		1<<1,	/* overrun */
26*74a4d8c2SCharles.Forsyth 	TxS=			1<<10,	/* transmit start condition */
27*74a4d8c2SCharles.Forsyth 	TxeNAK=		1<<2,	/* last transmitted byte not acknowledged */
28*74a4d8c2SCharles.Forsyth 	TxeUN=		1<<1,	/* underflow */
29*74a4d8c2SCharles.Forsyth 	TxeCL=		1<<0,	/* collision */
30*74a4d8c2SCharles.Forsyth 	TxERR=		(TxeNAK|TxeUN|TxeCL),
31*74a4d8c2SCharles.Forsyth 
32*74a4d8c2SCharles.Forsyth 	/* i2cmod */
33*74a4d8c2SCharles.Forsyth 	REVD=	1<<5,	/* =1, LSB first */
34*74a4d8c2SCharles.Forsyth 	GCD=	1<<4,	/* =1, general call address disabled */
35*74a4d8c2SCharles.Forsyth 	FLT=		1<<3,	/* =0, not filtered; =1, filtered */
36*74a4d8c2SCharles.Forsyth 	PDIV=	3<<1,	/* predivisor field */
37*74a4d8c2SCharles.Forsyth 	EN=		1<<0,	/* enable */
38*74a4d8c2SCharles.Forsyth 
39*74a4d8c2SCharles.Forsyth 	/* i2com */
40*74a4d8c2SCharles.Forsyth 	STR=		1<<7,	/* start transmit */
41*74a4d8c2SCharles.Forsyth 	I2CM=	1<<0,	/* master */
42*74a4d8c2SCharles.Forsyth 	I2CS=	0<<0,	/* slave */
43*74a4d8c2SCharles.Forsyth 
44*74a4d8c2SCharles.Forsyth 	/* i2cer */
45*74a4d8c2SCharles.Forsyth 	TXE =	1<<4,
46*74a4d8c2SCharles.Forsyth 	BSY =	1<<2,
47*74a4d8c2SCharles.Forsyth 	TXB =	1<<1,
48*74a4d8c2SCharles.Forsyth 	RXB =	1<<0,
49*74a4d8c2SCharles.Forsyth 
50*74a4d8c2SCharles.Forsyth 	/* port B bits */
51*74a4d8c2SCharles.Forsyth 	I2CSDA =	IBIT(27),
52*74a4d8c2SCharles.Forsyth 	I2CSCL = IBIT(26),
53*74a4d8c2SCharles.Forsyth 
54*74a4d8c2SCharles.Forsyth 	Rbit =	1<<0,	/* bit in address byte denoting read */
55*74a4d8c2SCharles.Forsyth 
56*74a4d8c2SCharles.Forsyth 	/* maximum I2C I/O (can change) */
57*74a4d8c2SCharles.Forsyth 	Bufsize =	64,
58*74a4d8c2SCharles.Forsyth 	Tbuflen=	Bufsize+4,	/* extra address bytes and alignment */
59*74a4d8c2SCharles.Forsyth 	Freq =	100000,
60*74a4d8c2SCharles.Forsyth 	I2CTimeout = 250,	/* msec */
61*74a4d8c2SCharles.Forsyth };
62*74a4d8c2SCharles.Forsyth 
63*74a4d8c2SCharles.Forsyth /* data cache needn't be flushed if buffers allocated in uncached INTMEM */
64*74a4d8c2SCharles.Forsyth #define	DCFLUSH(a,n)
65*74a4d8c2SCharles.Forsyth 
66*74a4d8c2SCharles.Forsyth /*
67*74a4d8c2SCharles.Forsyth  * I2C software structures
68*74a4d8c2SCharles.Forsyth  */
69*74a4d8c2SCharles.Forsyth 
70*74a4d8c2SCharles.Forsyth struct Ctlr {
71*74a4d8c2SCharles.Forsyth 	Lock;
72*74a4d8c2SCharles.Forsyth 	QLock	io;
73*74a4d8c2SCharles.Forsyth 	int	init;
74*74a4d8c2SCharles.Forsyth 	I2C*	i2c;
75*74a4d8c2SCharles.Forsyth 	IOCparam*	sp;
76*74a4d8c2SCharles.Forsyth 
77*74a4d8c2SCharles.Forsyth 	BD*	rd;
78*74a4d8c2SCharles.Forsyth 	BD*	td;
79*74a4d8c2SCharles.Forsyth 	int	phase;
80*74a4d8c2SCharles.Forsyth 	char*	addr;
81*74a4d8c2SCharles.Forsyth 	char*	txbuf;
82*74a4d8c2SCharles.Forsyth 	char*	rxbuf;
83*74a4d8c2SCharles.Forsyth };
84*74a4d8c2SCharles.Forsyth typedef struct Ctlr Ctlr;
85*74a4d8c2SCharles.Forsyth 
86*74a4d8c2SCharles.Forsyth static	Ctlr	i2ctlr[1];
87*74a4d8c2SCharles.Forsyth extern	int	predawn;
88*74a4d8c2SCharles.Forsyth 
89*74a4d8c2SCharles.Forsyth static	void	interrupt(Ureg*, void*);
90*74a4d8c2SCharles.Forsyth 
91*74a4d8c2SCharles.Forsyth static void
enable(void)92*74a4d8c2SCharles.Forsyth enable(void)
93*74a4d8c2SCharles.Forsyth {
94*74a4d8c2SCharles.Forsyth 	I2C *i2c;
95*74a4d8c2SCharles.Forsyth 
96*74a4d8c2SCharles.Forsyth 	i2c = i2ctlr->i2c;
97*74a4d8c2SCharles.Forsyth 	i2c->i2cer = ~0;	/* clear events */
98*74a4d8c2SCharles.Forsyth 	eieio();
99*74a4d8c2SCharles.Forsyth 	i2c->i2mod |= EN;
100*74a4d8c2SCharles.Forsyth 	eieio();
101*74a4d8c2SCharles.Forsyth 	i2c->i2cmr = TXE|BSY|TXB|RXB;	/* enable all interrupts */
102*74a4d8c2SCharles.Forsyth 	eieio();
103*74a4d8c2SCharles.Forsyth }
104*74a4d8c2SCharles.Forsyth 
105*74a4d8c2SCharles.Forsyth static void
disable(void)106*74a4d8c2SCharles.Forsyth disable(void)
107*74a4d8c2SCharles.Forsyth {
108*74a4d8c2SCharles.Forsyth 	I2C *i2c;
109*74a4d8c2SCharles.Forsyth 
110*74a4d8c2SCharles.Forsyth 	i2c = i2ctlr->i2c;
111*74a4d8c2SCharles.Forsyth 	i2c->i2cmr = 0;	/* mask all interrupts */
112*74a4d8c2SCharles.Forsyth 	i2c->i2mod &= ~EN;
113*74a4d8c2SCharles.Forsyth }
114*74a4d8c2SCharles.Forsyth 
115*74a4d8c2SCharles.Forsyth /*
116*74a4d8c2SCharles.Forsyth  * called by the reset routine of any driver using the I2C
117*74a4d8c2SCharles.Forsyth  */
118*74a4d8c2SCharles.Forsyth void
i2csetup(void)119*74a4d8c2SCharles.Forsyth i2csetup(void)
120*74a4d8c2SCharles.Forsyth {
121*74a4d8c2SCharles.Forsyth 	IMM *io;
122*74a4d8c2SCharles.Forsyth 	I2C *i2c;
123*74a4d8c2SCharles.Forsyth 	IOCparam *sp;
124*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
125*74a4d8c2SCharles.Forsyth 	long f, e, emin;
126*74a4d8c2SCharles.Forsyth 	int p, d, dmax;
127*74a4d8c2SCharles.Forsyth 
128*74a4d8c2SCharles.Forsyth 	ctlr = i2ctlr;
129*74a4d8c2SCharles.Forsyth 	if(ctlr->init)
130*74a4d8c2SCharles.Forsyth 		return;
131*74a4d8c2SCharles.Forsyth 	print("i2c setup...\n");
132*74a4d8c2SCharles.Forsyth 	ctlr->init = 1;
133*74a4d8c2SCharles.Forsyth 	i2c = KADDR(INTMEM+0x860);
134*74a4d8c2SCharles.Forsyth 	ctlr->i2c = i2c;
135*74a4d8c2SCharles.Forsyth 	sp = KADDR(INTMEM+0x3c80);
136*74a4d8c2SCharles.Forsyth 	ctlr->sp = sp;
137*74a4d8c2SCharles.Forsyth 	disable();
138*74a4d8c2SCharles.Forsyth 
139*74a4d8c2SCharles.Forsyth 	if(ctlr->txbuf == nil){
140*74a4d8c2SCharles.Forsyth 		ctlr->txbuf = ialloc(Tbuflen, 2);
141*74a4d8c2SCharles.Forsyth 		ctlr->addr = ctlr->txbuf+Bufsize;
142*74a4d8c2SCharles.Forsyth 	}
143*74a4d8c2SCharles.Forsyth 	if(ctlr->rxbuf == nil)
144*74a4d8c2SCharles.Forsyth 		ctlr->rxbuf = ialloc(Bufsize, 2);
145*74a4d8c2SCharles.Forsyth 	if(ctlr->rd == nil){
146*74a4d8c2SCharles.Forsyth 		ctlr->rd = bdalloc(1);
147*74a4d8c2SCharles.Forsyth 		ctlr->rd->addr = PADDR(ctlr->rxbuf);
148*74a4d8c2SCharles.Forsyth 		ctlr->rd->length = 0;
149*74a4d8c2SCharles.Forsyth 		ctlr->rd->status = BDWrap;
150*74a4d8c2SCharles.Forsyth 	}
151*74a4d8c2SCharles.Forsyth 	if(ctlr->td == nil){
152*74a4d8c2SCharles.Forsyth 		ctlr->td = bdalloc(2);
153*74a4d8c2SCharles.Forsyth 		ctlr->td->addr = PADDR(ctlr->txbuf);
154*74a4d8c2SCharles.Forsyth 		ctlr->td->length = 0;
155*74a4d8c2SCharles.Forsyth 		ctlr->td->status = BDWrap|BDLast;
156*74a4d8c2SCharles.Forsyth 	}
157*74a4d8c2SCharles.Forsyth 
158*74a4d8c2SCharles.Forsyth 	/* select port pins */
159*74a4d8c2SCharles.Forsyth 	io = ioplock();
160*74a4d8c2SCharles.Forsyth 	io->pbdir |= I2CSDA | I2CSCL;
161*74a4d8c2SCharles.Forsyth 	io->pbodr |= I2CSDA | I2CSCL;
162*74a4d8c2SCharles.Forsyth 	io->pbpar |= I2CSDA | I2CSCL;
163*74a4d8c2SCharles.Forsyth 	iopunlock();
164*74a4d8c2SCharles.Forsyth 
165*74a4d8c2SCharles.Forsyth 	/* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */
166*74a4d8c2SCharles.Forsyth 	sp = ctlr->sp;
167*74a4d8c2SCharles.Forsyth 	sp->rbase = PADDR(ctlr->rd);
168*74a4d8c2SCharles.Forsyth 	sp->tbase = PADDR(ctlr->td);
169*74a4d8c2SCharles.Forsyth 	sp->rfcr = 0x18;
170*74a4d8c2SCharles.Forsyth 	sp->tfcr = 0x18;
171*74a4d8c2SCharles.Forsyth 	sp->mrblr = Bufsize;
172*74a4d8c2SCharles.Forsyth 	sp->rstate = 0;
173*74a4d8c2SCharles.Forsyth 	sp->rptr = 0;
174*74a4d8c2SCharles.Forsyth 	sp->rbptr = sp->rbase;
175*74a4d8c2SCharles.Forsyth 	sp->rcnt = 0;
176*74a4d8c2SCharles.Forsyth 	sp->tstate = 0;
177*74a4d8c2SCharles.Forsyth 	sp->tbptr = sp->tbase;
178*74a4d8c2SCharles.Forsyth 	sp->tptr = 0;
179*74a4d8c2SCharles.Forsyth 	sp->tcnt = 0;
180*74a4d8c2SCharles.Forsyth 	eieio();
181*74a4d8c2SCharles.Forsyth 
182*74a4d8c2SCharles.Forsyth 	i2c->i2com = I2CM;
183*74a4d8c2SCharles.Forsyth 	i2c->i2mod = 0;	/* normal mode */
184*74a4d8c2SCharles.Forsyth 	i2c->i2add = 0;
185*74a4d8c2SCharles.Forsyth 
186*74a4d8c2SCharles.Forsyth 	emin = Freq;
187*74a4d8c2SCharles.Forsyth 	dmax = (m->cpuhz/Freq)/2-3;
188*74a4d8c2SCharles.Forsyth 	for(d=0; d < dmax; d++){
189*74a4d8c2SCharles.Forsyth 		for(p=3; p>=0; p--){
190*74a4d8c2SCharles.Forsyth 			f = (m->cpuhz>>(p+2))/(2*(d+3));
191*74a4d8c2SCharles.Forsyth 			e = Freq - f;
192*74a4d8c2SCharles.Forsyth 			if(e < 0)
193*74a4d8c2SCharles.Forsyth 				e = -e;
194*74a4d8c2SCharles.Forsyth 			if(e < emin){
195*74a4d8c2SCharles.Forsyth 				emin = e;
196*74a4d8c2SCharles.Forsyth 				i2c->i2brg = d;
197*74a4d8c2SCharles.Forsyth 				i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */
198*74a4d8c2SCharles.Forsyth 			}
199*74a4d8c2SCharles.Forsyth 		}
200*74a4d8c2SCharles.Forsyth 	}
201*74a4d8c2SCharles.Forsyth 	//print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod);
202*74a4d8c2SCharles.Forsyth 	setvec(VectorCPIC+0x10, interrupt, i2ctlr);
203*74a4d8c2SCharles.Forsyth }
204*74a4d8c2SCharles.Forsyth 
205*74a4d8c2SCharles.Forsyth enum {
206*74a4d8c2SCharles.Forsyth 	Idling,
207*74a4d8c2SCharles.Forsyth 	Done,
208*74a4d8c2SCharles.Forsyth 	Busy,
209*74a4d8c2SCharles.Forsyth 		Sending,
210*74a4d8c2SCharles.Forsyth 		Recving,
211*74a4d8c2SCharles.Forsyth };
212*74a4d8c2SCharles.Forsyth 
213*74a4d8c2SCharles.Forsyth static void
interrupt(Ureg *,void * arg)214*74a4d8c2SCharles.Forsyth interrupt(Ureg*, void *arg)
215*74a4d8c2SCharles.Forsyth {
216*74a4d8c2SCharles.Forsyth 	int events;
217*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
218*74a4d8c2SCharles.Forsyth 	I2C *i2c;
219*74a4d8c2SCharles.Forsyth 
220*74a4d8c2SCharles.Forsyth 	ctlr = arg;
221*74a4d8c2SCharles.Forsyth 	i2c = ctlr->i2c;
222*74a4d8c2SCharles.Forsyth 	events = i2c->i2cer;
223*74a4d8c2SCharles.Forsyth 	eieio();
224*74a4d8c2SCharles.Forsyth 	i2c->i2cer = events;
225*74a4d8c2SCharles.Forsyth 	if(events & (BSY|TXE)){
226*74a4d8c2SCharles.Forsyth 		//print("I2C#%x\n", events);
227*74a4d8c2SCharles.Forsyth 		if(ctlr->phase != Idling){
228*74a4d8c2SCharles.Forsyth 			ctlr->phase = Idling;
229*74a4d8c2SCharles.Forsyth 		}
230*74a4d8c2SCharles.Forsyth 	}else{
231*74a4d8c2SCharles.Forsyth 		if(events & TXB){
232*74a4d8c2SCharles.Forsyth 			//print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status);
233*74a4d8c2SCharles.Forsyth 			if(ctlr->phase == Sending){
234*74a4d8c2SCharles.Forsyth 				ctlr->phase = Done;
235*74a4d8c2SCharles.Forsyth 			}
236*74a4d8c2SCharles.Forsyth 		}
237*74a4d8c2SCharles.Forsyth 		if(events & RXB){
238*74a4d8c2SCharles.Forsyth 			//print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length);
239*74a4d8c2SCharles.Forsyth 			if(ctlr->phase == Recving){
240*74a4d8c2SCharles.Forsyth 				ctlr->phase = Done;
241*74a4d8c2SCharles.Forsyth 			}
242*74a4d8c2SCharles.Forsyth 		}
243*74a4d8c2SCharles.Forsyth 	}
244*74a4d8c2SCharles.Forsyth }
245*74a4d8c2SCharles.Forsyth 
246*74a4d8c2SCharles.Forsyth static int
done(void * a)247*74a4d8c2SCharles.Forsyth done(void *a)
248*74a4d8c2SCharles.Forsyth {
249*74a4d8c2SCharles.Forsyth 	return ((Ctlr*)a)->phase < Busy;
250*74a4d8c2SCharles.Forsyth }
251*74a4d8c2SCharles.Forsyth 
252*74a4d8c2SCharles.Forsyth static void
i2cwait(Ctlr * ctlr)253*74a4d8c2SCharles.Forsyth i2cwait(Ctlr *ctlr)
254*74a4d8c2SCharles.Forsyth {
255*74a4d8c2SCharles.Forsyth 	/* TO DO: timeout */
256*74a4d8c2SCharles.Forsyth 	while(!done(ctlr)){
257*74a4d8c2SCharles.Forsyth 		if(predawn)
258*74a4d8c2SCharles.Forsyth 			interrupt(nil, ctlr);
259*74a4d8c2SCharles.Forsyth 	}
260*74a4d8c2SCharles.Forsyth }
261*74a4d8c2SCharles.Forsyth 
262*74a4d8c2SCharles.Forsyth long
i2csend(int addr,void * buf,long n)263*74a4d8c2SCharles.Forsyth i2csend(int addr, void *buf, long n)
264*74a4d8c2SCharles.Forsyth {
265*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
266*74a4d8c2SCharles.Forsyth 	int i, p, s;
267*74a4d8c2SCharles.Forsyth 
268*74a4d8c2SCharles.Forsyth 	ctlr = i2ctlr;
269*74a4d8c2SCharles.Forsyth 	if(n > Bufsize)
270*74a4d8c2SCharles.Forsyth 		return -1;
271*74a4d8c2SCharles.Forsyth 	i = 1;
272*74a4d8c2SCharles.Forsyth 	ctlr->txbuf[0] = addr & ~1;
273*74a4d8c2SCharles.Forsyth 	if(addr & 1){
274*74a4d8c2SCharles.Forsyth 		ctlr->txbuf[1] = addr>>8;
275*74a4d8c2SCharles.Forsyth 		i++;
276*74a4d8c2SCharles.Forsyth 	}
277*74a4d8c2SCharles.Forsyth 	memmove(ctlr->txbuf+i, buf, n);
278*74a4d8c2SCharles.Forsyth 	DCFLUSH(ctlr->txbuf, Tbuflen);
279*74a4d8c2SCharles.Forsyth 	ctlr->phase = Sending;
280*74a4d8c2SCharles.Forsyth 	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
281*74a4d8c2SCharles.Forsyth 	ctlr->td->addr = PADDR(ctlr->txbuf);
282*74a4d8c2SCharles.Forsyth 	ctlr->td->length = n+i;
283*74a4d8c2SCharles.Forsyth 	ctlr->td->status = BDReady|BDWrap|BDLast|BDInt;
284*74a4d8c2SCharles.Forsyth 	enable();
285*74a4d8c2SCharles.Forsyth 	ctlr->i2c->i2com = STR|I2CM;
286*74a4d8c2SCharles.Forsyth 	eieio();
287*74a4d8c2SCharles.Forsyth 	i2cwait(ctlr);
288*74a4d8c2SCharles.Forsyth 	disable();
289*74a4d8c2SCharles.Forsyth 	p = ctlr->phase;
290*74a4d8c2SCharles.Forsyth 	s = ctlr->td->status;
291*74a4d8c2SCharles.Forsyth 	if(s & BDReady || s & TxERR || p != Done)
292*74a4d8c2SCharles.Forsyth 		return -1;
293*74a4d8c2SCharles.Forsyth 	return n;
294*74a4d8c2SCharles.Forsyth }
295*74a4d8c2SCharles.Forsyth 
296*74a4d8c2SCharles.Forsyth long
i2crecv(int addr,void * buf,long n)297*74a4d8c2SCharles.Forsyth i2crecv(int addr, void *buf, long n)
298*74a4d8c2SCharles.Forsyth {
299*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
300*74a4d8c2SCharles.Forsyth 	int p, s, flag;
301*74a4d8c2SCharles.Forsyth 	BD *td;
302*74a4d8c2SCharles.Forsyth 	long nr;
303*74a4d8c2SCharles.Forsyth 
304*74a4d8c2SCharles.Forsyth 	ctlr = i2ctlr;
305*74a4d8c2SCharles.Forsyth 	if(n > Bufsize)
306*74a4d8c2SCharles.Forsyth 		return -1;
307*74a4d8c2SCharles.Forsyth 	ctlr->txbuf[0] = addr|Rbit;
308*74a4d8c2SCharles.Forsyth 	if(addr & 1){	/* special select sequence */
309*74a4d8c2SCharles.Forsyth 		ctlr->addr[0] = addr &~ 1;
310*74a4d8c2SCharles.Forsyth 		ctlr->addr[1] = addr>>8;
311*74a4d8c2SCharles.Forsyth 	}
312*74a4d8c2SCharles.Forsyth 	DCFLUSH(ctlr->txbuf, Tbuflen);
313*74a4d8c2SCharles.Forsyth 	DCFLUSH(ctlr->rxbuf, Bufsize);
314*74a4d8c2SCharles.Forsyth 	ctlr->phase = Recving;
315*74a4d8c2SCharles.Forsyth 	ctlr->rd->addr = PADDR(ctlr->rxbuf);
316*74a4d8c2SCharles.Forsyth 	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
317*74a4d8c2SCharles.Forsyth 	flag = 0;
318*74a4d8c2SCharles.Forsyth 	td = ctlr->td;
319*74a4d8c2SCharles.Forsyth 	td[1].status = 0;
320*74a4d8c2SCharles.Forsyth 	if(addr & 1){
321*74a4d8c2SCharles.Forsyth 		/* special select sequence */
322*74a4d8c2SCharles.Forsyth 		td->addr = PADDR(ctlr->addr);
323*74a4d8c2SCharles.Forsyth 		td->length = 2;
324*74a4d8c2SCharles.Forsyth 		/* td->status made BDReady below */
325*74a4d8c2SCharles.Forsyth 		td++;
326*74a4d8c2SCharles.Forsyth 		flag = TxS;
327*74a4d8c2SCharles.Forsyth 	}
328*74a4d8c2SCharles.Forsyth 	td->addr = PADDR(ctlr->txbuf);
329*74a4d8c2SCharles.Forsyth 	td->length = n+1;
330*74a4d8c2SCharles.Forsyth 	td->status = BDReady|BDWrap|BDLast | flag;	/* not BDInt: leave that to receive */
331*74a4d8c2SCharles.Forsyth 	if(flag)
332*74a4d8c2SCharles.Forsyth 		ctlr->td->status = BDReady;
333*74a4d8c2SCharles.Forsyth 	enable();
334*74a4d8c2SCharles.Forsyth 	ctlr->i2c->i2com = STR|I2CM;
335*74a4d8c2SCharles.Forsyth 	eieio();
336*74a4d8c2SCharles.Forsyth 	i2cwait(ctlr);
337*74a4d8c2SCharles.Forsyth 	disable();
338*74a4d8c2SCharles.Forsyth 	p = ctlr->phase;
339*74a4d8c2SCharles.Forsyth 	s = ctlr->td->status;
340*74a4d8c2SCharles.Forsyth 	if(flag)
341*74a4d8c2SCharles.Forsyth 		s |= ctlr->td[1].status;
342*74a4d8c2SCharles.Forsyth 	nr = ctlr->rd->length;
343*74a4d8c2SCharles.Forsyth 	if(nr > n)
344*74a4d8c2SCharles.Forsyth 		nr = n;	/* shouldn't happen */
345*74a4d8c2SCharles.Forsyth 	if(s & TxERR || s & BDReady || ctlr->rd->status & BDEmpty)
346*74a4d8c2SCharles.Forsyth 		return -1;
347*74a4d8c2SCharles.Forsyth 	if(p != Done)
348*74a4d8c2SCharles.Forsyth 		return -1;
349*74a4d8c2SCharles.Forsyth 	memmove(buf, ctlr->rxbuf, nr);
350*74a4d8c2SCharles.Forsyth 	return nr;
351*74a4d8c2SCharles.Forsyth }
352