]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devi82365.c
Import sources from 2011-03-30 iso image
[plan9front.git] / sys / src / 9 / pc / devi82365.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  *  Intel 82365SL PCIC controller and compatibles.
11  */
12 enum
13 {
14         /*
15          *  registers indices
16          */
17         Rid=            0x0,            /* identification and revision */
18         Ris=            0x1,            /* interface status */
19         Rpc=            0x2,            /* power control */
20          Foutena=        (1<<7),        /*  output enable */
21          Fautopower=     (1<<5),        /*  automatic power switching */
22          Fcardena=       (1<<4),        /*  PC card enable */
23         Rigc=           0x3,            /* interrupt and general control */
24          Fiocard=        (1<<5),        /*  I/O card (vs memory) */
25          Fnotreset=      (1<<6),        /*  reset if not set */ 
26          FSMIena=        (1<<4),        /*  enable change interrupt on SMI */ 
27         Rcsc=           0x4,            /* card status change */
28         Rcscic=         0x5,            /* card status change interrupt config */
29          Fchangeena=     (1<<3),        /*  card changed */
30          Fbwarnena=      (1<<1),        /*  card battery warning */
31          Fbdeadena=      (1<<0),        /*  card battery dead */
32         Rwe=            0x6,            /* address window enable */
33          Fmem16=         (1<<5),        /*  use A23-A12 to decode address */
34         Rio=            0x7,            /* I/O control */
35          Fwidth16=       (1<<0),        /*  16 bit data width */
36          Fiocs16=        (1<<1),        /*  IOCS16 determines data width */
37          Fzerows=        (1<<2),        /*  zero wait state */
38          Ftiming=        (1<<3),        /*  timing register to use */
39         Riobtm0lo=      0x8,            /* I/O address 0 start low byte */
40         Riobtm0hi=      0x9,            /* I/O address 0 start high byte */
41         Riotop0lo=      0xa,            /* I/O address 0 stop low byte */
42         Riotop0hi=      0xb,            /* I/O address 0 stop high byte */
43         Riobtm1lo=      0xc,            /* I/O address 1 start low byte */
44         Riobtm1hi=      0xd,            /* I/O address 1 start high byte */
45         Riotop1lo=      0xe,            /* I/O address 1 stop low byte */
46         Riotop1hi=      0xf,            /* I/O address 1 stop high byte */
47         Rmap=           0x10,           /* map 0 */
48
49         /*
50          *  CL-PD67xx extension registers
51          */
52         Rmisc1=         0x16,           /* misc control 1 */
53          F5Vdetect=      (1<<0),
54          Fvcc3V=         (1<<1),
55          Fpmint=         (1<<2),
56          Fpsirq=         (1<<3),
57          Fspeaker=       (1<<4),
58          Finpack=        (1<<7),
59         Rfifo=          0x17,           /* fifo control */
60          Fflush=         (1<<7),        /*  flush fifo */
61         Rmisc2=         0x1E,           /* misc control 2 */
62          Flowpow=        (1<<1),        /*  low power mode */
63         Rchipinfo=      0x1F,           /* chip information */
64         Ratactl=        0x26,           /* ATA control */
65
66         /*
67          *  offsets into the system memory address maps
68          */
69         Mbtmlo=         0x0,            /* System mem addr mapping start low byte */
70         Mbtmhi=         0x1,            /* System mem addr mapping start high byte */
71          F16bit=         (1<<7),        /*  16-bit wide data path */
72         Mtoplo=         0x2,            /* System mem addr mapping stop low byte */
73         Mtophi=         0x3,            /* System mem addr mapping stop high byte */
74          Ftimer1=        (1<<6),        /*  timer set 1 */
75         Mofflo=         0x4,            /* Card memory offset address low byte */
76         Moffhi=         0x5,            /* Card memory offset address high byte */
77          Fregactive=     (1<<6),        /*  attribute memory */
78
79         /*
80          *  configuration registers - they start at an offset in attribute
81          *  memory found in the CIS.
82          */
83         Rconfig=        0,
84          Creset=         (1<<7),        /*  reset device */
85          Clevel=         (1<<6),        /*  level sensitive interrupt line */
86          Cirq=           (1<<2),        /*  IRQ enable */
87          Cdecode=        (1<<1),        /*  address decode */
88          Cfunc=          (1<<0),        /*  function enable */
89         Riobase0=       5,
90         Riobase1=       6,
91         Riosize=        9,
92 };
93
94 #define MAP(x,o)        (Rmap + (x)*0x8 + o)
95
96 typedef struct I82365   I82365;
97
98 /* a controller */
99 enum
100 {
101         Ti82365,
102         Tpd6710,
103         Tpd6720,
104         Tvg46x,
105 };
106 struct I82365
107 {
108         int     type;
109         int     dev;
110         int     nslot;
111         int     xreg;           /* index register address */
112         int     dreg;           /* data register address */
113         int     irq;
114 };
115 static I82365 *controller[4];
116 static int ncontroller;
117 static PCMslot  *slot;
118 static PCMslot  *lastslot;
119 static nslot;
120
121 static void     i82365intr(Ureg*, void*);
122 static int      pcmio(int, ISAConf*);
123 static long     pcmread(int, int, void*, long, vlong);
124 static long     pcmwrite(int, int, void*, long, vlong);
125
126 static void i82365dump(PCMslot*);
127
128 /*
129  *  reading and writing card registers
130  */
131 static uchar
132 rdreg(PCMslot *pp, int index)
133 {
134         outb(((I82365*)pp->cp)->xreg, pp->base + index);
135         return inb(((I82365*)pp->cp)->dreg);
136 }
137 static void
138 wrreg(PCMslot *pp, int index, uchar val)
139 {
140         outb(((I82365*)pp->cp)->xreg, pp->base + index);
141         outb(((I82365*)pp->cp)->dreg, val);
142 }
143
144 /*
145  *  get info about card
146  */
147 static void
148 slotinfo(PCMslot *pp)
149 {
150         uchar isr;
151
152         isr = rdreg(pp, Ris);
153         pp->occupied = (isr & (3<<2)) == (3<<2);
154         pp->powered = isr & (1<<6);
155         pp->battery = (isr & 3) == 3;
156         pp->wrprot = isr & (1<<4);
157         pp->busy = isr & (1<<5);
158         pp->msec = TK2MS(MACHP(0)->ticks);
159 }
160
161 static int
162 vcode(int volt)
163 {
164         switch(volt){
165         case 5:
166                 return 1;
167         case 12:
168                 return 2;
169         default:
170                 return 0;
171         }
172 }
173
174 /*
175  *  enable the slot card
176  */
177 static void
178 slotena(PCMslot *pp)
179 {
180         if(pp->enabled)
181                 return;
182
183         /* power up and unreset, wait's are empirical (???) */
184         wrreg(pp, Rpc, Fautopower|Foutena|Fcardena);
185         delay(300);
186         wrreg(pp, Rigc, 0);
187         delay(100);
188         wrreg(pp, Rigc, Fnotreset);
189         delay(500);
190
191         /* get configuration */
192         slotinfo(pp);
193         if(pp->occupied){
194                 pcmcisread(pp);
195                 pp->enabled = 1;
196         } else
197                 wrreg(pp, Rpc, Fautopower);
198 }
199
200 /*
201  *  disable the slot card
202  */
203 static void
204 slotdis(PCMslot *pp)
205 {
206         wrreg(pp, Rpc, 0);      /* turn off card power */
207         wrreg(pp, Rwe, 0);      /* no windows */
208         pp->enabled = 0;
209 }
210
211 /*
212  *  status change interrupt
213  */
214 static void
215 i82365intr(Ureg *, void *)
216 {
217         uchar csc, was;
218         PCMslot *pp;
219
220         if(slot == 0)
221                 return;
222
223         for(pp = slot; pp < lastslot; pp++){
224                 csc = rdreg(pp, Rcsc);
225                 was = pp->occupied;
226                 slotinfo(pp);
227                 if(csc & (1<<3) && was != pp->occupied){
228                         if(!pp->occupied)
229                                 slotdis(pp);
230                 }
231         }
232 }
233
234 enum
235 {
236         Mshift= 12,
237         Mgran=  (1<<Mshift),    /* granularity of maps */
238         Mmask=  ~(Mgran-1),     /* mask for address bits important to the chip */
239 };
240
241 /*
242  *  get a map for pc card region, return corrected len
243  */
244 PCMmap*
245 pcmmap(int slotno, ulong offset, int len, int attr)
246 {
247         PCMslot *pp;
248         uchar we, bit;
249         PCMmap *m, *nm;
250         int i;
251         ulong e;
252
253         pp = slot + slotno;
254         lock(&pp->mlock);
255
256         /* convert offset to granularity */
257         if(len <= 0)
258                 len = 1;
259         e = ROUND(offset+len, Mgran);
260         offset &= Mmask;
261         len = e - offset;
262
263         /* look for a map that covers the right area */
264         we = rdreg(pp, Rwe);
265         bit = 1;
266         nm = 0;
267         for(m = pp->mmap; m < &pp->mmap[nelem(pp->mmap)]; m++){
268                 if((we & bit))
269                 if(m->attr == attr)
270                 if(offset >= m->ca && e <= m->cea){
271
272                         m->ref++;
273                         unlock(&pp->mlock);
274                         return m;
275                 }
276                 bit <<= 1;
277                 if(nm == 0 && m->ref == 0)
278                         nm = m;
279         }
280         m = nm;
281         if(m == 0){
282                 unlock(&pp->mlock);
283                 return 0;
284         }
285
286         /* if isa space isn't big enough, free it and get more */
287         if(m->len < len){
288                 if(m->isa){
289                         umbfree(m->isa, m->len);
290                         m->len = 0;
291                 }
292                 m->isa = PADDR(umbmalloc(0, len, Mgran));
293                 if(m->isa == 0){
294                         print("pcmmap: out of isa space\n");
295                         unlock(&pp->mlock);
296                         return 0;
297                 }
298                 m->len = len;
299         }
300
301         /* set up new map */
302         m->ca = offset;
303         m->cea = m->ca + m->len;
304         m->attr = attr;
305         i = m-pp->mmap;
306         bit = 1<<i;
307         wrreg(pp, Rwe, we & ~bit);              /* disable map before changing it */
308         wrreg(pp, MAP(i, Mbtmlo), m->isa>>Mshift);
309         wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
310         wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
311         wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
312         offset -= m->isa;
313         offset &= (1<<25)-1;
314         offset >>= Mshift;
315         wrreg(pp, MAP(i, Mofflo), offset);
316         wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
317         wrreg(pp, Rwe, we | bit);               /* enable map */
318         m->ref = 1;
319
320         unlock(&pp->mlock);
321         return m;
322 }
323
324 void
325 pcmunmap(int slotno, PCMmap* m)
326 {
327         PCMslot *pp;
328
329         pp = slot + slotno;
330         lock(&pp->mlock);
331         m->ref--;
332         unlock(&pp->mlock);
333 }
334
335 static void
336 increfp(PCMslot *pp)
337 {
338         lock(pp);
339         if(pp->ref++ == 0)
340                 slotena(pp);
341         unlock(pp);
342 }
343
344 static void
345 decrefp(PCMslot *pp)
346 {
347         lock(pp);
348         if(pp->ref-- == 1)
349                 slotdis(pp);
350         unlock(pp);
351 }
352
353 /*
354  *  look for a card whose version contains 'idstr'
355  */
356 static int
357 pcmcia_pcmspecial(char *idstr, ISAConf *isa)
358 {
359         PCMslot *pp;
360         extern char *strstr(char*, char*);
361         int enabled;
362
363         for(pp = slot; pp < lastslot; pp++){
364                 if(pp->special)
365                         continue;       /* already taken */
366
367                 /*
368                  *  make sure we don't power on cards when we already know what's
369                  *  in them.  We'll reread every two minutes if necessary
370                  */
371                 enabled = 0;
372                 if (pp->msec == ~0 || TK2MS(MACHP(0)->ticks) - pp->msec > 120000){
373                         increfp(pp);
374                         enabled++;
375                 }
376
377                 if(pp->occupied) {
378                         if(strstr(pp->verstr, idstr)){
379                                 if (!enabled){
380                                         enabled = 1;
381                                         increfp(pp);
382                                 }
383                                 if(isa == 0 || pcmio(pp->slotno, isa) == 0){
384                                         pp->special = 1;
385                                         return pp->slotno;
386                                 }
387                         }
388                 } else
389                         pp->special = 1;
390                 if (enabled)
391                         decrefp(pp);
392         }
393         return -1;
394 }
395
396 static void
397 pcmcia_pcmspecialclose(int slotno)
398 {
399         PCMslot *pp;
400
401         if(slotno >= nslot)
402                 panic("pcmspecialclose");
403         pp = slot + slotno;
404         pp->special = 0;
405         decrefp(pp);
406 }
407
408 enum
409 {
410         Qdir,
411         Qmem,
412         Qattr,
413         Qctl,
414
415         Nents = 3,
416 };
417
418 #define SLOTNO(c)       ((ulong)((c->qid.path>>8)&0xff))
419 #define TYPE(c) ((ulong)(c->qid.path&0xff))
420 #define QID(s,t)        (((s)<<8)|(t))
421
422 static int
423 pcmgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp)
424 {
425         int slotno;
426         Qid qid;
427         long len;
428         PCMslot *pp;
429
430         if(i == DEVDOTDOT){
431                 mkqid(&qid, Qdir, 0, QTDIR);
432                 devdir(c, qid, "#y", 0, eve, 0555, dp);
433                 return 1;
434         }
435
436         if(i >= Nents*nslot)
437                 return -1;
438         slotno = i/Nents;
439         pp = slot + slotno;
440         len = 0;
441         switch(i%Nents){
442         case 0:
443                 qid.path = QID(slotno, Qmem);
444                 snprint(up->genbuf, sizeof up->genbuf, "pcm%dmem", slotno);
445                 len = pp->memlen;
446                 break;
447         case 1:
448                 qid.path = QID(slotno, Qattr);
449                 snprint(up->genbuf, sizeof up->genbuf, "pcm%dattr", slotno);
450                 len = pp->memlen;
451                 break;
452         case 2:
453                 qid.path = QID(slotno, Qctl);
454                 snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno);
455                 break;
456         }
457         qid.vers = 0;
458         qid.type = QTFILE;
459         devdir(c, qid, up->genbuf, len, eve, 0660, dp);
460         return 1;
461 }
462
463 static char *chipname[] =
464 {
465 [Ti82365]       "Intel 82365SL",
466 [Tpd6710]       "Cirrus Logic CL-PD6710",
467 [Tpd6720]       "Cirrus Logic CL-PD6720",
468 [Tvg46x]        "Vadem VG-46x",
469 };
470
471 static I82365*
472 i82365probe(int x, int d, int dev)
473 {
474         uchar c, id;
475         I82365 *cp;
476         ISAConf isa;
477         int i, nslot;
478
479         outb(x, Rid + (dev<<7));
480         id = inb(d);
481         if((id & 0xf0) != 0x80)
482                 return 0;               /* not a memory & I/O card */
483         if((id & 0x0f) == 0x00)
484                 return 0;               /* no revision number, not possible */
485
486         cp = xalloc(sizeof(I82365));
487         cp->xreg = x;
488         cp->dreg = d;
489         cp->dev = dev;
490         cp->type = Ti82365;
491         cp->nslot = 2;
492
493         switch(id){
494         case 0x82:
495         case 0x83:
496         case 0x84:
497                 /* could be a cirrus */
498                 outb(x, Rchipinfo + (dev<<7));
499                 outb(d, 0);
500                 c = inb(d);
501                 if((c & 0xc0) != 0xc0)
502                         break;
503                 c = inb(d);
504                 if((c & 0xc0) != 0x00)
505                         break;
506                 if(c & 0x20){
507                         cp->type = Tpd6720;
508                 } else {
509                         cp->type = Tpd6710;
510                         cp->nslot = 1;
511                 }
512
513                 /* low power mode */
514                 outb(x, Rmisc2 + (dev<<7));
515                 c = inb(d);
516                 outb(d, c & ~Flowpow);
517                 break;
518         }
519
520         /* if it's not a Cirrus, it could be a Vadem... */
521         if(cp->type == Ti82365){
522                 /* unlock the Vadem extended regs */
523                 outb(x, 0x0E + (dev<<7));
524                 outb(x, 0x37 + (dev<<7));
525
526                 /* make the id register show the Vadem id */
527                 outb(x, 0x3A + (dev<<7));
528                 c = inb(d);
529                 outb(d, c|0xC0);
530                 outb(x, Rid + (dev<<7));
531                 c = inb(d);
532                 if(c & 0x08)
533                         cp->type = Tvg46x;
534
535                 /* go back to Intel compatible id */
536                 outb(x, 0x3A + (dev<<7));
537                 c = inb(d);
538                 outb(d, c & ~0xC0);
539         }
540
541         memset(&isa, 0, sizeof(ISAConf));
542         if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq)
543                 cp->irq = isa.irq;
544         else
545                 cp->irq = IrqPCMCIA;
546
547         for(i = 0; i < isa.nopt; i++){
548                 if(cistrncmp(isa.opt[i], "nslot=", 6))
549                         continue;
550                 nslot = strtol(&isa.opt[i][6], nil, 0);
551                 if(nslot > 0 && nslot <= 2)
552                         cp->nslot = nslot;
553         }
554
555         controller[ncontroller++] = cp;
556         return cp;
557 }
558
559 static void
560 i82365dump(PCMslot *pp)
561 {
562         int i;
563
564         for(i = 0; i < 0x40; i++){
565                 if((i&0x0F) == 0)
566                         print("\n%2.2uX:        ", i);
567                 print("%2.2uX ", rdreg(pp, i));
568                 if(((i+1) & 0x0F) == 0x08)
569                         print(" - ");
570         }
571         print("\n");
572 }
573
574 /*
575  *  set up for slot cards
576  */
577 void
578 devi82365link(void)
579 {
580         static int already;
581         int i, j;
582         I82365 *cp;
583         PCMslot *pp;
584         char buf[32], *p;
585
586         if(already)
587                 return;
588         already = 1;
589
590         if((p=getconf("pcmcia0")) && strncmp(p, "disabled", 8)==0)
591                 return;
592
593         if(_pcmspecial)
594                 return;
595         
596         /* look for controllers if the ports aren't already taken */
597         if(ioalloc(0x3E0, 2, 0, "i82365.0") >= 0){
598                 i82365probe(0x3E0, 0x3E1, 0);
599                 i82365probe(0x3E0, 0x3E1, 1);
600                 if(ncontroller == 0)
601                         iofree(0x3E0);
602         }
603         if(ioalloc(0x3E2, 2, 0, "i82365.1") >= 0){
604                 i = ncontroller;
605                 i82365probe(0x3E2, 0x3E3, 0);
606                 i82365probe(0x3E2, 0x3E3, 1);
607                 if(ncontroller == i)
608                         iofree(0x3E2);
609         }
610
611         if(ncontroller == 0)
612                 return;
613
614         _pcmspecial = pcmcia_pcmspecial;
615         _pcmspecialclose = pcmcia_pcmspecialclose;
616
617         for(i = 0; i < ncontroller; i++)
618                 nslot += controller[i]->nslot;
619         slot = xalloc(nslot * sizeof(PCMslot));
620
621         lastslot = slot;
622         for(i = 0; i < ncontroller; i++){
623                 cp = controller[i];
624                 print("#y%d: %d slot %s: port 0x%uX irq %d\n",
625                         i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq);
626                 for(j = 0; j < cp->nslot; j++){
627                         pp = lastslot++;
628                         pp->slotno = pp - slot;
629                         pp->memlen = 64*MB;
630                         pp->base = (cp->dev<<7) | (j<<6);
631                         pp->cp = cp;
632                         pp->msec = ~0;
633                         pp->verstr[0] = 0;
634                         slotdis(pp);
635
636                         /* interrupt on status change */
637                         wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena);
638                         rdreg(pp, Rcsc);
639                 }
640
641                 /* for card management interrupts */
642                 snprint(buf, sizeof buf, "i82365.%d", i);
643                 intrenable(cp->irq, i82365intr, 0, BUSUNKNOWN, buf);
644         }
645 }
646
647 static Chan*
648 i82365attach(char *spec)
649 {
650         return devattach('y', spec);
651 }
652
653 static Walkqid*
654 i82365walk(Chan *c, Chan *nc, char **name, int nname)
655 {
656         return devwalk(c, nc, name, nname, 0, 0, pcmgen);
657 }
658
659 static int
660 i82365stat(Chan *c, uchar *db, int n)
661 {
662         return devstat(c, db, n, 0, 0, pcmgen);
663 }
664
665 static Chan*
666 i82365open(Chan *c, int omode)
667 {
668         if(c->qid.type & QTDIR){
669                 if(omode != OREAD)
670                         error(Eperm);
671         } else
672                 increfp(slot + SLOTNO(c));
673         c->mode = openmode(omode);
674         c->flag |= COPEN;
675         c->offset = 0;
676         return c;
677 }
678
679 static void
680 i82365close(Chan *c)
681 {
682         if(c->flag & COPEN)
683                 if((c->qid.type & QTDIR) == 0)
684                         decrefp(slot+SLOTNO(c));
685 }
686
687 /* a memmove using only bytes */
688 static void
689 memmoveb(uchar *to, uchar *from, int n)
690 {
691         while(n-- > 0)
692                 *to++ = *from++;
693 }
694
695 /* a memmove using only shorts & bytes */
696 static void
697 memmoves(uchar *to, uchar *from, int n)
698 {
699         ushort *t, *f;
700
701         if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){
702                 while(n-- > 0)
703                         *to++ = *from++;
704         } else {
705                 n = n/2;
706                 t = (ushort*)to;
707                 f = (ushort*)from;
708                 while(n-- > 0)
709                         *t++ = *f++;
710         }
711 }
712
713 static long
714 pcmread(int slotno, int attr, void *a, long n, vlong off)
715 {
716         int i, len;
717         PCMmap *m;
718         uchar *ac;
719         PCMslot *pp;
720         ulong offset = off;
721
722         pp = slot + slotno;
723         if(pp->memlen < offset)
724                 return 0;
725         if(pp->memlen < offset + n)
726                 n = pp->memlen - offset;
727
728         m = 0;
729         if(waserror()){
730                 if(m)
731                         pcmunmap(pp->slotno, m);
732                 nexterror();
733         }
734
735         ac = a;
736         for(len = n; len > 0; len -= i){
737                 m = pcmmap(pp->slotno, offset, 0, attr);
738                 if(m == 0)
739                         error("cannot map PCMCIA card");
740                 if(offset + len > m->cea)
741                         i = m->cea - offset;
742                 else
743                         i = len;
744                 memmoveb(ac, KADDR(m->isa + offset - m->ca), i);
745                 pcmunmap(pp->slotno, m);
746                 offset += i;
747                 ac += i;
748         }
749
750         poperror();
751         return n;
752 }
753
754 static long
755 i82365read(Chan *c, void *a, long n, vlong off)
756 {
757         char *p, *buf, *e;
758         PCMslot *pp;
759         ulong offset = off;
760
761         switch(TYPE(c)){
762         case Qdir:
763                 return devdirread(c, a, n, 0, 0, pcmgen);
764         case Qmem:
765         case Qattr:
766                 return pcmread(SLOTNO(c), TYPE(c) == Qattr, a, n, off);
767         case Qctl:
768                 buf = p = malloc(READSTR);
769                 e = p + READSTR;
770                 pp = slot + SLOTNO(c);
771
772                 buf[0] = 0;
773                 if(pp->occupied){
774                         p = seprint(p, e, "occupied\n");
775                         if(pp->verstr[0])
776                                 p = seprint(p, e, "version %s\n", pp->verstr);
777                 }
778                 if(pp->enabled)
779                         p = seprint(p, e, "enabled\n");
780                 if(pp->powered)
781                         p = seprint(p, e, "powered\n");
782                 if(pp->configed)
783                         p = seprint(p, e, "configed\n");
784                 if(pp->wrprot)
785                         p = seprint(p, e, "write protected\n");
786                 if(pp->busy)
787                         p = seprint(p, e, "busy\n");
788                 seprint(p, e, "battery lvl %d\n", pp->battery);
789
790                 n = readstr(offset, a, n, buf);
791                 free(buf);
792
793                 return n;
794         }
795         error(Ebadarg);
796         return -1;      /* not reached */
797 }
798
799 static long
800 pcmwrite(int dev, int attr, void *a, long n, vlong off)
801 {
802         int i, len;
803         PCMmap *m;
804         uchar *ac;
805         PCMslot *pp;
806         ulong offset = off;
807
808         pp = slot + dev;
809         if(pp->memlen < offset)
810                 return 0;
811         if(pp->memlen < offset + n)
812                 n = pp->memlen - offset;
813
814         m = 0;
815         if(waserror()){
816                 if(m)
817                         pcmunmap(pp->slotno, m);
818                 nexterror();
819         }
820
821         ac = a;
822         for(len = n; len > 0; len -= i){
823                 m = pcmmap(pp->slotno, offset, 0, attr);
824                 if(m == 0)
825                         error("cannot map PCMCIA card");
826                 if(offset + len > m->cea)
827                         i = m->cea - offset;
828                 else
829                         i = len;
830                 memmoveb(KADDR(m->isa + offset - m->ca), ac, i);
831                 pcmunmap(pp->slotno, m);
832                 offset += i;
833                 ac += i;
834         }
835
836         poperror();
837         return n;
838 }
839
840 static long
841 i82365write(Chan *c, void *a, long n, vlong off)
842 {
843         PCMslot *pp;
844         char buf[32];
845
846         switch(TYPE(c)){
847         case Qctl:
848                 if(n >= sizeof(buf))
849                         n = sizeof(buf) - 1;
850                 strncpy(buf, a, n);
851                 buf[n] = 0;
852                 pp = slot + SLOTNO(c);
853                 if(!pp->occupied)
854                         error(Eio);
855
856                 /* set vpp on card */
857                 if(strncmp(buf, "vpp", 3) == 0)
858                         wrreg(pp, Rpc, vcode(atoi(buf+3))|Fautopower|Foutena|Fcardena);
859                 return n;
860         case Qmem:
861         case Qattr:
862                 pp = slot + SLOTNO(c);
863                 if(pp->occupied == 0 || pp->enabled == 0)
864                         error(Eio);
865                 n = pcmwrite(pp->slotno, TYPE(c) == Qattr, a, n, off);
866                 if(n < 0)
867                         error(Eio);
868                 return n;
869         }
870         error(Ebadarg);
871         return -1;      /* not reached */
872 }
873
874 Dev i82365devtab = {
875         'y',
876         "i82365",
877
878         devreset,
879         devinit,
880         devshutdown,
881         i82365attach,
882         i82365walk,
883         i82365stat,
884         i82365open,
885         devcreate,
886         i82365close,
887         i82365read,
888         devbread,
889         i82365write,
890         devbwrite,
891         devremove,
892         devwstat,
893 };
894
895 /*
896  *  configure the PCMslot for IO.  We assume very heavily that we can read
897  *  configuration info from the CIS.  If not, we won't set up correctly.
898  */
899 static int
900 pcmio(int slotno, ISAConf *isa)
901 {
902         uchar we, x, *p;
903         PCMslot *pp;
904         PCMconftab *ct, *et, *t;
905         PCMmap *m;
906         int i, index, irq;
907         char *cp;
908
909         irq = isa->irq;
910         if(irq == 2)
911                 irq = 9;
912
913         if(slotno > nslot)
914                 return -1;
915         pp = slot + slotno;
916
917         if(!pp->occupied)
918                 return -1;
919
920         et = &pp->ctab[pp->nctab];
921
922         ct = 0;
923         for(i = 0; i < isa->nopt; i++){
924                 if(strncmp(isa->opt[i], "index=", 6))
925                         continue;
926                 index = strtol(&isa->opt[i][6], &cp, 0);
927                 if(cp == &isa->opt[i][6] || index >= pp->nctab)
928                         return -1;
929                 ct = &pp->ctab[index];
930         }
931
932         if(ct == 0){
933                 /* assume default is right */
934                 if(pp->def)
935                         ct = pp->def;
936                 else
937                         ct = pp->ctab;
938         
939                 /* try for best match */
940                 if(ct->nio == 0
941                 || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
942                         for(t = pp->ctab; t < et; t++)
943                                 if(t->nio
944                                 && t->io[0].start == isa->port
945                                 && ((1<<irq) & t->irqs)){
946                                         ct = t;
947                                         break;
948                                 }
949                 }
950                 if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
951                         for(t = pp->ctab; t < et; t++)
952                                 if(t->nio && ((1<<irq) & t->irqs)){
953                                         ct = t;
954                                         break;
955                                 }
956                 }
957                 if(ct->nio == 0){
958                         for(t = pp->ctab; t < et; t++)
959                                 if(t->nio){
960                                         ct = t;
961                                         break;
962                                 }
963                 }
964         }
965
966         if(ct == et || ct->nio == 0)
967                 return -1;
968         if(isa->port == 0 && ct->io[0].start == 0)
969                 return -1;
970
971         /* route interrupts */
972         isa->irq = irq;
973         wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
974
975         /* set power and enable device */
976         x = vcode(ct->vpp1);
977         wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena);
978
979         /* 16-bit data path */
980         if(ct->bit16)
981                 x = Ftiming|Fiocs16|Fwidth16;
982         else
983                 x = Ftiming;
984         if(ct->nio == 2 && ct->io[1].start)
985                 x |= x<<4;
986         wrreg(pp, Rio, x);
987
988         /*
989          * enable io port map 0
990          * the 'top' register value includes the last valid address
991          */
992         if(isa->port == 0)
993                 isa->port = ct->io[0].start;
994         we = rdreg(pp, Rwe);
995         wrreg(pp, Riobtm0lo, isa->port);
996         wrreg(pp, Riobtm0hi, isa->port>>8);
997         i = isa->port+ct->io[0].len-1;
998         wrreg(pp, Riotop0lo, i);
999         wrreg(pp, Riotop0hi, i>>8);
1000         we |= 1<<6;
1001         if(ct->nio >= 2 && ct->io[1].start){
1002                 wrreg(pp, Riobtm1lo, ct->io[1].start);
1003                 wrreg(pp, Riobtm1hi, ct->io[1].start>>8);
1004                 i = ct->io[1].start+ct->io[1].len-1;
1005                 wrreg(pp, Riotop1lo, i);
1006                 wrreg(pp, Riotop1hi, i>>8);
1007                 we |= 1<<7;
1008         }
1009         wrreg(pp, Rwe, we);
1010
1011         /* only touch Rconfig if it is present */
1012         m = pcmmap(slotno, pp->cfg[0].caddr + Rconfig, 0x20, 1);
1013         p = KADDR(m->isa + pp->cfg[0].caddr - m->ca);
1014         if(pp->cfg[0].cpresent & (1<<Rconfig)){
1015                 /*  Reset adapter */
1016
1017                 /*  set configuration and interrupt type.
1018                  *  if level is possible on the card, use it.
1019                  */
1020                 x = ct->index;
1021                 if(ct->irqtype & 0x20)
1022                         x |= Clevel;
1023
1024                 /*  enable the device, enable address decode and
1025                  *  irq enable.
1026                  */
1027                 x |= Cfunc|Cdecode|Cirq;
1028
1029                 p[0] = x;
1030                 //delay(5);
1031                 microdelay(40);
1032         }
1033
1034         if(pp->cfg[0].cpresent & (1<<Riobase0)){
1035                 /* set up the iobase 0 */
1036                 p[Riobase0 << 1] = isa->port;
1037                 p[Riobase1 << 1] = isa->port >> 8;
1038         }
1039
1040         if(pp->cfg[0].cpresent & (1<<Riosize))
1041                 p[Riosize << 1] = ct->io[0].len;
1042         pcmunmap(slotno, m);
1043         return 0;
1044 }