xref: /inferno-os/os/boot/rpcg/i2c.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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 	MaxIO=	128,
58 	Bufsize =	MaxIO+4,	/* extra space for address/clock 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 	int	timeout;
81 	char*	addr;
82 	char*	txbuf;
83 	char*	rxbuf;
84 };
85 typedef struct Ctlr Ctlr;
86 
87 static	Ctlr	i2ctlr[1];
88 extern	int	predawn;
89 
90 static	void	interrupt(Ureg*, void*);
91 
92 static void
93 enable(void)
94 {
95 	I2C *i2c;
96 
97 	i2c = i2ctlr->i2c;
98 	i2c->i2cer = ~0;	/* clear events */
99 	eieio();
100 	i2c->i2mod |= EN;
101 	eieio();
102 	i2c->i2cmr = TXE|BSY|TXB|RXB;	/* enable all interrupts */
103 	eieio();
104 }
105 
106 static void
107 disable(void)
108 {
109 	I2C *i2c;
110 
111 	i2c = i2ctlr->i2c;
112 	i2c->i2cmr = 0;	/* mask all interrupts */
113 	i2c->i2mod &= ~EN;
114 }
115 
116 /*
117  * called by the reset routine of any driver using the I2C
118  */
119 void
120 i2csetup(void)
121 {
122 	IMM *io;
123 	I2C *i2c;
124 	IOCparam *sp;
125 	Ctlr *ctlr;
126 	long f, e, emin;
127 	int p, d, dmax;
128 
129 	ctlr = i2ctlr;
130 	if(ctlr->init)
131 		return;
132 	print("i2c setup...\n");
133 	ctlr->init = 1;
134 	i2c = KADDR(INTMEM+0x860);
135 	ctlr->i2c = i2c;
136 	sp = KADDR(INTMEM+0x3c80);
137 	ctlr->sp = sp;
138 	disable();
139 
140 	if(ctlr->txbuf == nil){
141 		ctlr->txbuf = ialloc(Bufsize, 2);
142 		ctlr->addr = ctlr->txbuf+Bufsize;
143 	}
144 	if(ctlr->rxbuf == nil)
145 		ctlr->rxbuf = ialloc(Bufsize, 2);
146 	if(ctlr->rd == nil){
147 		ctlr->rd = bdalloc(1);
148 		ctlr->rd->addr = PADDR(ctlr->rxbuf);
149 		ctlr->rd->length = 0;
150 		ctlr->rd->status = BDWrap;
151 	}
152 	if(ctlr->td == nil){
153 		ctlr->td = bdalloc(2);
154 		ctlr->td->addr = PADDR(ctlr->txbuf);
155 		ctlr->td->length = 0;
156 		ctlr->td->status = BDWrap|BDLast;
157 	}
158 
159 	/* select port pins */
160 	io = ioplock();
161 	io->pbdir |= I2CSDA | I2CSCL;
162 	io->pbodr |= I2CSDA | I2CSCL;
163 	io->pbpar |= I2CSDA | I2CSCL;
164 	iopunlock();
165 
166 	/* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */
167 	sp = ctlr->sp;
168 	sp->rbase = PADDR(ctlr->rd);
169 	sp->tbase = PADDR(ctlr->td);
170 	sp->rfcr = 0x18;
171 	sp->tfcr = 0x18;
172 	sp->mrblr = Bufsize;
173 	sp->rstate = 0;
174 	sp->rptr = 0;
175 	sp->rbptr = sp->rbase;
176 	sp->rcnt = 0;
177 	sp->tstate = 0;
178 	sp->tbptr = sp->tbase;
179 	sp->tptr = 0;
180 	sp->tcnt = 0;
181 	eieio();
182 
183 	i2c->i2com = I2CM;
184 	i2c->i2mod = 0;	/* normal mode */
185 	i2c->i2add = 0;
186 
187 	emin = Freq;
188 	dmax = (m->cpuhz/Freq)/2-3;
189 	for(d=0; d < dmax; d++){
190 		for(p=3; p>=0; p--){
191 			f = (m->cpuhz>>(p+2))/(2*(d+3));
192 			e = Freq - f;
193 			if(e < 0)
194 				e = -e;
195 			if(e < emin){
196 				emin = e;
197 				i2c->i2brg = d;
198 				i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */
199 			}
200 		}
201 	}
202 	//print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod);
203 	setvec(VectorCPIC+0x10, interrupt, i2ctlr);
204 }
205 
206 enum {
207 	Idling,
208 	Done,
209 	Busy,
210 		Sending,
211 		Recving,
212 };
213 
214 static void
215 interrupt(Ureg*, void *arg)
216 {
217 	int events;
218 	Ctlr *ctlr;
219 	I2C *i2c;
220 
221 	ctlr = arg;
222 	i2c = ctlr->i2c;
223 	events = i2c->i2cer;
224 	eieio();
225 	i2c->i2cer = events;
226 	if(events & (BSY|TXE)){
227 		print("I2C#%x\n", events);
228 		if(ctlr->phase != Idling){
229 			ctlr->phase = Idling;
230 		}
231 	}else{
232 		if(events & TXB){
233 			//print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status);
234 			if(ctlr->phase == Sending){
235 				ctlr->phase = Done;
236 			}
237 		}
238 		if(events & RXB){
239 			//print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length);
240 			if(ctlr->phase == Recving){
241 				ctlr->phase = Done;
242 			}
243 		}
244 	}
245 }
246 
247 static int
248 done(void *a)
249 {
250 	return ((Ctlr*)a)->phase < Busy;
251 }
252 
253 static void
254 i2cwait(Ctlr *ctlr)
255 {
256 	int i;
257 
258 	ctlr->timeout = 0;
259 	i = 0;
260 	while(!done(ctlr)){
261 		if(predawn){
262 			if(++i > 100){
263 				ctlr->phase = Done;
264 				ctlr->timeout = 1;
265 				return;
266 			}
267 			delay(1);
268 			interrupt(nil, ctlr);
269 		}
270 	}
271 }
272 
273 long
274 i2csend(int addr, void *buf, long n)
275 {
276 	Ctlr *ctlr;
277 	int i, p, s;
278 
279 	ctlr = i2ctlr;
280 	if(n > MaxIO)
281 		return -1;
282 	i = 1;
283 	ctlr->txbuf[0] = addr & ~1;
284 	if(addr & 1){
285 		ctlr->txbuf[1] = addr>>8;
286 		i++;
287 	}
288 	memmove(ctlr->txbuf+i, buf, n);
289 	DCFLUSH(ctlr->txbuf, Bufsize);
290 	ctlr->phase = Sending;
291 	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
292 	ctlr->td->addr = PADDR(ctlr->txbuf);
293 	ctlr->td->length = n+i;
294 	ctlr->td->status = BDReady|BDWrap|BDLast|BDInt;
295 	enable();
296 	ctlr->i2c->i2com = STR|I2CM;
297 	eieio();
298 	i2cwait(ctlr);
299 	disable();
300 	p = ctlr->phase;
301 	s = ctlr->td->status;
302 	if(s & BDReady || s & TxERR || p != Done || ctlr->timeout)
303 		return -1;
304 	return n;
305 }
306 
307 long
308 i2crecv(int addr, void *buf, long n)
309 {
310 	Ctlr *ctlr;
311 	int p, s, flag;
312 	BD *td;
313 	long nr;
314 
315 	ctlr = i2ctlr;
316 	if(n > MaxIO)
317 		return -1;
318 	ctlr->txbuf[0] = addr|Rbit;
319 	if(addr & 1){	/* special select sequence */
320 		ctlr->addr[0] = addr &~ 1;
321 		ctlr->addr[1] = addr>>8;
322 	}
323 	DCFLUSH(ctlr->txbuf, Bufsize);
324 	DCFLUSH(ctlr->rxbuf, Bufsize);
325 	ctlr->phase = Recving;
326 	ctlr->rd->addr = PADDR(ctlr->rxbuf);
327 	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
328 	flag = 0;
329 	td = ctlr->td;
330 	td[1].status = 0;
331 	if(addr & 1){
332 		/* special select sequence */
333 		td->addr = PADDR(ctlr->addr);
334 		td->length = 2;
335 		/* td->status made BDReady below */
336 		td++;
337 		flag = TxS;
338 	}
339 	td->addr = PADDR(ctlr->txbuf);
340 	td->length = n+1;
341 	td->status = BDReady|BDWrap|BDLast | flag;	/* not BDInt: leave that to receive */
342 	if(flag)
343 		ctlr->td->status = BDReady;
344 	enable();
345 	ctlr->i2c->i2com = STR|I2CM;
346 	eieio();
347 	i2cwait(ctlr);
348 	disable();
349 	p = ctlr->phase;
350 	s = ctlr->td->status;
351 	if(flag)
352 		s |= ctlr->td[1].status;
353 	nr = ctlr->rd->length;
354 	if(nr > n)
355 		nr = n;	/* shouldn't happen */
356 	if(s & TxERR || s & BDReady || p != Done || ctlr->rd->status & BDEmpty || ctlr->timeout)
357 		return -1;
358 	memmove(buf, ctlr->rxbuf, nr);
359 	return nr;
360 }
361