xref: /inferno-os/os/mpc/i2c.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1*74a4d8c2SCharles.Forsyth #include "u.h"
2*74a4d8c2SCharles.Forsyth #include "../port/lib.h"
3*74a4d8c2SCharles.Forsyth #include "mem.h"
4*74a4d8c2SCharles.Forsyth #include "dat.h"
5*74a4d8c2SCharles.Forsyth #include "fns.h"
6*74a4d8c2SCharles.Forsyth #include "io.h"
7*74a4d8c2SCharles.Forsyth #include "../port/error.h"
8*74a4d8c2SCharles.Forsyth 
9*74a4d8c2SCharles.Forsyth /*
10*74a4d8c2SCharles.Forsyth  * basic read/write interface to mpc8xx I2C bus (master mode)
11*74a4d8c2SCharles.Forsyth  */
12*74a4d8c2SCharles.Forsyth 
13*74a4d8c2SCharles.Forsyth typedef struct Ctlr Ctlr;
14*74a4d8c2SCharles.Forsyth typedef struct I2C I2C;
15*74a4d8c2SCharles.Forsyth 
16*74a4d8c2SCharles.Forsyth struct I2C {
17*74a4d8c2SCharles.Forsyth 	uchar	i2mod;
18*74a4d8c2SCharles.Forsyth 	uchar	rsv12a[3];
19*74a4d8c2SCharles.Forsyth 	uchar	i2add;
20*74a4d8c2SCharles.Forsyth 	uchar	rsv12b[3];
21*74a4d8c2SCharles.Forsyth 	uchar	i2brg;
22*74a4d8c2SCharles.Forsyth 	uchar	rsv12c[3];
23*74a4d8c2SCharles.Forsyth 	uchar	i2com;
24*74a4d8c2SCharles.Forsyth 	uchar	rsv12d[3];
25*74a4d8c2SCharles.Forsyth 	uchar	i2cer;
26*74a4d8c2SCharles.Forsyth 	uchar	rsv12e[3];
27*74a4d8c2SCharles.Forsyth 	uchar	i2cmr;
28*74a4d8c2SCharles.Forsyth };
29*74a4d8c2SCharles.Forsyth 
30*74a4d8c2SCharles.Forsyth enum {
31*74a4d8c2SCharles.Forsyth 	/* i2c-specific BD flags */
32*74a4d8c2SCharles.Forsyth 	RxeOV=		1<<1,	/* overrun */
33*74a4d8c2SCharles.Forsyth 	TxS=			1<<10,	/* transmit start condition */
34*74a4d8c2SCharles.Forsyth 	TxeNAK=		1<<2,	/* last transmitted byte not acknowledged */
35*74a4d8c2SCharles.Forsyth 	TxeUN=		1<<1,	/* underflow */
36*74a4d8c2SCharles.Forsyth 	TxeCL=		1<<0,	/* collision */
37*74a4d8c2SCharles.Forsyth 	TxERR=		(TxeNAK|TxeUN|TxeCL),
38*74a4d8c2SCharles.Forsyth 
39*74a4d8c2SCharles.Forsyth 	/* i2cmod */
40*74a4d8c2SCharles.Forsyth 	REVD=	1<<5,	/* =1, LSB first */
41*74a4d8c2SCharles.Forsyth 	GCD=	1<<4,	/* =1, general call address disabled */
42*74a4d8c2SCharles.Forsyth 	FLT=		1<<3,	/* =0, not filtered; =1, filtered */
43*74a4d8c2SCharles.Forsyth 	PDIV=	3<<1,	/* predivisor field */
44*74a4d8c2SCharles.Forsyth 	EN=		1<<0,	/* enable */
45*74a4d8c2SCharles.Forsyth 
46*74a4d8c2SCharles.Forsyth 	/* i2com */
47*74a4d8c2SCharles.Forsyth 	STR=		1<<7,	/* start transmit */
48*74a4d8c2SCharles.Forsyth 	I2CM=	1<<0,	/* master */
49*74a4d8c2SCharles.Forsyth 	I2CS=	0<<0,	/* slave */
50*74a4d8c2SCharles.Forsyth 
51*74a4d8c2SCharles.Forsyth 	/* i2cer */
52*74a4d8c2SCharles.Forsyth 	TXE =	1<<4,
53*74a4d8c2SCharles.Forsyth 	BSY =	1<<2,
54*74a4d8c2SCharles.Forsyth 	TXB =	1<<1,
55*74a4d8c2SCharles.Forsyth 	RXB =	1<<0,
56*74a4d8c2SCharles.Forsyth 
57*74a4d8c2SCharles.Forsyth 	/* port B bits */
58*74a4d8c2SCharles.Forsyth 	I2CSDA =	IBIT(27),
59*74a4d8c2SCharles.Forsyth 	I2CSCL = IBIT(26),
60*74a4d8c2SCharles.Forsyth 
61*74a4d8c2SCharles.Forsyth 	Rbit =	1<<0,	/* bit in address byte denoting read */
62*74a4d8c2SCharles.Forsyth 
63*74a4d8c2SCharles.Forsyth 	/* maximum I2C I/O (can change) */
64*74a4d8c2SCharles.Forsyth 	MaxIO =	128,
65*74a4d8c2SCharles.Forsyth 	MaxSA =	2,	/* longest subaddress */
66*74a4d8c2SCharles.Forsyth 	Bufsize =	(MaxIO+MaxSA+1+4)&~3,	/* extra space for subaddress/clock bytes and alignment */
67*74a4d8c2SCharles.Forsyth 	Freq =	100000,
68*74a4d8c2SCharles.Forsyth 	I2CTimeout = 250,	/* msec */
69*74a4d8c2SCharles.Forsyth 
70*74a4d8c2SCharles.Forsyth 	Chatty = 0,
71*74a4d8c2SCharles.Forsyth };
72*74a4d8c2SCharles.Forsyth 
73*74a4d8c2SCharles.Forsyth #define	DPRINT	if(Chatty)print
74*74a4d8c2SCharles.Forsyth 
75*74a4d8c2SCharles.Forsyth /* data cache needn't be flushed if buffers allocated in uncached PHYSIMM */
76*74a4d8c2SCharles.Forsyth #define	DCFLUSH(a,n)
77*74a4d8c2SCharles.Forsyth 
78*74a4d8c2SCharles.Forsyth /*
79*74a4d8c2SCharles.Forsyth  * I2C software structures
80*74a4d8c2SCharles.Forsyth  */
81*74a4d8c2SCharles.Forsyth 
82*74a4d8c2SCharles.Forsyth struct Ctlr {
83*74a4d8c2SCharles.Forsyth 	Lock;
84*74a4d8c2SCharles.Forsyth 	QLock	io;
85*74a4d8c2SCharles.Forsyth 	int	init;
86*74a4d8c2SCharles.Forsyth 	int	busywait;	/* running before system set up */
87*74a4d8c2SCharles.Forsyth 	I2C*	i2c;
88*74a4d8c2SCharles.Forsyth 	IOCparam*	sp;
89*74a4d8c2SCharles.Forsyth 
90*74a4d8c2SCharles.Forsyth 	BD*	rd;
91*74a4d8c2SCharles.Forsyth 	BD*	td;
92*74a4d8c2SCharles.Forsyth 	int	phase;
93*74a4d8c2SCharles.Forsyth 	Rendez	r;
94*74a4d8c2SCharles.Forsyth 	char*	addr;
95*74a4d8c2SCharles.Forsyth 	char*	txbuf;
96*74a4d8c2SCharles.Forsyth 	char*	rxbuf;
97*74a4d8c2SCharles.Forsyth };
98*74a4d8c2SCharles.Forsyth 
99*74a4d8c2SCharles.Forsyth static	Ctlr	i2ctlr[1];
100*74a4d8c2SCharles.Forsyth 
101*74a4d8c2SCharles.Forsyth static	void	interrupt(Ureg*, void*);
102*74a4d8c2SCharles.Forsyth 
103*74a4d8c2SCharles.Forsyth static void
enable(void)104*74a4d8c2SCharles.Forsyth enable(void)
105*74a4d8c2SCharles.Forsyth {
106*74a4d8c2SCharles.Forsyth 	I2C *i2c;
107*74a4d8c2SCharles.Forsyth 
108*74a4d8c2SCharles.Forsyth 	i2c = i2ctlr->i2c;
109*74a4d8c2SCharles.Forsyth 	i2c->i2cer = ~0;	/* clear events */
110*74a4d8c2SCharles.Forsyth 	eieio();
111*74a4d8c2SCharles.Forsyth 	i2c->i2mod |= EN;
112*74a4d8c2SCharles.Forsyth 	eieio();
113*74a4d8c2SCharles.Forsyth 	i2c->i2cmr = TXE|BSY|TXB|RXB;	/* enable all interrupts */
114*74a4d8c2SCharles.Forsyth 	eieio();
115*74a4d8c2SCharles.Forsyth }
116*74a4d8c2SCharles.Forsyth 
117*74a4d8c2SCharles.Forsyth static void
disable(void)118*74a4d8c2SCharles.Forsyth disable(void)
119*74a4d8c2SCharles.Forsyth {
120*74a4d8c2SCharles.Forsyth 	I2C *i2c;
121*74a4d8c2SCharles.Forsyth 
122*74a4d8c2SCharles.Forsyth 	i2c = i2ctlr->i2c;
123*74a4d8c2SCharles.Forsyth 	i2c->i2cmr = 0;	/* mask all interrupts */
124*74a4d8c2SCharles.Forsyth 	i2c->i2mod &= ~EN;
125*74a4d8c2SCharles.Forsyth }
126*74a4d8c2SCharles.Forsyth 
127*74a4d8c2SCharles.Forsyth /*
128*74a4d8c2SCharles.Forsyth  * called by the reset routine of any driver using the I2C
129*74a4d8c2SCharles.Forsyth  */
130*74a4d8c2SCharles.Forsyth void
i2csetup(int busywait)131*74a4d8c2SCharles.Forsyth i2csetup(int busywait)
132*74a4d8c2SCharles.Forsyth {
133*74a4d8c2SCharles.Forsyth 	IMM *io;
134*74a4d8c2SCharles.Forsyth 	I2C *i2c;
135*74a4d8c2SCharles.Forsyth 	IOCparam *sp;
136*74a4d8c2SCharles.Forsyth 	CPMdev *cpm;
137*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
138*74a4d8c2SCharles.Forsyth 	long f, e, emin;
139*74a4d8c2SCharles.Forsyth 	int p, d, dmax;
140*74a4d8c2SCharles.Forsyth 
141*74a4d8c2SCharles.Forsyth 	ctlr = i2ctlr;
142*74a4d8c2SCharles.Forsyth 	ctlr->busywait = busywait;
143*74a4d8c2SCharles.Forsyth 	if(ctlr->init)
144*74a4d8c2SCharles.Forsyth 		return;
145*74a4d8c2SCharles.Forsyth 	print("i2c setup...\n");
146*74a4d8c2SCharles.Forsyth 	ctlr->init = 1;
147*74a4d8c2SCharles.Forsyth 	cpm = cpmdev(CPi2c);
148*74a4d8c2SCharles.Forsyth 	i2c = cpm->regs;
149*74a4d8c2SCharles.Forsyth 	ctlr->i2c = i2c;
150*74a4d8c2SCharles.Forsyth 	sp = cpm->param;
151*74a4d8c2SCharles.Forsyth 	if(sp == nil)
152*74a4d8c2SCharles.Forsyth 		panic("I2C: can't allocate new parameter memory\n");
153*74a4d8c2SCharles.Forsyth 	ctlr->sp = sp;
154*74a4d8c2SCharles.Forsyth 	disable();
155*74a4d8c2SCharles.Forsyth 
156*74a4d8c2SCharles.Forsyth 	if(ctlr->txbuf == nil){
157*74a4d8c2SCharles.Forsyth 		ctlr->txbuf = cpmalloc(Bufsize, 2);
158*74a4d8c2SCharles.Forsyth 		ctlr->addr = ctlr->txbuf+MaxIO;
159*74a4d8c2SCharles.Forsyth 	}
160*74a4d8c2SCharles.Forsyth 	if(ctlr->rxbuf == nil)
161*74a4d8c2SCharles.Forsyth 		ctlr->rxbuf = cpmalloc(Bufsize, 2);
162*74a4d8c2SCharles.Forsyth 	if(ctlr->rd == nil){
163*74a4d8c2SCharles.Forsyth 		ctlr->rd = bdalloc(1);
164*74a4d8c2SCharles.Forsyth 		ctlr->rd->addr = PADDR(ctlr->rxbuf);
165*74a4d8c2SCharles.Forsyth 		ctlr->rd->length = 0;
166*74a4d8c2SCharles.Forsyth 		ctlr->rd->status = BDWrap;
167*74a4d8c2SCharles.Forsyth 	}
168*74a4d8c2SCharles.Forsyth 	if(ctlr->td == nil){
169*74a4d8c2SCharles.Forsyth 		ctlr->td = bdalloc(2);
170*74a4d8c2SCharles.Forsyth 		ctlr->td->addr = PADDR(ctlr->txbuf);
171*74a4d8c2SCharles.Forsyth 		ctlr->td->length = 0;
172*74a4d8c2SCharles.Forsyth 		ctlr->td->status = BDWrap|BDLast;
173*74a4d8c2SCharles.Forsyth 	}
174*74a4d8c2SCharles.Forsyth 
175*74a4d8c2SCharles.Forsyth 	/* select port pins */
176*74a4d8c2SCharles.Forsyth 	io = ioplock();
177*74a4d8c2SCharles.Forsyth 	io->pbdir |= I2CSDA | I2CSCL;
178*74a4d8c2SCharles.Forsyth 	io->pbodr |= I2CSDA | I2CSCL;
179*74a4d8c2SCharles.Forsyth 	io->pbpar |= I2CSDA | I2CSCL;
180*74a4d8c2SCharles.Forsyth 	iopunlock();
181*74a4d8c2SCharles.Forsyth 
182*74a4d8c2SCharles.Forsyth 	/* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */
183*74a4d8c2SCharles.Forsyth 	sp = ctlr->sp;
184*74a4d8c2SCharles.Forsyth 	sp->rbase = PADDR(ctlr->rd);
185*74a4d8c2SCharles.Forsyth 	sp->tbase = PADDR(ctlr->td);
186*74a4d8c2SCharles.Forsyth 	sp->rfcr = 0x18;
187*74a4d8c2SCharles.Forsyth 	sp->tfcr = 0x18;
188*74a4d8c2SCharles.Forsyth 	sp->mrblr = Bufsize;
189*74a4d8c2SCharles.Forsyth 	sp->rstate = 0;
190*74a4d8c2SCharles.Forsyth 	sp->rptr = 0;
191*74a4d8c2SCharles.Forsyth 	sp->rbptr = sp->rbase;
192*74a4d8c2SCharles.Forsyth 	sp->rcnt = 0;
193*74a4d8c2SCharles.Forsyth 	sp->tstate = 0;
194*74a4d8c2SCharles.Forsyth 	sp->tbptr = sp->tbase;
195*74a4d8c2SCharles.Forsyth 	sp->tptr = 0;
196*74a4d8c2SCharles.Forsyth 	sp->tcnt = 0;
197*74a4d8c2SCharles.Forsyth 	eieio();
198*74a4d8c2SCharles.Forsyth 
199*74a4d8c2SCharles.Forsyth 	i2c->i2com = I2CM;
200*74a4d8c2SCharles.Forsyth 	i2c->i2mod = 0;	/* normal mode */
201*74a4d8c2SCharles.Forsyth 	i2c->i2add = 0;
202*74a4d8c2SCharles.Forsyth 
203*74a4d8c2SCharles.Forsyth 	emin = Freq;
204*74a4d8c2SCharles.Forsyth 	dmax = (m->cpuhz/Freq)/2-3;
205*74a4d8c2SCharles.Forsyth 	for(d=0; d < dmax; d++){
206*74a4d8c2SCharles.Forsyth 		for(p=3; p>=0; p--){
207*74a4d8c2SCharles.Forsyth 			f = (m->cpuhz>>(p+2))/(2*(d+3));
208*74a4d8c2SCharles.Forsyth 			e = Freq - f;
209*74a4d8c2SCharles.Forsyth 			if(e < 0)
210*74a4d8c2SCharles.Forsyth 				e = -e;
211*74a4d8c2SCharles.Forsyth 			if(e < emin){
212*74a4d8c2SCharles.Forsyth 				emin = e;
213*74a4d8c2SCharles.Forsyth 				i2c->i2brg = d;
214*74a4d8c2SCharles.Forsyth 				i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */
215*74a4d8c2SCharles.Forsyth 			}
216*74a4d8c2SCharles.Forsyth 		}
217*74a4d8c2SCharles.Forsyth 	}
218*74a4d8c2SCharles.Forsyth 	//print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod);
219*74a4d8c2SCharles.Forsyth 	intrenable(VectorCPIC+cpm->irq, interrupt, i2ctlr, BUSUNKNOWN, "i2c");
220*74a4d8c2SCharles.Forsyth }
221*74a4d8c2SCharles.Forsyth 
222*74a4d8c2SCharles.Forsyth enum {
223*74a4d8c2SCharles.Forsyth 	Idling,
224*74a4d8c2SCharles.Forsyth 	Done,
225*74a4d8c2SCharles.Forsyth 	Busy,
226*74a4d8c2SCharles.Forsyth 		Sending,
227*74a4d8c2SCharles.Forsyth 		Recving,
228*74a4d8c2SCharles.Forsyth };
229*74a4d8c2SCharles.Forsyth 
230*74a4d8c2SCharles.Forsyth static void
interrupt(Ureg *,void * arg)231*74a4d8c2SCharles.Forsyth interrupt(Ureg*, void *arg)
232*74a4d8c2SCharles.Forsyth {
233*74a4d8c2SCharles.Forsyth 	int events;
234*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
235*74a4d8c2SCharles.Forsyth 	I2C *i2c;
236*74a4d8c2SCharles.Forsyth 
237*74a4d8c2SCharles.Forsyth 	ctlr = arg;
238*74a4d8c2SCharles.Forsyth 	i2c = ctlr->i2c;
239*74a4d8c2SCharles.Forsyth 	events = i2c->i2cer;
240*74a4d8c2SCharles.Forsyth 	eieio();
241*74a4d8c2SCharles.Forsyth 	i2c->i2cer = events;
242*74a4d8c2SCharles.Forsyth 	if(events & (BSY|TXE)){
243*74a4d8c2SCharles.Forsyth 		//print("I2C#%x\n", events);
244*74a4d8c2SCharles.Forsyth 		if(ctlr->phase != Idling){
245*74a4d8c2SCharles.Forsyth 			ctlr->phase = Idling;
246*74a4d8c2SCharles.Forsyth 			wakeup(&ctlr->r);
247*74a4d8c2SCharles.Forsyth 		}
248*74a4d8c2SCharles.Forsyth 	}else{
249*74a4d8c2SCharles.Forsyth 		if(events & TXB){
250*74a4d8c2SCharles.Forsyth 			//print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status);
251*74a4d8c2SCharles.Forsyth 			if(ctlr->phase == Sending){
252*74a4d8c2SCharles.Forsyth 				ctlr->phase = Done;
253*74a4d8c2SCharles.Forsyth 				wakeup(&ctlr->r);
254*74a4d8c2SCharles.Forsyth 			}
255*74a4d8c2SCharles.Forsyth 		}
256*74a4d8c2SCharles.Forsyth 		if(events & RXB){
257*74a4d8c2SCharles.Forsyth 			//print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length);
258*74a4d8c2SCharles.Forsyth 			if(ctlr->phase == Recving){
259*74a4d8c2SCharles.Forsyth 				ctlr->phase = Done;
260*74a4d8c2SCharles.Forsyth 				wakeup(&ctlr->r);
261*74a4d8c2SCharles.Forsyth 			}
262*74a4d8c2SCharles.Forsyth 		}
263*74a4d8c2SCharles.Forsyth 	}
264*74a4d8c2SCharles.Forsyth }
265*74a4d8c2SCharles.Forsyth 
266*74a4d8c2SCharles.Forsyth static int
done(void * a)267*74a4d8c2SCharles.Forsyth done(void *a)
268*74a4d8c2SCharles.Forsyth {
269*74a4d8c2SCharles.Forsyth 	return ((Ctlr*)a)->phase < Busy;
270*74a4d8c2SCharles.Forsyth }
271*74a4d8c2SCharles.Forsyth 
272*74a4d8c2SCharles.Forsyth static void
i2cwait(Ctlr * ctlr)273*74a4d8c2SCharles.Forsyth i2cwait(Ctlr *ctlr)
274*74a4d8c2SCharles.Forsyth {
275*74a4d8c2SCharles.Forsyth 	int i;
276*74a4d8c2SCharles.Forsyth 
277*74a4d8c2SCharles.Forsyth 	if(up == nil || ctlr->busywait){
278*74a4d8c2SCharles.Forsyth 		for(i=0; i < 5 && !done(ctlr); i++){
279*74a4d8c2SCharles.Forsyth 			delay(2);
280*74a4d8c2SCharles.Forsyth 			interrupt(nil, ctlr);
281*74a4d8c2SCharles.Forsyth 		}
282*74a4d8c2SCharles.Forsyth 	}else
283*74a4d8c2SCharles.Forsyth 		tsleep(&ctlr->r, done, ctlr, I2CTimeout);
284*74a4d8c2SCharles.Forsyth }
285*74a4d8c2SCharles.Forsyth 
286*74a4d8c2SCharles.Forsyth static int
i2cerror(char * s)287*74a4d8c2SCharles.Forsyth i2cerror(char *s)
288*74a4d8c2SCharles.Forsyth {
289*74a4d8c2SCharles.Forsyth 	if(up)
290*74a4d8c2SCharles.Forsyth 		error(s);
291*74a4d8c2SCharles.Forsyth 	/* no current process, don't call error */
292*74a4d8c2SCharles.Forsyth 	DPRINT("i2c error: %s\n", s);
293*74a4d8c2SCharles.Forsyth 	return -1;
294*74a4d8c2SCharles.Forsyth }
295*74a4d8c2SCharles.Forsyth 
296*74a4d8c2SCharles.Forsyth long
i2csend(I2Cdev * d,void * buf,long n,ulong offset)297*74a4d8c2SCharles.Forsyth i2csend(I2Cdev *d, void *buf, long n, ulong offset)
298*74a4d8c2SCharles.Forsyth {
299*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
300*74a4d8c2SCharles.Forsyth 	int i, p, s;
301*74a4d8c2SCharles.Forsyth 
302*74a4d8c2SCharles.Forsyth 	ctlr = i2ctlr;
303*74a4d8c2SCharles.Forsyth 	if(up){
304*74a4d8c2SCharles.Forsyth 		if(n > MaxIO)
305*74a4d8c2SCharles.Forsyth 			error(Etoobig);
306*74a4d8c2SCharles.Forsyth 		qlock(&ctlr->io);
307*74a4d8c2SCharles.Forsyth 		if(waserror()){
308*74a4d8c2SCharles.Forsyth 			qunlock(&ctlr->io);
309*74a4d8c2SCharles.Forsyth 			nexterror();
310*74a4d8c2SCharles.Forsyth 		}
311*74a4d8c2SCharles.Forsyth 	}
312*74a4d8c2SCharles.Forsyth 	ctlr->txbuf[0] = d->addr<<1;
313*74a4d8c2SCharles.Forsyth 	i = 1;
314*74a4d8c2SCharles.Forsyth 	if(d->salen > 1)
315*74a4d8c2SCharles.Forsyth 		ctlr->txbuf[i++] = offset>>8;
316*74a4d8c2SCharles.Forsyth 	if(d->salen)
317*74a4d8c2SCharles.Forsyth 		ctlr->txbuf[i++] = offset;
318*74a4d8c2SCharles.Forsyth 	memmove(ctlr->txbuf+i, buf, n);
319*74a4d8c2SCharles.Forsyth 	if(Chatty){
320*74a4d8c2SCharles.Forsyth 		print("tx: %8.8lux: ", PADDR(ctlr->txbuf));
321*74a4d8c2SCharles.Forsyth 		for(s=0; s<n+i; s++)
322*74a4d8c2SCharles.Forsyth 			print(" %.2ux", ctlr->txbuf[s]&0xFF);
323*74a4d8c2SCharles.Forsyth 		print("\n");
324*74a4d8c2SCharles.Forsyth 	}
325*74a4d8c2SCharles.Forsyth 	DCFLUSH(ctlr->txbuf, Bufsize);
326*74a4d8c2SCharles.Forsyth 	ilock(ctlr);
327*74a4d8c2SCharles.Forsyth 	ctlr->phase = Sending;
328*74a4d8c2SCharles.Forsyth 	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
329*74a4d8c2SCharles.Forsyth 	ctlr->td->addr = PADDR(ctlr->txbuf);
330*74a4d8c2SCharles.Forsyth 	ctlr->td->length = n+i;
331*74a4d8c2SCharles.Forsyth 	ctlr->td->status = BDReady|BDWrap|BDLast|BDInt;
332*74a4d8c2SCharles.Forsyth 	enable();
333*74a4d8c2SCharles.Forsyth 	ctlr->i2c->i2com = STR|I2CM;
334*74a4d8c2SCharles.Forsyth 	eieio();
335*74a4d8c2SCharles.Forsyth 	iunlock(ctlr);
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(up){
341*74a4d8c2SCharles.Forsyth 		poperror();
342*74a4d8c2SCharles.Forsyth 		qunlock(&ctlr->io);
343*74a4d8c2SCharles.Forsyth 	}
344*74a4d8c2SCharles.Forsyth 	if(s & BDReady)
345*74a4d8c2SCharles.Forsyth 		return i2cerror("timed out");
346*74a4d8c2SCharles.Forsyth 	if(s & TxERR){
347*74a4d8c2SCharles.Forsyth 		sprint(up->genbuf, "write error: status %.4ux", s);
348*74a4d8c2SCharles.Forsyth 		return i2cerror(up->genbuf);
349*74a4d8c2SCharles.Forsyth 	}
350*74a4d8c2SCharles.Forsyth 	if(p != Done)
351*74a4d8c2SCharles.Forsyth 		return i2cerror("phase error");
352*74a4d8c2SCharles.Forsyth 	return n;
353*74a4d8c2SCharles.Forsyth }
354*74a4d8c2SCharles.Forsyth 
355*74a4d8c2SCharles.Forsyth long
i2crecv(I2Cdev * d,void * buf,long n,ulong offset)356*74a4d8c2SCharles.Forsyth i2crecv(I2Cdev *d, void *buf, long n, ulong offset)
357*74a4d8c2SCharles.Forsyth {
358*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
359*74a4d8c2SCharles.Forsyth 	int p, s, flag, i;
360*74a4d8c2SCharles.Forsyth 	BD *td;
361*74a4d8c2SCharles.Forsyth 	long nr;
362*74a4d8c2SCharles.Forsyth 
363*74a4d8c2SCharles.Forsyth 	ctlr = i2ctlr;
364*74a4d8c2SCharles.Forsyth 	if(up){
365*74a4d8c2SCharles.Forsyth 		if(n > MaxIO)
366*74a4d8c2SCharles.Forsyth 			error(Etoobig);
367*74a4d8c2SCharles.Forsyth 		qlock(&ctlr->io);
368*74a4d8c2SCharles.Forsyth 		if(waserror()){
369*74a4d8c2SCharles.Forsyth 			qunlock(&ctlr->io);
370*74a4d8c2SCharles.Forsyth 			nexterror();
371*74a4d8c2SCharles.Forsyth 		}
372*74a4d8c2SCharles.Forsyth 	}
373*74a4d8c2SCharles.Forsyth 	ctlr->txbuf[0] = (d->addr<<1)|Rbit;
374*74a4d8c2SCharles.Forsyth 	if(d->salen){	/* special write to set address */
375*74a4d8c2SCharles.Forsyth 		ctlr->addr[0] = d->addr<<1;
376*74a4d8c2SCharles.Forsyth 		i = 1;
377*74a4d8c2SCharles.Forsyth 		if(d->salen > 1)
378*74a4d8c2SCharles.Forsyth 			ctlr->addr[i++] = offset >> 8;
379*74a4d8c2SCharles.Forsyth 		ctlr->addr[i] = offset;
380*74a4d8c2SCharles.Forsyth 	}
381*74a4d8c2SCharles.Forsyth 	DCFLUSH(ctlr->txbuf, Bufsize);
382*74a4d8c2SCharles.Forsyth 	DCFLUSH(ctlr->rxbuf, Bufsize);
383*74a4d8c2SCharles.Forsyth 	ilock(ctlr);
384*74a4d8c2SCharles.Forsyth 	ctlr->phase = Recving;
385*74a4d8c2SCharles.Forsyth 	ctlr->rd->addr = PADDR(ctlr->rxbuf);
386*74a4d8c2SCharles.Forsyth 	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
387*74a4d8c2SCharles.Forsyth 	flag = 0;
388*74a4d8c2SCharles.Forsyth 	td = ctlr->td;
389*74a4d8c2SCharles.Forsyth 	td[1].status = 0;
390*74a4d8c2SCharles.Forsyth 	if(d->salen){
391*74a4d8c2SCharles.Forsyth 		/* special select sequence */
392*74a4d8c2SCharles.Forsyth 		td->addr = PADDR(ctlr->addr);
393*74a4d8c2SCharles.Forsyth 		i = d->salen+1;
394*74a4d8c2SCharles.Forsyth 		if(i > 3)
395*74a4d8c2SCharles.Forsyth 			i = 3;
396*74a4d8c2SCharles.Forsyth 		td->length = i;
397*74a4d8c2SCharles.Forsyth 		/* td->status made BDReady below */
398*74a4d8c2SCharles.Forsyth 		td++;
399*74a4d8c2SCharles.Forsyth 		flag = TxS;
400*74a4d8c2SCharles.Forsyth 	}
401*74a4d8c2SCharles.Forsyth 	td->addr = PADDR(ctlr->txbuf);
402*74a4d8c2SCharles.Forsyth 	td->length = n+1;
403*74a4d8c2SCharles.Forsyth 	td->status = BDReady|BDWrap|BDLast | flag;	/* not BDInt: leave that to receive */
404*74a4d8c2SCharles.Forsyth 	if(flag)
405*74a4d8c2SCharles.Forsyth 		ctlr->td->status = BDReady;
406*74a4d8c2SCharles.Forsyth 	enable();
407*74a4d8c2SCharles.Forsyth 	ctlr->i2c->i2com = STR|I2CM;
408*74a4d8c2SCharles.Forsyth 	eieio();
409*74a4d8c2SCharles.Forsyth 	iunlock(ctlr);
410*74a4d8c2SCharles.Forsyth 	i2cwait(ctlr);
411*74a4d8c2SCharles.Forsyth 	disable();
412*74a4d8c2SCharles.Forsyth 	p = ctlr->phase;
413*74a4d8c2SCharles.Forsyth 	s = ctlr->td->status;
414*74a4d8c2SCharles.Forsyth 	if(flag)
415*74a4d8c2SCharles.Forsyth 		s |= ctlr->td[1].status;
416*74a4d8c2SCharles.Forsyth 	nr = ctlr->rd->length;
417*74a4d8c2SCharles.Forsyth 	if(up){
418*74a4d8c2SCharles.Forsyth 		poperror();
419*74a4d8c2SCharles.Forsyth 		qunlock(&ctlr->io);
420*74a4d8c2SCharles.Forsyth 	}
421*74a4d8c2SCharles.Forsyth 	DPRINT("nr=%ld %4.4ux %8.8lux\n", nr, ctlr->rd->status, ctlr->rd->addr);
422*74a4d8c2SCharles.Forsyth 	if(nr > n)
423*74a4d8c2SCharles.Forsyth 		nr = n;	/* shouldn't happen */
424*74a4d8c2SCharles.Forsyth 	if(s & TxERR){
425*74a4d8c2SCharles.Forsyth 		sprint(up->genbuf, "read: tx status: %.4ux", s);
426*74a4d8c2SCharles.Forsyth 		return i2cerror(up->genbuf);
427*74a4d8c2SCharles.Forsyth 	}
428*74a4d8c2SCharles.Forsyth 	if(s & BDReady || ctlr->rd->status & BDEmpty)
429*74a4d8c2SCharles.Forsyth 		return i2cerror("timed out");
430*74a4d8c2SCharles.Forsyth 	if(p != Done)
431*74a4d8c2SCharles.Forsyth 		return i2cerror("phase error");
432*74a4d8c2SCharles.Forsyth 	memmove(buf, ctlr->rxbuf, nr);
433*74a4d8c2SCharles.Forsyth 	if(Chatty){
434*74a4d8c2SCharles.Forsyth 		for(s=0; s<nr; s++)
435*74a4d8c2SCharles.Forsyth 			print(" %2.2ux", ctlr->rxbuf[s]&0xFF);
436*74a4d8c2SCharles.Forsyth 		print("\n");
437*74a4d8c2SCharles.Forsyth 	}
438*74a4d8c2SCharles.Forsyth 	return nr;
439*74a4d8c2SCharles.Forsyth }
440