]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bitsy/devpcmcia.c
merge
[plan9front.git] / sys / src / 9 / bitsy / devpcmcia.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7 #include "io.h"
8
9 /*
10  * BUG: insertion events are detected by polling.
11  *      Should look into the compaq docs to see if
12  *      there's an interrupt for card insertion
13  *      there's probably one.
14  */
15
16 static PCMslot  slot[2];
17 int nslot = 2;
18
19 struct {
20         Ref;
21         Rendez  event;          // where to wait for card events
22         int     evreader;       // there's a reader for events
23 } pcmcia;
24
25 enum
26 {
27         Qdir,
28         Qmem,
29         Qattr,
30         Qctl,
31         Qevs,
32
33         Nents = 3,
34 };
35
36 enum
37 {
38         /*
39          *  configuration registers - they start at an offset in attribute
40          *  memory found in the CIS.
41          */
42         Rconfig=        0,
43          Creset=         (1<<7),        /*  reset device */
44          Clevel=         (1<<6),        /*  level sensitive interrupt line */
45 };
46
47 static void increfp(PCMslot*);
48 static void decrefp(PCMslot*);
49 static void slotmap(int, ulong, ulong, ulong);
50 static void slottiming(int, int, int, int, int);
51 static void slotinfo(Ureg*, void*);
52
53 #define TYPE(c)         (((ulong)c->qid.path)&0xff)
54 #define PATH(s,t)       (((s)<<8)|(t))
55
56 static PCMslot*
57 slotof(Chan *c)
58 {
59         ulong x;
60
61         x = c->qid.path;
62         return slot + ((x>>8)&0xff);
63 }
64
65 static int
66 pcmgen(Chan *c, char *, Dirtab * , int, int i, Dir *dp)
67 {
68         int slotno;
69         Qid qid;
70         long len;
71         PCMslot *sp;
72
73         if(i == DEVDOTDOT){
74                 mkqid(&qid, Qdir, 0, QTDIR);
75                 devdir(c, qid, "#y", 0, eve, 0555, dp);
76                 return 1;
77         }
78
79         if(i >= Nents*nslot + 1)
80                 return -1;
81         if(i == Nents*nslot){
82                 len = 0;
83                 qid.path = PATH(0, Qevs);
84                 snprint(up->genbuf, sizeof up->genbuf, "pcmevs");
85                 goto found;
86         }
87
88         slotno = i/Nents;
89         sp = slot + slotno;
90         len = 0;
91         switch(i%Nents){
92         case 0:
93                 qid.path = PATH(slotno, Qmem);
94                 snprint(up->genbuf, sizeof up->genbuf, "pcm%dmem", slotno);
95                 len = sp->memlen;
96                 break;
97         case 1:
98                 qid.path = PATH(slotno, Qattr);
99                 snprint(up->genbuf, sizeof up->genbuf, "pcm%dattr", slotno);
100                 len = sp->memlen;
101                 break;
102         case 2:
103                 qid.path = PATH(slotno, Qctl);
104                 snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno);
105                 break;
106         }
107 found:
108         qid.vers = 0;
109         qid.type = QTFILE;
110         devdir(c, qid, up->genbuf, len, eve, 0660, dp);
111         return 1;
112 }
113
114 static int
115 bitno(ulong x)
116 {
117         int i;
118
119         for(i = 0; i < 8*sizeof(x); i++)
120                 if((1<<i) & x)
121                         break;
122         return i;
123 }
124
125 /*
126  *  set up the cards, default timing is 300 ns
127  */
128 static void
129 pcmciareset(void)
130 {
131         /* staticly map the whole area */
132         slotmap(0, PHYSPCM0REGS, PYHSPCM0ATTR, PYHSPCM0MEM);
133         slotmap(1, PHYSPCM1REGS, PYHSPCM1ATTR, PYHSPCM1MEM);
134
135         /* set timing to the default, 300 */
136         slottiming(0, 300, 300, 300, 0);
137         slottiming(1, 300, 300, 300, 0);
138
139         /* if there's no pcmcia sleave, no interrupts */
140         if(gpioregs->level & GPIO_OPT_IND_i)
141                 return;
142
143         /* sleave there, interrupt on card removal */
144         intrenable(GPIOrising, bitno(GPIO_CARD_IND1_i), slotinfo, nil, "pcmcia slot1 status");
145         intrenable(GPIOrising, bitno(GPIO_CARD_IND0_i), slotinfo, nil, "pcmcia slot0 status");
146 }
147
148 static Chan*
149 pcmciaattach(char *spec)
150 {
151         return devattach('y', spec);
152 }
153
154 static Walkqid*
155 pcmciawalk(Chan *c, Chan *nc, char **name, int nname)
156 {
157         return devwalk(c, nc, name, nname, 0, 0, pcmgen);
158 }
159
160 static int
161 pcmciastat(Chan *c, uchar *db, int n)
162 {
163         return devstat(c, db, n, 0, 0, pcmgen);
164 }
165
166 static Chan*
167 pcmciaopen(Chan *c, int omode)
168 {
169         PCMslot *slotp;
170
171         if(c->qid.type & QTDIR){
172                 if(omode != OREAD)
173                         error(Eperm);
174         } else {
175                 slotp = slotof(c);
176                 increfp(slotp);
177         }
178         c->mode = openmode(omode);
179         c->flag |= COPEN;
180         c->offset = 0;
181         return c;
182 }
183
184 static void
185 pcmciaclose(Chan *c)
186 {
187         if(c->flag & COPEN)
188                 if((c->qid.type & QTDIR) == 0)
189                         decrefp(slotof(c));
190 }
191
192 /* a memmove using only bytes */
193 static void
194 memmoveb(uchar *to, uchar *from, int n)
195 {
196         while(n-- > 0)
197                 *to++ = *from++;
198 }
199
200 /* a memmove using only shorts & bytes */
201 static void
202 memmoves(uchar *to, uchar *from, int n)
203 {
204         ushort *t, *f;
205
206         if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){
207                 while(n-- > 0)
208                         *to++ = *from++;
209         } else {
210                 n = n/2;
211                 t = (ushort*)to;
212                 f = (ushort*)from;
213                 while(n-- > 0)
214                         *t++ = *f++;
215         }
216 }
217
218 static long
219 pcmread(void *a, long n, ulong off, PCMslot *sp, uchar *start, ulong len)
220 {
221         rlock(sp);
222         if(waserror()){
223                 runlock(sp);
224                 nexterror();
225         }
226         if(off > len)
227                 return 0;
228         if(off + n > len)
229                 n = len - off;
230         memmoveb(a, start+off, n);
231         runlock(sp);
232         poperror();
233         return n;
234 }
235
236 static long
237 pcmctlread(void *a, long n, ulong off, PCMslot *sp)
238 {
239         char *p, *buf, *e;
240
241         buf = p = malloc(READSTR);
242         if(waserror()){
243                 free(buf);
244                 nexterror();
245         }
246         e = p + READSTR;
247
248         buf[0] = 0;
249         if(sp->occupied){
250                 p = seprint(p, e, "occupied\n");
251                 if(sp->verstr[0])
252                         p = seprint(p, e, "version %s\n", sp->verstr);
253         }
254         USED(p);
255
256         n = readstr(off, a, n, buf);
257         free(buf);
258         poperror();
259         return n;
260 }
261
262 static int
263 inserted(void *)
264 {
265         if (slot[0].inserted)
266                 return 1;
267         if (slot[1].inserted)
268                 return 2;
269         return 0;
270 }
271
272 static long
273 pcmevsread(void *a, long n, ulong off)
274 {
275         int i;
276         char *buf = nil;
277         char *e;
278
279         if (pcmcia.evreader)
280                 error("At most one reader");
281         off = 0;
282         pcmcia.evreader++;
283         if (waserror()){
284                 free(buf);
285                 pcmcia.evreader--;
286                 nexterror();
287         }
288         while((i = inserted(nil)) == 0){
289                 slotinfo(nil, nil);
290                 tsleep(&pcmcia.event, inserted, nil, 500);
291         }
292         pcmcia.evreader--;
293         slot[i-1].inserted = 0;
294         buf = malloc(READSTR);
295         e = buf + READSTR;
296         buf[0] = 0;
297         seprint(buf, e, "#y/pcm%dctl\n", i-1);
298         n = readstr(off, a, n, buf);
299         free(buf);
300         poperror();
301         return n;
302 }
303
304 static long
305 pcmciaread(Chan *c, void *a, long n, vlong off)
306 {
307         PCMslot *sp;
308         ulong offset = off;
309
310         sp = slotof(c);
311
312         switch(TYPE(c)){
313         case Qdir:
314                 return devdirread(c, a, n, 0, 0, pcmgen);
315         case Qmem:
316                 if(!sp->occupied)
317                         error(Eio);
318                 return pcmread(a, n, offset, sp, sp->mem, 64*OneMeg);
319         case Qattr:
320                 if(!sp->occupied)
321                         error(Eio);
322                 return pcmread(a, n, offset, sp, sp->attr, OneMeg);
323         case Qevs:
324                 return pcmevsread(a, n, offset);
325         case Qctl:
326                 return pcmctlread(a, n, offset, sp);
327         }
328         error(Ebadarg);
329         return -1;      /* not reached */
330 }
331
332 static long
333 pcmwrite(void *a, long n, ulong off, PCMslot *sp, uchar *start, ulong len)
334 {
335         rlock(sp);
336         if(waserror()){
337                 runlock(sp);
338                 nexterror();
339         }
340         if(off > len)
341                 error(Eio);
342         if(off + n > len)
343                 error(Eio);
344         memmoveb(start+off, a, n);
345         poperror();
346         runlock(sp);
347         return n;
348 }
349
350 static long
351 pcmctlwrite(char *p, long n, ulong, PCMslot *sp)
352 {
353         Cmdbuf *cmd;
354         uchar *cp;
355         int index, i, dtx;
356         Rune r;
357         DevConf cf;
358         Devport port;
359
360         cmd = parsecmd(p, n);
361         if(strcmp(cmd->f[0], "configure") == 0){
362                 wlock(sp);
363                 if(waserror()){
364                         wunlock(sp);
365                         nexterror();
366                 }
367
368                 /* see if driver exists and is configurable */
369                 if(cmd->nf < 3)
370                         error(Ebadarg);
371                 p = cmd->f[1];
372                 if(*p++ != '#')
373                         error(Ebadarg);
374                 p += chartorune(&r, p);
375                 dtx = devno(r, 1);
376                 if(dtx < 0)
377                         error("no such device type");
378                 if(devtab[dtx]->config == nil)
379                         error("not a dynamicly configurable device");
380
381                 /* set pcmcia card configuration */
382                 index = 0;
383                 if(sp->def != nil)
384                         index = sp->def->index;
385                 if(cmd->nf > 3){
386                         i = atoi(cmd->f[3]);
387                         if(i < 0 || i >= sp->nctab)
388                                 error("bad configuration index");
389                         index = i;
390                 }
391                 if(sp->cfg[0].cpresent & (1<<Rconfig)){
392                         cp = sp->attr;
393                         cp += sp->cfg[0].caddr + Rconfig;
394                         *cp = index;
395                 }
396
397                 /* configure device */
398                 memset(&cf, 0, sizeof cf);
399                 kstrdup(&cf.type, cmd->f[2]);
400                 cf.mem = (ulong)sp->mem;
401                 cf.ports = &port;
402                 cf.ports[0].port = (ulong)sp->regs;
403                 cf.ports[0].size = 0;
404                 cf.nports = 1;
405                 cf.itype = GPIOfalling;
406                 cf.intnum = bitno(sp == slot ? GPIO_CARD_IRQ0_i : GPIO_CARD_IRQ1_i);
407                 if(devtab[dtx]->config(1, p, &cf) < 0)
408                         error("couldn't configure device");
409                 sp->dev = devtab[dtx];
410                 free(cf.type);
411                 wunlock(sp);
412                 poperror();
413
414                 /* don't let the power turn off */
415                 increfp(sp);
416         }else if(strcmp(cmd->f[0], "remove") == 0){
417                 /* see if driver exists and is configurable */
418                 if(cmd->nf != 2)
419                         error(Ebadarg);
420                 p = cmd->f[1];
421                 if(*p++ != '#')
422                         error(Ebadarg);
423                 p += chartorune(&r, p);
424                 dtx = devno(r, 1);
425                 if(dtx < 0)
426                         error("no such device type");
427                 if(devtab[dtx]->config == nil)
428                         error("not a dynamicly configurable device");
429                 if(devtab[dtx]->config(0, p, nil) < 0)
430                         error("couldn't unconfigure device");
431
432                 /* let the power turn off */
433                 decrefp(sp);
434         }
435         free(cmd);
436
437         return 0;
438 }
439
440 static long
441 pcmciawrite(Chan *c, void *a, long n, vlong off)
442 {
443         PCMslot *sp;
444         ulong offset = off;
445
446         sp = slotof(c);
447
448         switch(TYPE(c)){
449         case Qmem:
450                 if(!sp->occupied)
451                         error(Eio);
452                 return pcmwrite(a, n, offset, sp, sp->mem, 64*OneMeg);
453         case Qattr:
454                 if(!sp->occupied)
455                         error(Eio);
456                 return pcmwrite(a, n, offset, sp, sp->attr, OneMeg);
457         case Qevs:
458                 break;
459         case Qctl:
460                 if(!sp->occupied)
461                         error(Eio);
462                 return pcmctlwrite(a, n, offset, sp);
463         }
464         error(Ebadarg);
465         return -1;      /* not reached */
466 }
467
468 /*
469  * power up/down pcmcia
470  */
471 void
472 pcmciapower(int on)
473 {
474         PCMslot *sp;
475
476         /* if there's no pcmcia sleave, no interrupts */
477 iprint("pcmciapower %d\n", on);
478
479         if (on){
480                 /* set timing to the default, 300 */
481                 slottiming(0, 300, 300, 300, 0);
482                 slottiming(1, 300, 300, 300, 0);
483
484                 /* if there's no pcmcia sleave, no interrupts */
485                 if(gpioregs->level & GPIO_OPT_IND_i){
486                         iprint("pcmciapower: no sleeve\n");
487                         return;
488                 }
489
490                 for (sp = slot; sp < slot + nslot; sp++){
491                         if (sp->dev){
492                                 increfp(sp);
493                                 iprint("pcmciapower: %s\n", sp->verstr);
494                                 delay(10000);
495                                 if (sp->dev->power)
496                                         sp->dev->power(on);
497                         }
498                 }
499         }else{
500                 if(gpioregs->level & GPIO_OPT_IND_i){
501                         iprint("pcmciapower: no sleeve\n");
502                         return;
503                 }
504
505                 for (sp = slot; sp < slot + nslot; sp++){
506                         if (sp->dev){
507                                 if (sp->dev->power)
508                                         sp->dev->power(on);
509                                 decrefp(sp);
510                         }
511                         sp->occupied = 0;
512                         sp->cisread = 0;
513                 }
514                 egpiobits(EGPIO_exp_nvram_power|EGPIO_exp_full_power, 0);
515         }
516 }
517
518 Dev pcmciadevtab = {
519         'y',
520         "pcmcia",
521
522         pcmciareset,
523         devinit,
524         devshutdown,
525         pcmciaattach,
526         pcmciawalk,
527         pcmciastat,
528         pcmciaopen,
529         devcreate,
530         pcmciaclose,
531         pcmciaread,
532         devbread,
533         pcmciawrite,
534         devbwrite,
535         devremove,
536         devwstat,
537         pcmciapower,
538 };
539
540 /* see what's there */
541 static void
542 slotinfo(Ureg*, void*)
543 {
544         ulong x = gpioregs->level;
545
546         if(x & GPIO_OPT_IND_i){
547                 /* no expansion pack */
548                 slot[0].occupied = slot[0].inserted = 0;
549                 slot[1].occupied = slot[1].inserted = 0;
550         } else {
551                 if(x & GPIO_CARD_IND0_i){
552                         slot[0].occupied = slot[0].inserted = 0;
553                         slot[0].cisread = 0;
554                 } else {
555                         if(slot[0].occupied == 0){
556                                 slot[0].inserted = 1;
557                                 slot[0].cisread = 0;
558                         }
559                         slot[0].occupied = 1;
560                 }
561                 if(x & GPIO_CARD_IND1_i){
562                         slot[1].occupied = slot[1].inserted = 0;
563                         slot[1].cisread = 0;
564                 } else {
565                         if(slot[1].occupied == 0){
566                                 slot[1].inserted = 1;
567                                 slot[1].cisread = 0;
568                         }
569                         slot[1].occupied = 1;
570                 }
571                 if (inserted(nil))
572                         wakeup(&pcmcia.event);
573         }
574 }
575
576 /* use reference card to turn cards on and off */
577 static void
578 increfp(PCMslot *sp)
579 {
580         wlock(sp);
581         if(waserror()){
582                 wunlock(sp);
583                 nexterror();
584         }
585
586 iprint("increfp %ld\n", sp - slot);
587         if(incref(&pcmcia) == 1){
588 iprint("increfp full power\n");
589                 egpiobits(EGPIO_exp_nvram_power|EGPIO_exp_full_power, 1);
590                 delay(200);
591                 egpiobits(EGPIO_pcmcia_reset, 1);
592                 delay(100);
593                 egpiobits(EGPIO_pcmcia_reset, 0);
594                 delay(500);
595         }
596         incref(&sp->ref);
597         slotinfo(nil, nil);
598         if(sp->occupied && sp->cisread == 0) {
599                 pcmcisread(sp);
600         }
601
602         wunlock(sp);
603         poperror();
604 }
605
606 static void
607 decrefp(PCMslot *sp)
608 {
609 iprint("decrefp %ld\n", sp - slot);
610         decref(&sp->ref);
611         if(decref(&pcmcia) == 0){
612 iprint("increfp power down\n");
613                 egpiobits(EGPIO_exp_nvram_power|EGPIO_exp_full_power, 0);
614         }
615 }
616
617 /*
618  *  the regions are staticly mapped
619  */
620 static void
621 slotmap(int slotno, ulong regs, ulong attr, ulong mem)
622 {
623         PCMslot *sp;
624
625         sp = &slot[slotno];
626         sp->slotno = slotno;
627         sp->memlen = 64*OneMeg;
628         sp->verstr[0] = 0;
629
630         sp->mem = mapmem(mem, 64*OneMeg, 0);
631         sp->memmap.ca = 0;
632         sp->memmap.cea = 64*MB;
633         sp->memmap.isa = (ulong)mem;
634         sp->memmap.len = 64*OneMeg;
635         sp->memmap.attr = 0;
636
637         sp->attr = mapmem(attr, OneMeg, 0);
638         sp->attrmap.ca = 0;
639         sp->attrmap.cea = MB;
640         sp->attrmap.isa = (ulong)attr;
641         sp->attrmap.len = OneMeg;
642         sp->attrmap.attr = 1;
643
644         sp->regs = mapspecial(regs, 32*1024);
645 }
646
647 PCMmap*
648 pcmmap(int slotno, ulong, int, int attr)
649 {
650         if(slotno > nslot)
651                 panic("pcmmap");
652         if(attr)
653                 return &slot[slotno].attrmap;
654         else
655                 return &slot[slotno].memmap;
656 }
657
658 void
659 pcmunmap(int, PCMmap*)
660 {
661 }
662
663 /*
664  *  setup card timings
665  *    times are in ns
666  *    count = ceiling[access-time/(2*3*T)] - 1, where T is a processor cycle
667  *
668  */
669 static int
670 ns2count(int ns)
671 {
672         ulong y;
673
674         /* get 100 times cycle time */
675         y = 100000000/(conf.hz/1000);
676
677         /* get 10 times ns/(cycle*6) */
678         y = (1000*ns)/(6*y);
679
680         /* round up */
681         y += 9;
682         y /= 10;
683
684         /* subtract 1 */
685         return y-1;
686 }
687 static void
688 slottiming(int slotno, int tio, int tattr, int tmem, int fast)
689 {
690         ulong x;
691
692         x = 0;
693         if(fast)
694                 x |= 1<<MECR_fast0;
695         x |= ns2count(tio) << MECR_io0;
696         x |= ns2count(tattr) << MECR_attr0;
697         x |= ns2count(tmem) << MECR_mem0;
698         if(slotno == 0){
699                 x |= memconfregs->mecr & 0xffff0000;
700         } else {
701                 x <<= 16;
702                 x |= memconfregs->mecr & 0xffff;
703         }
704         memconfregs->mecr = x;
705 }
706
707 /* For compat with ../pc devices. Don't use it for the bitsy 
708  */
709 int
710 pcmspecial(char*, ISAConf*)
711 {
712         return -1;
713 }