]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devi82365.c
kernel: cleanup the software mouse cursor mess
[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                         m->ref++;
272                         unlock(&pp->mlock);
273                         return m;
274                 }
275                 bit <<= 1;
276                 if(nm == 0 && m->ref == 0)
277                         nm = m;
278         }
279         m = nm;
280         if(m == 0){
281                 unlock(&pp->mlock);
282                 return 0;
283         }
284
285         /* if isa space isn't big enough, free it and get more */
286         if(m->len < len){
287                 if(m->len){
288                         umbfree(m->isa, m->len);
289                         m->len = 0;
290                 }
291                 m->isa = umballoc(-1, len, Mgran);
292                 if(m->isa == -1){
293                         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         if(cp == nil){
488                 print("i82365probe: out of memory\n");
489                 return 0;
490         }
491         cp->xreg = x;
492         cp->dreg = d;
493         cp->dev = dev;
494         cp->type = Ti82365;
495         cp->nslot = 2;
496
497         switch(id){
498         case 0x82:
499         case 0x83:
500         case 0x84:
501                 /* could be a cirrus */
502                 outb(x, Rchipinfo + (dev<<7));
503                 outb(d, 0);
504                 c = inb(d);
505                 if((c & 0xc0) != 0xc0)
506                         break;
507                 c = inb(d);
508                 if((c & 0xc0) != 0x00)
509                         break;
510                 if(c & 0x20){
511                         cp->type = Tpd6720;
512                 } else {
513                         cp->type = Tpd6710;
514                         cp->nslot = 1;
515                 }
516
517                 /* low power mode */
518                 outb(x, Rmisc2 + (dev<<7));
519                 c = inb(d);
520                 outb(d, c & ~Flowpow);
521                 break;
522         }
523
524         /* if it's not a Cirrus, it could be a Vadem... */
525         if(cp->type == Ti82365){
526                 /* unlock the Vadem extended regs */
527                 outb(x, 0x0E + (dev<<7));
528                 outb(x, 0x37 + (dev<<7));
529
530                 /* make the id register show the Vadem id */
531                 outb(x, 0x3A + (dev<<7));
532                 c = inb(d);
533                 outb(d, c|0xC0);
534                 outb(x, Rid + (dev<<7));
535                 c = inb(d);
536                 if(c & 0x08)
537                         cp->type = Tvg46x;
538
539                 /* go back to Intel compatible id */
540                 outb(x, 0x3A + (dev<<7));
541                 c = inb(d);
542                 outb(d, c & ~0xC0);
543         }
544
545         memset(&isa, 0, sizeof(ISAConf));
546         if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq)
547                 cp->irq = isa.irq;
548         else
549                 cp->irq = IrqPCMCIA;
550
551         for(i = 0; i < isa.nopt; i++){
552                 if(cistrncmp(isa.opt[i], "nslot=", 6))
553                         continue;
554                 nslot = strtol(&isa.opt[i][6], nil, 0);
555                 if(nslot > 0 && nslot <= 2)
556                         cp->nslot = nslot;
557         }
558
559         controller[ncontroller++] = cp;
560         return cp;
561 }
562
563 static void
564 i82365dump(PCMslot *pp)
565 {
566         int i;
567
568         for(i = 0; i < 0x40; i++){
569                 if((i&0x0F) == 0)
570                         print("\n%2.2uX:        ", i);
571                 print("%2.2uX ", rdreg(pp, i));
572                 if(((i+1) & 0x0F) == 0x08)
573                         print(" - ");
574         }
575         print("\n");
576 }
577
578 /*
579  *  set up for slot cards
580  */
581 void
582 devi82365link(void)
583 {
584         static int already;
585         int i, j;
586         I82365 *cp;
587         PCMslot *pp;
588         char buf[32], *p;
589
590         if(already)
591                 return;
592         already = 1;
593
594         if((p=getconf("pcmcia0")) && strncmp(p, "disabled", 8)==0)
595                 return;
596
597         if(_pcmspecial)
598                 return;
599         
600         /* look for controllers if the ports aren't already taken */
601         if(ioalloc(0x3E0, 2, 0, "i82365.0") >= 0){
602                 i82365probe(0x3E0, 0x3E1, 0);
603                 i82365probe(0x3E0, 0x3E1, 1);
604                 if(ncontroller == 0)
605                         iofree(0x3E0);
606         }
607         if(ioalloc(0x3E2, 2, 0, "i82365.1") >= 0){
608                 i = ncontroller;
609                 i82365probe(0x3E2, 0x3E3, 0);
610                 i82365probe(0x3E2, 0x3E3, 1);
611                 if(ncontroller == i)
612                         iofree(0x3E2);
613         }
614
615         if(ncontroller == 0)
616                 return;
617
618         for(i = 0; i < ncontroller; i++)
619                 nslot += controller[i]->nslot;
620         slot = xalloc(nslot * sizeof(PCMslot));
621         if(slot == nil){
622                 print("i82365link: out of memory\n");
623                 nslot = 0;
624                 return;
625         }
626
627         _pcmspecial = pcmcia_pcmspecial;
628         _pcmspecialclose = pcmcia_pcmspecialclose;
629
630         lastslot = slot;
631         for(i = 0; i < ncontroller; i++){
632                 cp = controller[i];
633                 print("#y%d: %d slot %s: port 0x%uX irq %d\n",
634                         i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq);
635                 for(j = 0; j < cp->nslot; j++){
636                         pp = lastslot++;
637                         pp->slotno = pp - slot;
638                         pp->memlen = 64*MB;
639                         pp->base = (cp->dev<<7) | (j<<6);
640                         pp->cp = cp;
641                         pp->msec = ~0;
642                         pp->verstr[0] = 0;
643                         slotdis(pp);
644
645                         /* interrupt on status change */
646                         wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena);
647                         rdreg(pp, Rcsc);
648                 }
649
650                 /* for card management interrupts */
651                 snprint(buf, sizeof buf, "i82365.%d", i);
652                 intrenable(cp->irq, i82365intr, 0, BUSUNKNOWN, buf);
653         }
654 }
655
656 static Chan*
657 i82365attach(char *spec)
658 {
659         return devattach('y', spec);
660 }
661
662 static Walkqid*
663 i82365walk(Chan *c, Chan *nc, char **name, int nname)
664 {
665         return devwalk(c, nc, name, nname, 0, 0, pcmgen);
666 }
667
668 static int
669 i82365stat(Chan *c, uchar *db, int n)
670 {
671         return devstat(c, db, n, 0, 0, pcmgen);
672 }
673
674 static Chan*
675 i82365open(Chan *c, int omode)
676 {
677         if(c->qid.type & QTDIR){
678                 if(omode != OREAD)
679                         error(Eperm);
680         } else
681                 increfp(slot + SLOTNO(c));
682         c->mode = openmode(omode);
683         c->flag |= COPEN;
684         c->offset = 0;
685         return c;
686 }
687
688 static void
689 i82365close(Chan *c)
690 {
691         if(c->flag & COPEN)
692                 if((c->qid.type & QTDIR) == 0)
693                         decrefp(slot+SLOTNO(c));
694 }
695
696 /* a memmove using only bytes */
697 static void
698 memmoveb(uchar *to, uchar *from, int n)
699 {
700         while(n-- > 0)
701                 *to++ = *from++;
702 }
703
704 /* a memmove using only shorts & bytes */
705 static void
706 memmoves(uchar *to, uchar *from, int n)
707 {
708         ushort *t, *f;
709
710         if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){
711                 while(n-- > 0)
712                         *to++ = *from++;
713         } else {
714                 n = n/2;
715                 t = (ushort*)to;
716                 f = (ushort*)from;
717                 while(n-- > 0)
718                         *t++ = *f++;
719         }
720 }
721
722 static long
723 pcmread(int slotno, int attr, void *a, long n, vlong off)
724 {
725         int i, len;
726         PCMmap *m;
727         uchar *ac;
728         PCMslot *pp;
729         ulong offset = off;
730
731         pp = slot + slotno;
732         if(pp->memlen < offset)
733                 return 0;
734         if(pp->memlen < offset + n)
735                 n = pp->memlen - offset;
736
737         m = 0;
738         if(waserror()){
739                 if(m)
740                         pcmunmap(pp->slotno, m);
741                 nexterror();
742         }
743
744         ac = a;
745         for(len = n; len > 0; len -= i){
746                 m = pcmmap(pp->slotno, offset, 0, attr);
747                 if(m == 0)
748                         error("cannot map PCMCIA card");
749                 if(offset + len > m->cea)
750                         i = m->cea - offset;
751                 else
752                         i = len;
753                 memmoveb(ac, KADDR(m->isa + offset - m->ca), i);
754                 pcmunmap(pp->slotno, m);
755                 offset += i;
756                 ac += i;
757         }
758
759         poperror();
760         return n;
761 }
762
763 static long
764 i82365read(Chan *c, void *a, long n, vlong off)
765 {
766         char *p, *buf, *e;
767         PCMslot *pp;
768         ulong offset = off;
769
770         switch(TYPE(c)){
771         case Qdir:
772                 return devdirread(c, a, n, 0, 0, pcmgen);
773         case Qmem:
774         case Qattr:
775                 return pcmread(SLOTNO(c), TYPE(c) == Qattr, a, n, off);
776         case Qctl:
777                 buf = p = smalloc(READSTR);
778                 e = p + READSTR;
779                 pp = slot + SLOTNO(c);
780
781                 buf[0] = 0;
782                 if(pp->occupied){
783                         p = seprint(p, e, "occupied\n");
784                         if(pp->verstr[0])
785                                 p = seprint(p, e, "version %s\n", pp->verstr);
786                 }
787                 if(pp->enabled)
788                         p = seprint(p, e, "enabled\n");
789                 if(pp->powered)
790                         p = seprint(p, e, "powered\n");
791                 if(pp->configed)
792                         p = seprint(p, e, "configed\n");
793                 if(pp->wrprot)
794                         p = seprint(p, e, "write protected\n");
795                 if(pp->busy)
796                         p = seprint(p, e, "busy\n");
797                 seprint(p, e, "battery lvl %d\n", pp->battery);
798
799                 n = readstr(offset, a, n, buf);
800                 free(buf);
801
802                 return n;
803         }
804         error(Ebadarg);
805         return -1;      /* not reached */
806 }
807
808 static long
809 pcmwrite(int dev, int attr, void *a, long n, vlong off)
810 {
811         int i, len;
812         PCMmap *m;
813         uchar *ac;
814         PCMslot *pp;
815         ulong offset = off;
816
817         pp = slot + dev;
818         if(pp->memlen < offset)
819                 return 0;
820         if(pp->memlen < offset + n)
821                 n = pp->memlen - offset;
822
823         m = 0;
824         if(waserror()){
825                 if(m)
826                         pcmunmap(pp->slotno, m);
827                 nexterror();
828         }
829
830         ac = a;
831         for(len = n; len > 0; len -= i){
832                 m = pcmmap(pp->slotno, offset, 0, attr);
833                 if(m == 0)
834                         error("cannot map PCMCIA card");
835                 if(offset + len > m->cea)
836                         i = m->cea - offset;
837                 else
838                         i = len;
839                 memmoveb(KADDR(m->isa + offset - m->ca), ac, i);
840                 pcmunmap(pp->slotno, m);
841                 offset += i;
842                 ac += i;
843         }
844
845         poperror();
846         return n;
847 }
848
849 static long
850 i82365write(Chan *c, void *a, long n, vlong off)
851 {
852         PCMslot *pp;
853         char buf[32];
854
855         switch(TYPE(c)){
856         case Qctl:
857                 if(n >= sizeof(buf))
858                         n = sizeof(buf) - 1;
859                 strncpy(buf, a, n);
860                 buf[n] = 0;
861                 pp = slot + SLOTNO(c);
862                 if(!pp->occupied)
863                         error(Eio);
864
865                 /* set vpp on card */
866                 if(strncmp(buf, "vpp", 3) == 0)
867                         wrreg(pp, Rpc, vcode(strtol(buf+3, 0, 0))|Fautopower|Foutena|Fcardena);
868                 return n;
869         case Qmem:
870         case Qattr:
871                 pp = slot + SLOTNO(c);
872                 if(pp->occupied == 0 || pp->enabled == 0)
873                         error(Eio);
874                 n = pcmwrite(pp->slotno, TYPE(c) == Qattr, a, n, off);
875                 if(n < 0)
876                         error(Eio);
877                 return n;
878         }
879         error(Ebadarg);
880         return -1;      /* not reached */
881 }
882
883 Dev i82365devtab = {
884         'y',
885         "i82365",
886
887         devreset,
888         devinit,
889         devshutdown,
890         i82365attach,
891         i82365walk,
892         i82365stat,
893         i82365open,
894         devcreate,
895         i82365close,
896         i82365read,
897         devbread,
898         i82365write,
899         devbwrite,
900         devremove,
901         devwstat,
902 };
903
904 /*
905  *  configure the PCMslot for IO.  We assume very heavily that we can read
906  *  configuration info from the CIS.  If not, we won't set up correctly.
907  */
908 static int
909 pcmio(int slotno, ISAConf *isa)
910 {
911         uchar we, x, *p;
912         PCMslot *pp;
913         PCMconftab *ct, *et, *t;
914         PCMmap *m;
915         int i, index, irq;
916         char *cp;
917
918         irq = isa->irq;
919         if(irq == 2)
920                 irq = 9;
921
922         if(slotno > nslot)
923                 return -1;
924         pp = slot + slotno;
925
926         if(!pp->occupied)
927                 return -1;
928
929         et = &pp->ctab[pp->nctab];
930
931         ct = 0;
932         for(i = 0; i < isa->nopt; i++){
933                 if(strncmp(isa->opt[i], "index=", 6))
934                         continue;
935                 index = strtol(&isa->opt[i][6], &cp, 0);
936                 if(cp == &isa->opt[i][6] || index >= pp->nctab)
937                         return -1;
938                 ct = &pp->ctab[index];
939         }
940
941         if(ct == 0){
942                 /* assume default is right */
943                 if(pp->def)
944                         ct = pp->def;
945                 else
946                         ct = pp->ctab;
947         
948                 /* try for best match */
949                 if(ct->nio == 0
950                 || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
951                         for(t = pp->ctab; t < et; t++)
952                                 if(t->nio
953                                 && t->io[0].start == isa->port
954                                 && ((1<<irq) & t->irqs)){
955                                         ct = t;
956                                         break;
957                                 }
958                 }
959                 if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
960                         for(t = pp->ctab; t < et; t++)
961                                 if(t->nio && ((1<<irq) & t->irqs)){
962                                         ct = t;
963                                         break;
964                                 }
965                 }
966                 if(ct->nio == 0){
967                         for(t = pp->ctab; t < et; t++)
968                                 if(t->nio){
969                                         ct = t;
970                                         break;
971                                 }
972                 }
973         }
974
975         if(ct == et || ct->nio == 0)
976                 return -1;
977         if(isa->port == 0 && ct->io[0].start == 0)
978                 return -1;
979
980         /* route interrupts */
981         isa->irq = irq;
982         wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
983
984         /* set power and enable device */
985         x = vcode(ct->vpp1);
986         wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena);
987
988         /* 16-bit data path */
989         if(ct->bit16)
990                 x = Ftiming|Fiocs16|Fwidth16;
991         else
992                 x = Ftiming;
993         if(ct->nio == 2 && ct->io[1].start)
994                 x |= x<<4;
995         wrreg(pp, Rio, x);
996
997         /*
998          * enable io port map 0
999          * the 'top' register value includes the last valid address
1000          */
1001         if(isa->port == 0)
1002                 isa->port = ct->io[0].start;
1003         we = rdreg(pp, Rwe);
1004         wrreg(pp, Riobtm0lo, isa->port);
1005         wrreg(pp, Riobtm0hi, isa->port>>8);
1006         i = isa->port+ct->io[0].len-1;
1007         wrreg(pp, Riotop0lo, i);
1008         wrreg(pp, Riotop0hi, i>>8);
1009         we |= 1<<6;
1010         if(ct->nio >= 2 && ct->io[1].start){
1011                 wrreg(pp, Riobtm1lo, ct->io[1].start);
1012                 wrreg(pp, Riobtm1hi, ct->io[1].start>>8);
1013                 i = ct->io[1].start+ct->io[1].len-1;
1014                 wrreg(pp, Riotop1lo, i);
1015                 wrreg(pp, Riotop1hi, i>>8);
1016                 we |= 1<<7;
1017         }
1018         wrreg(pp, Rwe, we);
1019
1020         /* only touch Rconfig if it is present */
1021         m = pcmmap(slotno, pp->cfg[0].caddr + Rconfig, 0x20, 1);
1022         p = KADDR(m->isa + pp->cfg[0].caddr - m->ca);
1023         if(pp->cfg[0].cpresent & (1<<Rconfig)){
1024                 /*  Reset adapter */
1025
1026                 /*  set configuration and interrupt type.
1027                  *  if level is possible on the card, use it.
1028                  */
1029                 x = ct->index;
1030                 if(ct->irqtype & 0x20)
1031                         x |= Clevel;
1032
1033                 /*  enable the device, enable address decode and
1034                  *  irq enable.
1035                  */
1036                 x |= Cfunc|Cdecode|Cirq;
1037
1038                 p[0] = x;
1039                 //delay(5);
1040                 microdelay(40);
1041         }
1042
1043         if(pp->cfg[0].cpresent & (1<<Riobase0)){
1044                 /* set up the iobase 0 */
1045                 p[Riobase0 << 1] = isa->port;
1046                 p[Riobase1 << 1] = isa->port >> 8;
1047         }
1048
1049         if(pp->cfg[0].cpresent & (1<<Riosize))
1050                 p[Riosize << 1] = ct->io[0].len;
1051         pcmunmap(slotno, m);
1052         return 0;
1053 }