1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7
8 typedef struct Chanuse Chanuse;
9 struct Chanuse {
10 Lock;
11 void* owner;
12 } ;
13
14 enum {
15 BDSIZE= 1024, /* IO memory reserved for buffer descriptors */
16 CPMSIZE= 1024, /* IO memory reserved for other uses */
17
18 /* channel IDs */
19 SCC1ID= 0,
20 I2CID= 1,
21 IDMA1ID= 1,
22 SCC2ID= 4,
23 SPIID= 5,
24 IDMA2ID= 5,
25 TIMERID= 5,
26 SCC3ID= 8,
27 SMC1ID= 9,
28 DSP1ID= 9,
29 SCC4ID= 12,
30 SMC2ID= 13,
31 DSP2ID= 13,
32 NCPMID= 16,
33
34 NSCC = 4,
35
36 /* SCC.gsmr_l */
37 ENR = 1<<5, /* enable receiver */
38 ENT = 1<<4, /* enable transmitter */
39
40 NSMC = 2,
41
42 /* SMC.smcmr */
43 TEN = 1<<1, /* transmitter enable */
44 REN = 1<<0, /* receiver enable */
45 };
46
47 static Map bdmapv[BDSIZE/sizeof(BD)];
48 static RMap bdmap = {"buffer descriptors"};
49
50 static Map cpmmapv[CPMSIZE/sizeof(ulong)];
51 static RMap cpmmap = {"CPM memory"};
52
53 static Lock cpmlock;
54
55 static struct {
56 Lock;
57 ulong avail;
58 } brgens;
59
60 static Chanuse cpmids[NCPMID];
61 static CPMdev cpmdevinfo[] = {
62 [CPscc1] {SCC1ID, 0x1E, 0xA00, 0x3C00},
63 [CPscc2] {SCC2ID, 0x1D, 0xA20, 0x3D00},
64 [CPscc3] {SCC3ID, 0x1C, 0xA40, 0x3E00},
65 [CPscc4] {SCC4ID, 0x1B, 0xA60, 0x3F00},
66 [CPsmc1] {SMC1ID, 0x04, 0xA80, 0x3E80},
67 [CPsmc2] {SMC2ID, 0x03, 0xA90, 0x3F80},
68 [CPdsp1] {DSP1ID, 0x16, 0, 0x3EC0},
69 [CPdsp2] {DSP2ID, 0x16, 0, 0x3FC0},
70 [CPidma1] {IDMA1ID, 0x15, 0, 0x3CC0},
71 [CPidma2] {IDMA2ID, 0x14, 0, 0x3DC0},
72 [CPtimer] {TIMERID, 0x11, 0, 0x3DB0},
73 [CPspi] {SPIID, 0x05, 0xAA0, 0x3D80}, /* parameters relocated below */
74 [CPi2c] {I2CID, 0x10, 0x860, 0x3C80}, /* parameters relocated below */
75 };
76
77 static void i2cspireloc(void);
78 static void* relocateparam(ulong, int);
79
80 /*
81 * initialise the communications processor module
82 * and associated device registers
83 */
84 void
cpminit(void)85 cpminit(void)
86 {
87 IMM *io;
88
89 io = m->iomem;
90 io->sdcr = 1;
91 io->rccr = 0;
92 io->rmds = 0;
93 io->lccr = 0; /* disable LCD */
94 io->vccr = 0; /* disable video */
95 io->i2mod = 0; /* stop I2C */
96 io->pcint = 0; /* disable all port C interrupts */
97 io->pcso = 0;
98 io->pcdir =0;
99 io->pcpar = 0;
100 io->pcdat = 0;
101 io->papar = 0;
102 io->padir = 0;
103 io->paodr = 0;
104 io->padat = 0;
105 io->pbpar = 0;
106 io->pbdir = 0;
107 io->pbodr = 0;
108 io->pbdat = 0;
109 io->tgcr = 0x2222; /* reset timers, low-power stop */
110 eieio();
111
112 for(io->cpcr = 0x8001; io->cpcr & 1;) /* reset all CPM channels */
113 eieio();
114
115 mapinit(&bdmap, bdmapv, sizeof(bdmapv));
116 mapfree(&bdmap, DPBASE, BDSIZE);
117 mapinit(&cpmmap, cpmmapv, sizeof(cpmmapv));
118 mapfree(&cpmmap, DPBASE+BDSIZE, CPMSIZE);
119
120 if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001)
121 brgens.avail = 0x3;
122 else
123 brgens.avail = 0xF;
124 i2cspireloc();
125 }
126
127 /*
128 * return parameters defining a CPM device, given logical ID
129 */
130 CPMdev*
cpmdev(int n)131 cpmdev(int n)
132 {
133 CPMdev *d;
134
135 if(n < 0 || n >= nelem(cpmdevinfo))
136 panic("cpmdev");
137 d = &cpmdevinfo[n];
138 if(d->param == nil && d->pbase != 0){
139 if((n == CPi2c || n == CPspi)){
140 d->param = relocateparam(d->pbase, 0xB0-0x80); /* relocate */
141 if(d->param == nil)
142 return nil;
143 } else
144 d->param = (char*)m->iomem+d->pbase;
145 }
146 if(d->rbase != 0)
147 d->regs = (char*)m->iomem+d->rbase;
148 return d;
149 }
150
151 /*
152 * issue a request to a CPM device
153 */
154 void
cpmop(CPMdev * cpd,int op,int param)155 cpmop(CPMdev *cpd, int op, int param)
156 {
157 IMM *io;
158
159 ilock(&cpmlock);
160 io = m->iomem;
161 while(io->cpcr & 1)
162 eieio();
163 io->cpcr = (op<<8)|(cpd->id<<4)|(param<<1)|1;
164 eieio();
165 while(io->cpcr & 1)
166 eieio();
167 iunlock(&cpmlock);
168 }
169
170 /*
171 * lock the shared IO memory and return a reference to it
172 */
173 IMM*
ioplock(void)174 ioplock(void)
175 {
176 ilock(&cpmlock);
177 return m->iomem;
178 }
179
180 /*
181 * release the lock on the shared IO memory
182 */
183 void
iopunlock(void)184 iopunlock(void)
185 {
186 eieio();
187 iunlock(&cpmlock);
188 }
189
190 /*
191 * connect SCCx clocks in NSMI mode (x=1 for USB)
192 */
193 void
sccnmsi(int x,int rcs,int tcs)194 sccnmsi(int x, int rcs, int tcs)
195 {
196 IMM *io;
197 ulong v;
198 int sh;
199
200 sh = (x-1)*8; /* each SCCx field in sicr is 8 bits */
201 v = (((rcs&7)<<3) | (tcs&7)) << sh;
202 io = ioplock();
203 io->sicr = (io->sicr & ~(0xFF<<sh)) | v;
204 iopunlock();
205 }
206
207 /*
208 * connect SMCx clock in NSMI mode
209 */
210 void
smcnmsi(int x,int cs)211 smcnmsi(int x, int cs)
212 {
213 IMM *io;
214 ulong v;
215 int sh;
216
217 if(x == 1)
218 sh = 0;
219 else
220 sh = 16;
221 v = cs << (12+sh);
222 io = ioplock();
223 io->simode = (io->simode & ~(0xF000<<sh)) | v; /* SMCx to NMSI mode, set Tx/Rx clock */
224 iopunlock();
225 }
226
227 /*
228 * claim the use of a CPM ID (SCC, SMC) that might be used by two mutually exclusive devices,
229 * for the caller determined by the given parameter (which must be unique).
230 * returns non-zero if the resource is already in use.
231 */
232 int
cpmidopen(int id,void * owner)233 cpmidopen(int id, void *owner)
234 {
235 Chanuse *use;
236
237 use = &cpmids[id];
238 ilock(use);
239 if(use->owner != nil && use->owner != owner){
240 iunlock(use);
241 return -1;
242 }
243 use->owner = owner;
244 iunlock(use);
245 return 0;
246 }
247
248 /*
249 * release a previously claimed CPM ID
250 */
251 void
cpmidclose(int id)252 cpmidclose(int id)
253 {
254 Chanuse *use;
255
256 use = &cpmids[id];
257 ilock(use);
258 use->owner = nil;
259 iunlock(use);
260 }
261
262 /*
263 * if SCC d is currently enabled, shut it down
264 */
265 void
sccxstop(CPMdev * d)266 sccxstop(CPMdev *d)
267 {
268 SCC *scc;
269
270 if(d == nil)
271 return;
272 scc = d->regs;
273 if(scc->gsmrl & (ENT|ENR)){
274 if(scc->gsmrl & ENT)
275 cpmop(d, GracefulStopTx, 0);
276 if(scc->gsmrl & ENR)
277 cpmop(d, CloseRxBD, 0);
278 delay(1);
279 scc->gsmrl &= ~(ENT|ENR); /* disable current use */
280 eieio();
281 }
282 scc->sccm = 0; /* mask interrupts */
283 }
284
285 /*
286 * if SMC d is currently enabled, shut it down
287 */
288 void
smcxstop(CPMdev * d)289 smcxstop(CPMdev *d)
290 {
291 SMC *smc;
292
293 if(d == nil)
294 return;
295 smc = d->regs;
296 if(smc->smcmr & (TEN|REN)){
297 if(smc->smcmr & TEN)
298 cpmop(d, StopTx, 0);
299 if(smc->smcmr & REN)
300 cpmop(d, CloseRxBD, 0);
301 delay(1);
302 smc->smcmr &= ~(TEN|REN);
303 eieio();
304 }
305 smc->smcm = 0; /* mask interrupts */
306 }
307
308 /*
309 * allocate a buffer descriptor
310 */
311 BD *
bdalloc(int n)312 bdalloc(int n)
313 {
314 ulong a;
315
316 a = rmapalloc(&bdmap, 0, n*sizeof(BD), sizeof(BD));
317 if(a == 0)
318 panic("bdalloc");
319 return KADDR(a);
320 }
321
322 /*
323 * free a buffer descriptor
324 */
325 void
bdfree(BD * b,int n)326 bdfree(BD *b, int n)
327 {
328 if(b){
329 eieio();
330 mapfree(&bdmap, PADDR(b), n*sizeof(BD));
331 }
332 }
333
334 /*
335 * print a buffer descriptor and its data (when debugging)
336 */
337 void
dumpbd(char * name,BD * b,int maxn)338 dumpbd(char *name, BD *b, int maxn)
339 {
340 uchar *d;
341 int i;
342
343 print("%s #%4.4lux: s=#%4.4ux l=%ud a=#%8.8lux", name, PADDR(b)&0xFFFF, b->status, b->length, b->addr);
344 if(maxn > b->length)
345 maxn = b->length;
346 if(b->addr != 0){
347 d = KADDR(b->addr);
348 for(i=0; i<maxn; i++)
349 print(" %2.2ux", d[i]);
350 if(i < b->length)
351 print(" ...");
352 }
353 print("\n");
354 }
355
356 /*
357 * allocate memory from the shared IO memory space
358 */
359 void *
cpmalloc(int n,int align)360 cpmalloc(int n, int align)
361 {
362 ulong a;
363
364 a = rmapalloc(&cpmmap, 0, n, align);
365 if(a == 0)
366 panic("cpmalloc");
367 return KADDR(a);
368 }
369
370 /*
371 * free previously allocated shared memory
372 */
373 void
cpmfree(void * p,int n)374 cpmfree(void *p, int n)
375 {
376 if(p != nil && n > 0){
377 eieio();
378 mapfree(&cpmmap, PADDR(p), n);
379 }
380 }
381
382 /*
383 * allocate a baud rate generator, returning its index
384 * (or -1 if none is available)
385 */
386 int
brgalloc(void)387 brgalloc(void)
388 {
389 int n;
390
391 lock(&brgens);
392 for(n=0; brgens.avail!=0; n++)
393 if(brgens.avail & (1<<n)){
394 brgens.avail &= ~(1<<n);
395 unlock(&brgens);
396 return n;
397 }
398 unlock(&brgens);
399 return -1;
400 }
401
402 /*
403 * free a previously allocated baud rate generator
404 */
405 void
brgfree(int n)406 brgfree(int n)
407 {
408 if(n >= 0){
409 if(n > 3 || brgens.avail & (1<<n))
410 panic("brgfree");
411 lock(&brgens);
412 brgens.avail |= 1 << n;
413 unlock(&brgens);
414 }
415 }
416
417 /*
418 * return a value suitable for loading into a baud rate
419 * generator to produce the given rate if the generator
420 * is prescaled by the given amount (typically 16).
421 * the value must be or'd with BaudEnable to start the generator.
422 */
423 ulong
baudgen(int rate,int scale)424 baudgen(int rate, int scale)
425 {
426 int d;
427
428 rate *= scale;
429 d = (2*m->cpuhz+rate)/(2*rate) - 1;
430 if(d < 0)
431 d = 0;
432 if(d >= (1<<12))
433 return ((d+15)>>(4-1))|1; /* divider too big: enable prescale by 16 */
434 return d<<1;
435 }
436
437 /*
438 * initialise receive and transmit buffer rings.
439 */
440 int
ioringinit(Ring * r,int nrdre,int ntdre,int bufsize)441 ioringinit(Ring* r, int nrdre, int ntdre, int bufsize)
442 {
443 int i, x;
444
445 /* the ring entries must be aligned on sizeof(BD) boundaries */
446 r->nrdre = nrdre;
447 if(r->rdr == nil)
448 r->rdr = bdalloc(nrdre);
449 /* the buffer size must align with cache lines since the cache doesn't snoop */
450 bufsize = (bufsize+CACHELINESZ-1)&~(CACHELINESZ-1);
451 if(r->rrb == nil)
452 r->rrb = malloc(nrdre*bufsize);
453 if(r->rdr == nil || r->rrb == nil)
454 return -1;
455 dcflush(r->rrb, nrdre*bufsize);
456 x = PADDR(r->rrb);
457 for(i = 0; i < nrdre; i++){
458 r->rdr[i].length = 0;
459 r->rdr[i].addr = x;
460 r->rdr[i].status = BDEmpty|BDInt;
461 x += bufsize;
462 }
463 r->rdr[i-1].status |= BDWrap;
464 r->rdrx = 0;
465
466 r->ntdre = ntdre;
467 if(r->tdr == nil)
468 r->tdr = bdalloc(ntdre);
469 if(r->txb == nil)
470 r->txb = malloc(ntdre*sizeof(Block*));
471 if(r->tdr == nil || r->txb == nil)
472 return -1;
473 for(i = 0; i < ntdre; i++){
474 r->txb[i] = nil;
475 r->tdr[i].addr = 0;
476 r->tdr[i].length = 0;
477 r->tdr[i].status = 0;
478 }
479 r->tdr[i-1].status |= BDWrap;
480 r->tdrh = 0;
481 r->tdri = 0;
482 r->ntq = 0;
483 return 0;
484 }
485
486 /*
487 * Allocate a new parameter block for I2C or SPI,
488 * and plant a pointer to it for the microcode, returning the kernel address.
489 * See Motorola errata and microcode package:
490 * the design botch is that the parameters for the SCC2 ethernet overlap the
491 * SPI/I2C parameter space; this compensates by relocating the latter.
492 * This routine may be used iff i2cspireloc is used (and it is, above).
493 */
494 static void*
relocateparam(ulong olda,int nb)495 relocateparam(ulong olda, int nb)
496 {
497 void *p;
498
499 if(olda < (ulong)m->iomem)
500 olda += (ulong)m->iomem;
501 p = cpmalloc(nb, 32); /* ``RPBASE must be multiple of 32'' */
502 if(p == nil)
503 return p;
504 *(ushort*)KADDR(olda+0x2C) = PADDR(p); /* set RPBASE */
505 eieio();
506 return p;
507 }
508
509 /*
510 * I2C/SPI microcode package from Motorola
511 * (to relocate I2C/SPI parameters), which was distributed
512 * on their web site in S-record format.
513 *
514 * May 1998
515 */
516
517 /*S00600004844521B*/
518 static ulong ubase1 = 0x2000;
519 static ulong ucode1[] = {
520 /* #02202000 */ 0x7FFFEFD9,
521 /* #02202004 */ 0x3FFD0000,
522 /* #02202008 */ 0x7FFB49F7,
523 /* #0220200C */ 0x7FF90000,
524 /* #02202010 */ 0x5FEFADF7,
525 /* #02202014 */ 0x5F89ADF7,
526 /* #02202018 */ 0x5FEFAFF7,
527 /* #0220201C */ 0x5F89AFF7,
528 /* #02202020 */ 0x3A9CFBC8,
529 /* #02202024 */ 0xE7C0EDF0,
530 /* #02202028 */ 0x77C1E1BB,
531 /* #0220202C */ 0xF4DC7F1D,
532 /* #02202030 */ 0xABAD932F,
533 /* #02202034 */ 0x4E08FDCF,
534 /* #02202038 */ 0x6E0FAFF8,
535 /* #0220203C */ 0x7CCF76CF,
536 /* #02202040 */ 0xFD1FF9CF,
537 /* #02202044 */ 0xABF88DC6,
538 /* #02202048 */ 0xAB5679F7,
539 /* #0220204C */ 0xB0937383,
540 /* #02202050 */ 0xDFCE79F7,
541 /* #02202054 */ 0xB091E6BB,
542 /* #02202058 */ 0xE5BBE74F,
543 /* #0220205C */ 0xB3FA6F0F,
544 /* #02202060 */ 0x6FFB76CE,
545 /* #02202064 */ 0xEE0DF9CF,
546 /* #02202068 */ 0x2BFBEFEF,
547 /* #0220206C */ 0xCFEEF9CF,
548 /* #02202070 */ 0x76CEAD24,
549 /* #02202074 */ 0x90B2DF9A,
550 /* #02202078 */ 0x7FDDD0BF,
551 /* #0220207C */ 0x4BF847FD,
552 /* #02202080 */ 0x7CCF76CE,
553 /* #02202084 */ 0xCFEF7E1F,
554 /* #02202088 */ 0x7F1D7DFD,
555 /* #0220208C */ 0xF0B6EF71,
556 /* #02202090 */ 0x7FC177C1,
557 /* #02202094 */ 0xFBC86079,
558 /* #02202098 */ 0xE722FBC8,
559 /* #0220209C */ 0x5FFFDFFF,
560 /* #022020A0 */ 0x5FB2FFFB,
561 /* #022020A4 */ 0xFBC8F3C8,
562 /* #022020A8 */ 0x94A67F01,
563 /* #022020AC */ 0x7F1D5F39,
564 /* #022020B0 */ 0xAFE85F5E,
565 /* #022020B4 */ 0xFFDFDF96,
566 /* #022020B8 */ 0xCB9FAF7D,
567 /* #022020BC */ 0x5FC1AFED,
568 /* #022020C0 */ 0x8C1C5FC1,
569 /* #022020C4 */ 0xAFDD5FC3,
570 /* #022020C8 */ 0xDF9A7EFD,
571 /* #022020CC */ 0xB0B25FB2,
572 /* #022020D0 */ 0xFFFEABAD,
573 /* #022020D4 */ 0x5FB2FFFE,
574 /* #022020D8 */ 0x5FCE600B,
575 /* #022020DC */ 0xE6BB600B,
576 /* #022020E0 */ 0x5FCEDFC6,
577 /* #022020E4 */ 0x27FBEFDF,
578 /* #022020E8 */ 0x5FC8CFDE,
579 /* #022020EC */ 0x3A9CE7C0,
580 /* #022020F0 */ 0xEDF0F3C8,
581 /* #022020F4 */ 0x7F0154CD,
582 /* #022020F8 */ 0x7F1D2D3D,
583 /* #022020FC */ 0x363A7570,
584 /* #02202100 */ 0x7E0AF1CE,
585 /* #02202104 */ 0x37EF2E68,
586 /* #02202108 */ 0x7FEE10EC,
587 /* #0220210C */ 0xADF8EFDE,
588 /* #02202110 */ 0xCFEAE52F,
589 /* #02202114 */ 0x7D0FE12B,
590 /* #02202118 */ 0xF1CE5F65,
591 /* #0220211C */ 0x7E0A4DF8,
592 /* #02202120 */ 0xCFEA5F72,
593 /* #02202124 */ 0x7D0BEFEE,
594 /* #02202128 */ 0xCFEA5F74,
595 /* #0220212C */ 0xE522EFDE,
596 /* #02202130 */ 0x5F74CFDA,
597 /* #02202134 */ 0x0B627385,
598 /* #02202138 */ 0xDF627E0A,
599 /* #0220213C */ 0x30D8145B,
600 /* #02202140 */ 0xBFFFF3C8,
601 /* #02202144 */ 0x5FFFDFFF,
602 /* #02202148 */ 0xA7F85F5E,
603 /* #0220214C */ 0xBFFE7F7D,
604 /* #02202150 */ 0x10D31450,
605 /* #02202154 */ 0x5F36BFFF,
606 /* #02202158 */ 0xAF785F5E,
607 /* #0220215C */ 0xBFFDA7F8,
608 /* #02202160 */ 0x5F36BFFE,
609 /* #02202164 */ 0x77FD30C0,
610 /* #02202168 */ 0x4E08FDCF,
611 /* #0220216C */ 0xE5FF6E0F,
612 /* #02202170 */ 0xAFF87E1F,
613 /* #02202174 */ 0x7E0FFD1F,
614 /* #02202178 */ 0xF1CF5F1B,
615 /* #0220217C */ 0xABF80D5E,
616 /* #02202180 */ 0x5F5EFFEF,
617 /* #02202184 */ 0x79F730A2,
618 /* #02202188 */ 0xAFDD5F34,
619 /* #0220218C */ 0x47F85F34,
620 /* #02202190 */ 0xAFED7FDD,
621 /* #02202194 */ 0x50B24978,
622 /* #02202198 */ 0x47FD7F1D,
623 /* #0220219C */ 0x7DFD70AD,
624 /* #022021A0 */ 0xEF717EC1,
625 /* #022021A4 */ 0x6BA47F01,
626 /* #022021A8 */ 0x2D267EFD,
627 /* #022021AC */ 0x30DE5F5E,
628 /* #022021B0 */ 0xFFFD5F5E,
629 /* #022021B4 */ 0xFFEF5F5E,
630 /* #022021B8 */ 0xFFDF0CA0,
631 /* #022021BC */ 0xAFED0A9E,
632 /* #022021C0 */ 0xAFDD0C3A,
633 /* #022021C4 */ 0x5F3AAFBD,
634 /* #022021C8 */ 0x7FBDB082,
635 /* #022021CC */ 0x5F8247F8,
636 };
637
638 /*S00600004844521B*/
639 static ulong ubase2 = 0x2F00;
640 static ulong ucode2[] = {
641 /* #02202F00 */ 0x3E303430,
642 /* #02202F04 */ 0x34343737,
643 /* #02202F08 */ 0xABF7BF9B,
644 /* #02202F0C */ 0x994B4FBD,
645 /* #02202F10 */ 0xBD599493,
646 /* #02202F14 */ 0x349FFF37,
647 /* #02202F18 */ 0xFB9B177D,
648 /* #02202F1C */ 0xD9936956,
649 /* #02202F20 */ 0xBBFDD697,
650 /* #02202F24 */ 0xBDD2FD11,
651 /* #02202F28 */ 0x31DB9BB3,
652 /* #02202F2C */ 0x63139637,
653 /* #02202F30 */ 0x93733693,
654 /* #02202F34 */ 0x193137F7,
655 /* #02202F38 */ 0x331737AF,
656 /* #02202F3C */ 0x7BB9B999,
657 /* #02202F40 */ 0xBB197957,
658 /* #02202F44 */ 0x7FDFD3D5,
659 /* #02202F48 */ 0x73B773F7,
660 /* #02202F4C */ 0x37933B99,
661 /* #02202F50 */ 0x1D115316,
662 /* #02202F54 */ 0x99315315,
663 /* #02202F58 */ 0x31694BF4,
664 /* #02202F5C */ 0xFBDBD359,
665 /* #02202F60 */ 0x31497353,
666 /* #02202F64 */ 0x76956D69,
667 /* #02202F68 */ 0x7B9D9693,
668 /* #02202F6C */ 0x13131979,
669 /* #02202F70 */ 0x79376935,
670 };
671
672 /*
673 * compensate for chip design botch by installing
674 * microcode to relocate I2C and SPI parameters away
675 * from the ethernet parameters
676 */
677 static void
i2cspireloc(void)678 i2cspireloc(void)
679 {
680 IMM *io;
681 static int done;
682
683 if(done)
684 return;
685 io = m->iomem;
686 io->rccr &= ~3;
687 memmove((uchar*)m->iomem+ubase1, ucode1, sizeof(ucode1));
688 memmove((uchar*)m->iomem+ubase2, ucode2, sizeof(ucode2));
689 io->rctr1 = 0x802a; /* relocate SPI */
690 io->rctr2 = 0x8028; /* relocate SPI */
691 io->rctr3 = 0x802e; /* relocate I2C */
692 io->rctr4 = 0x802c; /* relocate I2C */
693 io->rccr |= 1;
694 done = 1;
695 }
696