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