more usb compatiblity changes from rodries
[libogc.git] / libogc / exi.c
blobb0f595b0dd76e1c3ed56d7c938fd1b8189742603
1 /*-------------------------------------------------------------
3 exi.c -- EXI subsystem
5 Copyright (C) 2004
6 Michael Wiedenbauer (shagkur)
7 Dave Murphy (WinterMute)
9 This software is provided 'as-is', without any express or implied
10 warranty. In no event will the authors be held liable for any
11 damages arising from the use of this software.
13 Permission is granted to anyone to use this software for any
14 purpose, including commercial applications, and to alter it and
15 redistribute it freely, subject to the following restrictions:
17 1. The origin of this software must not be misrepresented; you
18 must not claim that you wrote the original software. If you use
19 this software in a product, an acknowledgment in the product
20 documentation would be appreciated but is not required.
22 2. Altered source versions must be plainly marked as such, and
23 must not be misrepresented as being the original software.
25 3. This notice may not be removed or altered from any source
26 distribution.
28 -------------------------------------------------------------*/
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include "asm.h"
35 #include "irq.h"
36 #include "processor.h"
37 #include "spinlock.h"
38 #include "exi.h"
40 //#define _EXI_DEBUG
42 #define EXI_LOCK_DEVS 32
44 #define EXI_MAX_CHANNELS 3
45 #define EXI_MAX_DEVICES 3
47 #define EXI_DEVICE0 0x0080
48 #define EXI_DEVICE1 0x0100
49 #define EXI_DEVICE2 0x0200
51 #define EXI_EXI_IRQ 0x0002
52 #define EXI_TC_IRQ 0x0008
53 #define EXI_EXT_IRQ 0x0800
54 #define EXI_EXT_BIT 0x1000
56 #define _SHIFTL(v, s, w) \
57 ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s)))
58 #define _SHIFTR(v, s, w) \
59 ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1)))
62 struct _lck_dev {
63 lwp_node node;
64 u32 dev;
65 EXICallback unlockcb;
68 typedef struct _exibus_priv {
69 EXICallback CallbackEXI;
70 EXICallback CallbackTC;
71 EXICallback CallbackEXT;
73 u32 imm_len;
74 void *imm_buff;
75 u32 lockeddev;
76 u32 flags;
77 u32 lck_cnt;
78 u32 exi_id;
79 u64 exi_idtime;
80 lwp_queue lckd_dev;
81 u32 lckd_dev_bits;
82 } exibus_priv;
84 static lwp_queue _lckdev_queue;
85 static struct _lck_dev lckdevs[EXI_LOCK_DEVS];
86 static exibus_priv eximap[EXI_MAX_CHANNELS];
87 static u64 last_exi_idtime[EXI_MAX_CHANNELS];
89 static u32 exi_id_serport1 = 0;
91 static u32 exi_uart_chan = EXI_CHANNEL_0;
92 static u32 exi_uart_dev = EXI_DEVICE_0;
93 static u32 exi_uart_barnacle_enabled = 0;
94 static u32 exi_uart_enabled = 0;
96 static void __exi_irq_handler(u32,void *);
97 static void __tc_irq_handler(u32,void *);
98 static void __ext_irq_handler(u32,void *);
100 #if defined(HW_DOL)
101 static vu32* const _exiReg = (u32*)0xCC006800;
102 #elif defined(HW_RVL)
103 static vu32* const _exiReg = (u32*)0xCD006800;
104 #else
105 #error HW model unknown.
106 #endif
108 static __inline__ void __exi_clearirqs(s32 nChn,u32 nEXIIrq,u32 nTCIrq,u32 nEXTIrq)
110 u32 d;
111 #ifdef _EXI_DEBUG
112 printf("__exi_clearirqs(%d,%d,%d,%d)\n",nChn,nEXIIrq,nTCIrq,nEXTIrq);
113 #endif
114 d = (_exiReg[nChn*5]&~(EXI_EXI_IRQ|EXI_TC_IRQ|EXI_EXT_IRQ));
115 if(nEXIIrq) d |= EXI_EXI_IRQ;
116 if(nTCIrq) d |= EXI_TC_IRQ;
117 if(nEXTIrq) d |= EXI_EXT_IRQ;
118 _exiReg[nChn*5] = d;
121 static __inline__ void __exi_setinterrupts(s32 nChn,exibus_priv *exi)
123 exibus_priv *pexi = &eximap[EXI_CHANNEL_2];
124 #ifdef _EXI_DEBUG
125 printf("__exi_setinterrupts(%d,%p)\n",nChn,exi);
126 #endif
127 if(nChn==EXI_CHANNEL_0) {
128 __MaskIrq((IRQMASK(IRQ_EXI0_EXI)|IRQMASK(IRQ_EXI2_EXI)));
129 if(!(exi->flags&EXI_FLAG_LOCKED) && (exi->CallbackEXI || pexi->CallbackEXI))
130 __UnmaskIrq((IRQMASK(IRQ_EXI0_EXI)|IRQMASK(IRQ_EXI2_EXI)));
131 } else if(nChn==EXI_CHANNEL_1) {
132 __MaskIrq(IRQMASK(IRQ_EXI1_EXI));
133 if(!(exi->flags&EXI_FLAG_LOCKED) && exi->CallbackEXI) __UnmaskIrq(IRQMASK(IRQ_EXI1_EXI));
134 } else if(nChn==EXI_CHANNEL_2) { //explicitly use of channel 2 only if debugger is attached.
135 __MaskIrq(IRQMASK(IRQ_EXI0_EXI));
136 if(!(exi->flags&EXI_FLAG_LOCKED) && IRQ_GetHandler(IRQ_PI_DEBUG)) __UnmaskIrq(IRQMASK(IRQ_EXI2_EXI));
140 static void __exi_initmap(exibus_priv *exim)
142 s32 i;
143 exibus_priv *m;
145 __lwp_queue_initialize(&_lckdev_queue,lckdevs,EXI_LOCK_DEVS,sizeof(struct _lck_dev));
147 for(i=0;i<EXI_MAX_CHANNELS;i++) {
148 m = &exim[i];
149 m->CallbackEXI = NULL;
150 m->CallbackEXT = NULL;
151 m->CallbackTC = NULL;
152 m->imm_buff = NULL;
153 m->exi_id = 0;
154 m->exi_idtime = 0;
155 m->flags = 0;
156 m->imm_len = 0;
157 m->lck_cnt = 0;
158 m->lockeddev = 0;
159 m->lckd_dev_bits = 0;
160 __lwp_queue_init_empty(&m->lckd_dev);
164 static s32 __exi_probe(s32 nChn)
166 u64 time;
167 s32 ret = 1;
168 u32 level;
169 u32 val;
170 exibus_priv *exi = &eximap[nChn];
171 #ifdef _EXI_DEBUG
172 printf("__exi_probe(%d)\n",nChn);
173 #endif
174 _CPU_ISR_Disable(level);
175 val = _exiReg[nChn*5];
176 if(!(exi->flags&EXI_FLAG_ATTACH)) {
177 if(val&EXI_EXT_IRQ) {
178 __exi_clearirqs(nChn,0,0,1);
179 exi->exi_idtime = 0;
180 last_exi_idtime[nChn] = 0;
182 if(_exiReg[nChn*5]&EXI_EXT_BIT) {
183 time = gettime();
184 if(last_exi_idtime[nChn]==0) last_exi_idtime[nChn] = time;
185 if((val=diff_usec(last_exi_idtime[nChn],time)+10)<30) ret = 0;
186 else ret = 1;
187 #ifdef _EXI_DEBUG
188 printf("val = %u, ret = %d, last_exi_idtime[chn] = %llu\n",val,ret,last_exi_idtime[nChn]);
189 #endif
190 _CPU_ISR_Restore(level);
191 return ret;
192 } else {
193 exi->exi_idtime = 0;
194 last_exi_idtime[nChn] = 0;
195 _CPU_ISR_Restore(level);
196 return 0;
200 if(!(_exiReg[nChn*5]&EXI_EXT_BIT) || (_exiReg[nChn*5]&EXI_EXT_IRQ)) {
201 exi->exi_idtime = 0;
202 last_exi_idtime[nChn] = 0;
203 ret = 0;
205 _CPU_ISR_Restore(level);
206 return ret;
209 static s32 __exi_attach(s32 nChn,EXICallback ext_cb)
211 s32 ret;
212 u32 level;
213 exibus_priv *exi = &eximap[nChn];
214 #ifdef _EXI_DEBUG
215 printf("__exi_attach(%d,%p)\n",nChn,ext_cb);
216 #endif
217 _CPU_ISR_Disable(level);
218 ret = 0;
219 if(!(exi->flags&EXI_FLAG_ATTACH)) {
220 if(__exi_probe(nChn)==1) {
221 __exi_clearirqs(nChn,1,0,0);
222 exi->CallbackEXT = ext_cb;
223 __UnmaskIrq(((IRQMASK(IRQ_EXI0_EXT))>>(nChn*3)));
224 exi->flags |= EXI_FLAG_ATTACH;
225 ret = 1;
228 _CPU_ISR_Restore(level);
229 return ret;
232 s32 EXI_Lock(s32 nChn,s32 nDev,EXICallback unlockCB)
234 u32 level;
235 struct _lck_dev *lckd;
236 exibus_priv *exi = &eximap[nChn];
237 #ifdef _EXI_DEBUG
238 printf("EXI_Lock(%d,%d,%p)\n",nChn,nDev,unlockCB);
239 #endif
240 _CPU_ISR_Disable(level);
241 if(exi->flags&EXI_FLAG_LOCKED) {
242 if(unlockCB && !(exi->lckd_dev_bits&(1<<nDev))) {
243 lckd = (struct _lck_dev*)__lwp_queue_getI(&_lckdev_queue);
244 if(lckd) {
245 exi->lck_cnt++;
246 exi->lckd_dev_bits |= (1<<nDev);
247 lckd->dev = nDev;
248 lckd->unlockcb = unlockCB;
249 __lwp_queue_appendI(&exi->lckd_dev,&lckd->node);
252 _CPU_ISR_Restore(level);
253 return 0;
256 exi->lockeddev = nDev;
257 exi->flags |= EXI_FLAG_LOCKED;
258 __exi_setinterrupts(nChn,exi);
260 _CPU_ISR_Restore(level);
261 return 1;
264 s32 EXI_Unlock(s32 nChn)
266 u32 level,dev;
267 EXICallback cb;
268 struct _lck_dev *lckd;
269 exibus_priv *exi = &eximap[nChn];
270 #ifdef _EXI_DEBUG
271 printf("EXI_Unlock(%d)\n",nChn);
272 #endif
273 _CPU_ISR_Disable(level);
274 if(!(exi->flags&EXI_FLAG_LOCKED)) {
275 _CPU_ISR_Restore(level);
276 return 0;
279 exi->flags &= ~EXI_FLAG_LOCKED;
280 __exi_setinterrupts(nChn,exi);
282 if(!exi->lck_cnt) {
283 _CPU_ISR_Restore(level);
284 return 1;
287 exi->lck_cnt--;
288 lckd = (struct _lck_dev*)__lwp_queue_getI(&exi->lckd_dev);
289 __lwp_queue_appendI(&_lckdev_queue,&lckd->node);
291 cb = lckd->unlockcb;
292 dev = lckd->dev;
293 exi->lckd_dev_bits &= ~(1<<dev);
294 if(cb) cb(nChn,dev);
296 _CPU_ISR_Restore(level);
297 return 1;
300 s32 EXI_Select(s32 nChn,s32 nDev,s32 nFrq)
302 u32 val;
303 u32 level;
304 exibus_priv *exi = &eximap[nChn];
305 #ifdef _EXI_DEBUG
306 printf("EXI_Select(%d,%d,%d)\n",nChn,nDev,nFrq);
307 #endif
308 _CPU_ISR_Disable(level);
310 if(exi->flags&EXI_FLAG_SELECT) {
311 #ifdef _EXI_DEBUG
312 printf("EXI_Select(): allready selected.\n");
313 #endif
314 _CPU_ISR_Restore(level);
315 return 0;
318 if(nChn!=EXI_CHANNEL_2) {
319 if(nDev==EXI_DEVICE_0 && !(exi->flags&EXI_FLAG_ATTACH)) {
320 if(__exi_probe(nChn)==0) {
321 _CPU_ISR_Restore(level);
322 return 0;
325 if(!(exi->flags&EXI_FLAG_LOCKED) || exi->lockeddev!=nDev) {
326 #ifdef _EXI_DEBUG
327 printf("EXI_Select(): not locked or wrong dev(%d).\n",exi->lockeddev);
328 #endif
329 _CPU_ISR_Restore(level);
330 return 0;
334 exi->flags |= EXI_FLAG_SELECT;
335 val = _exiReg[nChn*5];
336 val = (val&0x405)|(0x80<<nDev)|(nFrq<<4);
337 _exiReg[nChn*5] = val;
339 if(exi->flags&EXI_FLAG_ATTACH) {
340 if(nChn==EXI_CHANNEL_0) __MaskIrq(IRQMASK(IRQ_EXI0_EXT));
341 else if(nChn==EXI_CHANNEL_1) __MaskIrq(IRQMASK(IRQ_EXI1_EXT));
344 _CPU_ISR_Restore(level);
345 return 1;
348 s32 EXI_SelectSD(s32 nChn,s32 nDev,s32 nFrq)
350 u32 val,id;
351 s32 ret;
352 u32 level;
353 exibus_priv *exi = &eximap[nChn];
354 #ifdef _EXI_DEBUG
355 printf("EXI_SelectSD(%d,%d,%d)\n",nChn,nDev,nFrq);
356 #endif
357 _CPU_ISR_Disable(level);
359 if(exi->flags&EXI_FLAG_SELECT) {
360 #ifdef _EXI_DEBUG
361 printf("EXI_SelectSD(): allready selected.\n");
362 #endif
363 _CPU_ISR_Restore(level);
364 return 0;
367 if(nChn!=EXI_CHANNEL_2) {
368 if(nDev==EXI_DEVICE_0 && !(exi->flags&EXI_FLAG_ATTACH)) {
369 if((ret=__exi_probe(nChn))==1) {
370 if(!exi->exi_idtime) ret = EXI_GetID(nChn,EXI_DEVICE_0,&id);
372 if(ret==0) {
373 _CPU_ISR_Restore(level);
374 return 0;
377 if(!(exi->flags&EXI_FLAG_LOCKED) || exi->lockeddev!=nDev) {
378 #ifdef _EXI_DEBUG
379 printf("EXI_SelectSD(): not locked or wrong dev(%d).\n",exi->lockeddev);
380 #endif
381 _CPU_ISR_Restore(level);
382 return 0;
386 exi->flags |= EXI_FLAG_SELECT;
387 val = _exiReg[nChn*5];
388 val = (val&0x405)|(nFrq<<4);
389 _exiReg[nChn*5] = val;
391 if(exi->flags&EXI_FLAG_ATTACH) {
392 if(nChn==EXI_CHANNEL_0) __MaskIrq(IRQMASK(IRQ_EXI0_EXT));
393 else if(nChn==EXI_CHANNEL_1) __MaskIrq(IRQMASK(IRQ_EXI1_EXT));
396 _CPU_ISR_Restore(level);
397 return 1;
400 s32 EXI_Deselect(s32 nChn)
402 u32 val;
403 u32 level;
404 exibus_priv *exi = &eximap[nChn];
405 #ifdef _EXI_DEBUG
406 printf("EXI_Deselect(%d)\n",nChn);
407 #endif
408 _CPU_ISR_Disable(level);
410 if(!(exi->flags&EXI_FLAG_SELECT)) {
411 _CPU_ISR_Restore(level);
412 return 0;
415 exi->flags &= ~EXI_FLAG_SELECT;
416 val = _exiReg[nChn*5];
417 _exiReg[nChn*5] = (val&0x405);
419 if(exi->flags&EXI_FLAG_ATTACH) {
420 if(nChn==EXI_CHANNEL_0) __UnmaskIrq(IRQMASK(IRQ_EXI0_EXT));
421 else if(nChn==EXI_CHANNEL_1) __UnmaskIrq(IRQMASK(IRQ_EXI1_EXT));
424 if(nChn!=EXI_CHANNEL_2 && val&EXI_DEVICE0) {
425 if(__exi_probe(nChn)==0) {
426 _CPU_ISR_Restore(level);
427 return 0;
430 _CPU_ISR_Restore(level);
431 return 1;
434 s32 EXI_Sync(s32 nChn)
436 u8 *buf;
437 s32 ret;
438 u32 level,i,cnt,val;
439 exibus_priv *exi = &eximap[nChn];
440 #ifdef _EXI_DEBUG
441 printf("EXI_Sync(%d)\n",nChn);
442 #endif
443 while(_exiReg[nChn*5+3]&0x0001);
445 _CPU_ISR_Disable(level);
447 ret = 0;
448 if(exi->flags&EXI_FLAG_SELECT && exi->flags&(EXI_FLAG_DMA|EXI_FLAG_IMM)) {
449 if(exi->flags&EXI_FLAG_IMM) {
450 cnt = exi->imm_len;
451 buf = exi->imm_buff;
452 if(buf && cnt>0) {
453 val = _exiReg[nChn*5+4];
454 for(i=0;i<cnt;i++) ((u8*)buf)[i] = (val>>((3-i)*8))&0xFF;
457 exi->flags &= ~(EXI_FLAG_DMA|EXI_FLAG_IMM);
458 ret = 1;
460 _CPU_ISR_Restore(level);
461 return ret;
464 s32 EXI_Imm(s32 nChn,void *pData,u32 nLen,u32 nMode,EXICallback tc_cb)
466 u32 level;
467 u32 value,i;
468 exibus_priv *exi = &eximap[nChn];
469 #ifdef _EXI_DEBUG
470 printf("EXI_Imm(%d,%p,%d,%d,%p)\n",nChn,pData,nLen,nMode,tc_cb);
471 #endif
472 _CPU_ISR_Disable(level);
474 if(exi->flags&(EXI_FLAG_DMA|EXI_FLAG_IMM) || !(exi->flags&EXI_FLAG_SELECT)) {
475 _CPU_ISR_Restore(level);
476 return 0;
479 exi->CallbackTC = tc_cb;
480 if(tc_cb) {
481 __exi_clearirqs(nChn,0,1,0);
482 __UnmaskIrq(IRQMASK((IRQ_EXI0_TC+(nChn*3))));
484 exi->flags |= EXI_FLAG_IMM;
486 exi->imm_buff = pData;
487 exi->imm_len = nLen;
488 if(nMode!=EXI_READ) {
489 for(i=0,value=0;i<nLen;i++) value |= (((u8*)pData)[i])<<((3-i)*8);
490 _exiReg[nChn*5+4] = value;
492 if(nMode==EXI_WRITE) exi->imm_len = 0;
494 _exiReg[nChn*5+3] = (((nLen-1)&0x03)<<4)|((nMode&0x03)<<2)|0x01;
496 _CPU_ISR_Restore(level);
497 return 1;
500 s32 EXI_ImmEx(s32 nChn,void *pData,u32 nLen,u32 nMode)
502 u8 *buf = pData;
503 u32 tc;
504 s32 ret = 0;
505 #ifdef _EXI_DEBUG
506 printf("EXI_ImmEx(%d,%p,%d,%d)\n",nChn,pData,nLen,nMode);
507 #endif
508 while(nLen) {
509 ret = 0;
510 tc = nLen;
511 if(tc>4) tc = 4;
513 if(!EXI_Imm(nChn,buf,tc,nMode,NULL)) break;
514 if(!EXI_Sync(nChn)) break;
515 nLen -= tc;
516 buf += tc;
518 ret = 1;
520 return ret;
523 s32 EXI_Dma(s32 nChn,void *pData,u32 nLen,u32 nMode,EXICallback tc_cb)
525 u32 level;
526 exibus_priv *exi = &eximap[nChn];
527 #ifdef _EXI_DEBUG
528 printf("EXI_Dma(%d,%p,%d,%d,%p)\n",nChn,pData,nLen,nMode,tc_cb);
529 #endif
530 _CPU_ISR_Disable(level);
532 if(exi->flags&(EXI_FLAG_DMA|EXI_FLAG_IMM) || !(exi->flags&EXI_FLAG_SELECT)) {
533 #ifdef _EXI_DEBUG
534 printf("EXI_Dma(%04x): abort\n",exi->flags);
535 #endif
536 _CPU_ISR_Restore(level);
537 return 0;
539 #ifdef _EXI_DEBUG
540 printf("EXI_Dma(tccb: %p)\n",tc_cb);
541 #endif
542 exi->CallbackTC = tc_cb;
543 if(tc_cb) {
544 __exi_clearirqs(nChn,0,1,0);
545 __UnmaskIrq((IRQMASK((IRQ_EXI0_TC+(nChn*3)))));
548 exi->imm_buff = NULL;
549 exi->imm_len = 0;
550 exi->flags |= EXI_FLAG_DMA;
552 _exiReg[nChn*5+1] = (u32)pData&0x03FFFFE0;
553 _exiReg[nChn*5+2] = nLen;
554 _exiReg[nChn*5+3] = ((nMode&0x03)<<2)|0x03;
556 _CPU_ISR_Restore(level);
557 return 1;
560 s32 EXI_GetState(s32 nChn)
562 exibus_priv *exi = &eximap[nChn];
563 return exi->flags;
566 static s32 __unlocked_handler(s32 nChn,s32 nDev)
568 u32 nId;
569 #ifdef _EXI_DEBUG
570 printf("__unlocked_handler(%d,%d)\n",nChn,nDev);
571 #endif
572 EXI_GetID(nChn,nDev,&nId);
573 return 1;
576 s32 EXI_GetID(s32 nChn,s32 nDev,u32 *nId)
578 u64 idtime = 0;
579 s32 ret,lck;
580 u32 reg,level;
581 exibus_priv *exi = &eximap[nChn];
583 #ifdef _EXI_DEBUG
584 printf("EXI_GetID(exi_id = %d)\n",exi->exi_id);
585 #endif
586 if(nChn<EXI_CHANNEL_2 && nDev==EXI_DEVICE_0) {
587 if(__exi_probe(nChn)==0) return 0;
588 if(exi->exi_idtime==last_exi_idtime[nChn]) {
589 #ifdef _EXI_DEBUG
590 printf("EXI_GetID(exi_id = %d)\n",exi->exi_id);
591 #endif
592 *nId = exi->exi_id;
593 return 1;
595 #ifdef _EXI_DEBUG
596 printf("EXI_GetID(setting interrupts,%08x)\n",exi->flags);
597 #endif
598 if(__exi_attach(nChn,NULL)==0) return 0;
599 idtime = last_exi_idtime[nChn];
601 #ifdef _EXI_DEBUG
602 printf("EXI_GetID(interrupts set)\n");
603 #endif
604 lck = 0;
605 if(nChn<EXI_CHANNEL_2 && nDev==EXI_DEVICE_0) lck = 1;
607 if(lck) ret = EXI_Lock(nChn,nDev,__unlocked_handler);
608 else ret = EXI_Lock(nChn,nDev,NULL);
610 if(ret) {
611 if(EXI_Select(nChn,nDev,EXI_SPEED1MHZ)==1) {
612 reg = 0;
613 EXI_Imm(nChn,&reg,2,EXI_WRITE,NULL);
614 EXI_Sync(nChn);
615 EXI_Imm(nChn,nId,4,EXI_READ,NULL);
616 EXI_Sync(nChn);
617 EXI_Deselect(nChn);
618 EXI_Unlock(nChn);
622 if(nChn<EXI_CHANNEL_2 && nDev==EXI_DEVICE_0) {
623 ret = 0;
624 EXI_Detach(nChn);
626 _CPU_ISR_Disable(level);
627 if(idtime==last_exi_idtime[nChn]) {
628 exi->exi_idtime = idtime;
629 exi->exi_id = *nId;
630 ret = 1;
632 _CPU_ISR_Restore(level);
633 #ifdef _EXI_DEBUG
634 printf("EXI_GetID(exi_id = %d)\n",exi->exi_id);
635 #endif
637 return ret;
640 s32 EXI_Attach(s32 nChn,EXICallback ext_cb)
642 s32 ret;
643 u32 level;
644 exibus_priv *exi = &eximap[nChn];
645 #ifdef _EXI_DEBUG
646 printf("EXI_Attach(%d)\n",nChn);
647 #endif
648 EXI_Probe(nChn);
650 _CPU_ISR_Disable(level);
651 if(exi->exi_idtime) {
652 ret = __exi_attach(nChn,ext_cb);
653 } else
654 ret = 0;
655 _CPU_ISR_Restore(level);
656 return ret;
659 s32 EXI_Detach(s32 nChn)
661 u32 level;
662 s32 ret = 1;
663 exibus_priv *exi = &eximap[nChn];
664 #ifdef _EXI_DEBUG
665 printf("EXI_Detach(%d)\n",nChn);
666 #endif
667 _CPU_ISR_Disable(level);
668 if(exi->flags&EXI_FLAG_ATTACH) {
669 if(exi->flags&EXI_FLAG_LOCKED && exi->lockeddev!=EXI_DEVICE_0) ret = 0;
670 else {
671 exi->flags &= ~EXI_FLAG_ATTACH;
672 __MaskIrq(((IRQMASK(IRQ_EXI0_EXI)|IRQMASK(IRQ_EXI0_TC)|IRQMASK(IRQ_EXI0_EXT))>>(nChn*3)));
675 _CPU_ISR_Restore(level);
676 return ret;
679 EXICallback EXI_RegisterEXICallback(s32 nChn,EXICallback exi_cb)
681 u32 level;
682 EXICallback old = NULL;
683 exibus_priv *exi = &eximap[nChn];
684 #ifdef _EXI_DEBUG
685 printf("EXI_RegisterEXICallback(%d,%p)\n",nChn,exi_cb);
686 #endif
687 _CPU_ISR_Disable(level);
688 old = exi->CallbackEXI;
689 exi->CallbackEXI = exi_cb;
690 if(nChn==EXI_CHANNEL_2) __exi_setinterrupts(EXI_CHANNEL_0,&eximap[EXI_CHANNEL_0]);
691 else __exi_setinterrupts(nChn,exi);
692 _CPU_ISR_Restore(level);
693 return old;
696 s32 EXI_Probe(s32 nChn)
698 s32 ret;
699 u32 id;
700 exibus_priv *exi = &eximap[nChn];
701 #ifdef _EXI_DEBUG
702 printf("EXI_Probe(%d)\n",nChn);
703 #endif
704 if((ret=__exi_probe(nChn))==1) {
705 if(exi->exi_idtime==0) {
706 if(EXI_GetID(nChn,EXI_DEVICE_0,&id)==0) ret = 0;
709 return ret;
712 s32 EXI_ProbeEx(s32 nChn)
714 if(EXI_Probe(nChn)==1) return 1;
715 if(last_exi_idtime[nChn]==0) return -1;
716 return 0;
719 void EXI_ProbeReset()
721 last_exi_idtime[0] = 0;
722 last_exi_idtime[1] = 0;
724 eximap[0].exi_idtime = 0;
725 eximap[1].exi_idtime = 0;
727 __exi_probe(0);
728 __exi_probe(1);
729 EXI_GetID(EXI_CHANNEL_0,EXI_DEVICE_2,&exi_id_serport1);
732 void __exi_init()
734 #ifdef _EXI_DEBUG
735 printf("__exi_init(): init expansion system.\n");
736 #endif
737 __MaskIrq(IM_EXI);
739 _exiReg[0] = 0;
740 _exiReg[5] = 0;
741 _exiReg[10] = 0;
743 _exiReg[0] = 0x2000;
745 __exi_initmap(eximap);
747 IRQ_Request(IRQ_EXI0_EXI,__exi_irq_handler,NULL);
748 IRQ_Request(IRQ_EXI0_TC,__tc_irq_handler,NULL);
749 IRQ_Request(IRQ_EXI0_EXT,__ext_irq_handler,NULL);
750 IRQ_Request(IRQ_EXI1_EXI,__exi_irq_handler,NULL);
751 IRQ_Request(IRQ_EXI1_TC,__tc_irq_handler,NULL);
752 IRQ_Request(IRQ_EXI1_EXT,__ext_irq_handler,NULL);
753 IRQ_Request(IRQ_EXI2_EXI,__exi_irq_handler,NULL);
754 IRQ_Request(IRQ_EXI2_TC,__tc_irq_handler,NULL);
756 EXI_ProbeReset();
759 void __exi_irq_handler(u32 nIrq,void *pCtx)
761 u32 chan,dev;
762 exibus_priv *exi = NULL;
763 const u32 fact = 0x55555556;
765 chan = ((fact*(nIrq-IRQ_EXI0_EXI))>>1)&0x0f;
766 dev = _SHIFTR((_exiReg[chan*5]&0x380),8,2);
768 exi = &eximap[chan];
769 __exi_clearirqs(chan,1,0,0);
771 if(!exi->CallbackEXI) return;
772 #ifdef _EXI_DEBUG
773 printf("__exi_irq_handler(%p)\n",exi->CallbackEXI);
774 #endif
775 exi->CallbackEXI(chan,dev);
778 void __tc_irq_handler(u32 nIrq,void *pCtx)
780 u32 cnt,len,d,chan,dev;
781 EXICallback tccb;
782 void *buf = NULL;
783 exibus_priv *exi = NULL;
784 const u32 fact = 0x55555556;
786 chan = ((fact*(nIrq-IRQ_EXI0_TC))>>1)&0x0f;
787 dev = _SHIFTR((_exiReg[chan*5]&0x380),8,2);
789 exi = &eximap[chan];
790 __MaskIrq(IRQMASK(nIrq));
791 __exi_clearirqs(chan,0,1,0);
793 tccb = exi->CallbackTC;
794 #ifdef _EXI_DEBUG
795 printf("__tc_irq_handler(%p)\n",tccb);
796 #endif
797 if(!tccb) return;
799 exi->CallbackTC = NULL;
800 if(exi->flags&(EXI_FLAG_DMA|EXI_FLAG_IMM)) {
801 if(exi->flags&EXI_FLAG_IMM) {
802 len = exi->imm_len;
803 buf = exi->imm_buff;
804 if(len>0 && buf) {
805 d = _exiReg[chan*5+4];
806 if(d>0) {
807 for(cnt=0;cnt<len;cnt++) ((u8*)buf)[cnt] = (d>>((3-cnt)*8))&0xFF;
811 exi->flags &= ~(EXI_FLAG_DMA|EXI_FLAG_IMM);
813 tccb(chan,dev);
816 void __ext_irq_handler(u32 nIrq,void *pCtx)
819 u32 chan,dev;
820 exibus_priv *exi = NULL;
821 const u32 fact = 0x55555556;
823 chan = ((fact*(nIrq-IRQ_EXI0_EXT))>>1)&0x0f;
824 dev = _SHIFTR((_exiReg[chan*5]&0x380),8,2);
826 exi = &eximap[chan];
827 __MaskIrq(IRQMASK(nIrq));
828 __exi_clearirqs(chan,0,0,1);
830 exi->flags &= ~EXI_FLAG_ATTACH;
831 if(exi->CallbackEXT) exi->CallbackEXT(chan,dev);
832 #ifdef _EXI_DEBUG
833 printf("__ext_irq_handler(%p)\n",exi->CallbackEXT);
834 #endif
838 /* EXI UART stuff */
839 static s32 __probebarnacle(s32 chn,u32 dev,u32 *rev)
841 u32 ret,reg;
843 if(chn!=EXI_CHANNEL_2 && dev==EXI_DEVICE_0) {
844 if(EXI_Attach(chn,NULL)==0) return 0;
847 ret = 0;
848 if(EXI_Lock(chn,dev,NULL)==1) {
849 if(EXI_Select(chn,dev,EXI_SPEED1MHZ)==1) {
850 reg = 0x20011300;
851 if(EXI_Imm(chn,&reg,sizeof(u32),EXI_WRITE,NULL)==0) ret |= 0x0001;
852 if(EXI_Sync(chn)==0) ret |= 0x0002;
853 if(EXI_Imm(chn,rev,sizeof(u32),EXI_READ,NULL)==0) ret |= 0x0004;
854 if(EXI_Sync(chn)==0) ret |= 0x0008;
855 if(EXI_Deselect(chn)==0) ret |= 0x0010;
858 EXI_Unlock(chn);
861 if(chn!=EXI_CHANNEL_2 && dev==EXI_DEVICE_0) EXI_Detach(chn);
863 if(ret) return 0;
864 if((*rev+0x00010000)==0xffff) return 0;
866 return 1;
869 static s32 __queuelength()
871 u32 reg;
872 u8 len = 0;
874 if(EXI_Select(exi_uart_chan,exi_uart_dev,EXI_SPEED8MHZ)==0) return -1;
876 reg = 0x20010000;
877 EXI_Imm(exi_uart_chan,&reg,sizeof(u32),EXI_WRITE,NULL);
878 EXI_Sync(exi_uart_chan);
879 EXI_Imm(exi_uart_chan,&len,sizeof(u8),EXI_READ,NULL);
880 EXI_Sync(exi_uart_chan);
882 EXI_Deselect(exi_uart_chan);
884 return (16-len);
887 void __SYS_EnableBarnacle(s32 chn,u32 dev)
889 u32 id,rev;
891 if(EXI_GetID(chn,dev,&id)==0) return;
893 if(id==0x01020000 || id==0x0004 || id==0x80000010 || id==0x80000008
894 || id==0x80000004 || id==0xffff || id==0x80000020 || id==0x0020
895 || id==0x0010 || id==0x0008 || id==0x01010000 || id==0x04040404
896 || id==0x04021000 || id==0x03010000 || id==0x02020000
897 || id==0x04020300 || id==0x04020200 || id==0x04130000
898 || id==0x04120000 || id==0x04060000 || id==0x04220000) return;
900 if(__probebarnacle(chn,dev,&rev)==0) return;
903 exi_uart_chan = chn;
904 exi_uart_dev = dev;
905 exi_uart_barnacle_enabled = 0xa5ff005a;
906 exi_uart_enabled = 0xa5ff005a;
909 s32 InitializeUART()
911 if((exi_uart_enabled+0x5a010000)==0x005a) return 0;
913 exi_uart_chan = EXI_CHANNEL_0;
914 exi_uart_dev = EXI_DEVICE_1;
916 exi_uart_enabled = 0xa5ff005a;
917 return 0;
920 s32 WriteUARTN(void *buf,u32 len)
922 u8 *ptr;
923 u32 reg;
924 s32 ret,qlen,cnt;
926 if((exi_uart_enabled+0x5a010000)!=0x005a) return 2;
927 if(EXI_Lock(exi_uart_chan,exi_uart_dev,NULL)==0) return 0;
929 ptr = buf;
930 while((ptr-(u8*)buf)<len) {
931 if(*ptr=='\n') *ptr = '\r';
932 ptr++;
935 ret = 0;
936 ptr = buf;
937 while(len) {
938 if((qlen=__queuelength())<0) {
939 ret = 3;
940 break;
941 } else if(qlen>=12 || qlen>=len) {
942 if(EXI_Select(exi_uart_chan,exi_uart_dev,EXI_SPEED8MHZ)==0) {
943 ret = 3;
944 break;
947 reg = 0xa0010000;
948 EXI_Imm(exi_uart_chan,&reg,sizeof(u32),EXI_WRITE,NULL);
949 EXI_Sync(exi_uart_chan);
951 while(qlen>0 && len>0) {
952 cnt = 4;
953 if(qlen>=0x0004) {
954 if(len<4) cnt = len;
955 if(qlen<len) break;
957 EXI_Imm(exi_uart_chan,ptr,cnt,EXI_WRITE,NULL);
958 EXI_Sync(exi_uart_chan);
959 qlen -= cnt;
960 len -= cnt;
965 EXI_Deselect(exi_uart_chan);
968 EXI_Unlock(exi_uart_chan);
969 return ret;