xref: /inferno-os/os/mpc/cpm.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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
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*
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
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*
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
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
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
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
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
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
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
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 *
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
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
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 *
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
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
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
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
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
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*
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
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