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