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