xref: /inferno-os/os/mpc/i2c.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
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