xref: /inferno-os/os/mpc/devpcmcia.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 
9 /*
10  *  MPC821/3 PCMCIA driver (prototype)
11  *
12  * unlike the i82365 adapter, there isn't an offset register:
13  * card addresses are simply the lower order 26 bits of the host address.
14  *
15  * to do:
16  *	split allocation of memory/attrib (all 26 bits valid) and io space (typically 10 or 12 bits)
17  *	correct config
18  *	interrupts and i/o space access
19  *	DMA?
20  *	power control
21  */
22 
23 enum
24 {
25 	Maxctlr=	1,
26 	Maxslot=	2,
27 	Slotashift=	16,
28 
29 	/* pipr */
30 	Cbvs1=	1<<15,
31 	Cbvs2=	1<<14,
32 	Cbwp=	1<<13,
33 	Cbcd2=	1<<12,
34 	Cbcd1=	1<<11,
35 	Cbbvd2=	1<<10,
36 	Cbbvd1=	1<<9,
37 	Cbrdy=	1<<8,
38 
39 	/* pscr and per */
40 	Cbvs1_c=	1<<15,
41 	Cbvs2_c=	1<<14,
42 	Cbwp_c=	1<<13,
43 	Cbcd2_c=	1<<12,
44 	Cbcd1_c=	1<<11,
45 	Cbbvd2_c=	1<<10,
46 	Cbbvd1_c=	1<<9,
47 	Cbrdy_l=	1<<7,
48 	Cbrdy_h=	1<<6,
49 	Cbrdy_r=	1<<5,
50 	Cbrdy_f=	1<<4,
51 
52 	/* pgcr[n] */
53 	Cbdreq_int=	0<<14,
54 	Cbdreq_iois16=	2<<14,
55 	Cbdreq_spkr=	3<<14,
56 	Cboe=	1<<7,
57 	Cbreset=	1<<6,
58 
59 	/* porN */
60 	Rport8=	0<<6,
61 	Rport16=	1<<6,
62 	Rmtype=	7<<3,	/* memory type field */
63 	 Rmem=	0<<3,	/* common memory space */
64 	 Rattrib=	2<<3,	/* attribute space */
65 	 Rio=		3<<3,
66 	 Rdma=	4<<3,	/* normal DMA */
67 	 Rdmalx=	5<<3,	/* DMA, last transaction */
68 	 RA22_23= 6<<3,	/* ``drive A22 and A23 signals on CE2 and CE1'' */
69 	RslotB=	1<<2,	/* select slot B (always, on MPC823) */
70 	Rwp=	1<<1,	/* write protect */
71 	Rvalid=	1<<0,	/* region valid */
72 
73 	Nmap=		8,		/* max number of maps to use */
74 
75 	/*
76 	 *  configuration registers - they start at an offset in attribute
77 	 *  memory found in the CIS.
78 	 */
79 	Rconfig=	0,
80 	 Creset=	 (1<<7),	/*  reset device */
81 	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
82 	Rccsr=	2,
83 	 Ciack	= (1<<0),
84 	 Cipend	= (1<<1),
85 	 Cpwrdown=	(1<<2),
86 	 Caudioen=	(1<<3),
87 	 Ciois8=	(1<<5),
88 	 Cchgena=	(1<<6),
89 	 Cchange=	(1<<7),
90 	Rpin=	4,	/* pin replacement register */
91 	Rscpr=	6,	/* socket and copy register */
92 	Riob0=	10,
93 	Riob1=	12,
94 	Riob2=	14,
95 	Riob3=	16,
96 	Riolim=	18,
97 
98 	Maxctab=	8,		/* maximum configuration table entries */
99 	MaxCIS = 8192,		/* maximum CIS size in bytes */
100 	Mgran = 8192,		/* maximum size of reads and writes */
101 
102 	Statusbounce=20,	/* msec debounce time */
103 };
104 
105 typedef struct Ctlr Ctlr;
106 
107 /* a controller (there's only one) */
108 struct Ctlr
109 {
110 	int	dev;
111 	int	nslot;
112 
113 	/* memory maps */
114 	Lock	mlock;		/* lock down the maps */
115 	PCMmap	mmap[Nmap];	/* maps */
116 
117 	/* IO port allocation */
118 	ulong	nextport;
119 };
120 static Ctlr controller[Maxctlr];
121 
122 static PCMslot	*slot;
123 static PCMslot	*lastslot;
124 static int	nslot;
125 
126 static	Map	pcmmapv[Nmap+1];
127 static	RMap	pcmmaps = {"PCMCIA mappings"};
128 
129 static void	pcmciaintr(Ureg*, void*);
130 static void	pcmciareset(void);
131 static int	pcmio(int, ISAConf*);
132 static long	pcmread(int, int, void*, long, ulong);
133 static long	pcmwrite(int, int, void*, long, ulong);
134 static void	slotdis(PCMslot*);
135 
136 static ulong pcmmalloc(ulong, long);
137 static void	pcmfree(ulong, long);
138 static void pcmciaproc(void*);
139 
140 static void pcmciadump(PCMslot*);
141 
142 /*
143  *  get info about card
144  */
145 static void
146 slotinfo(PCMslot *pp)
147 {
148 	ulong pipr;
149 
150 	pipr = (m->iomem->pipr >> pp->slotshift) & 0xFF00;
151 print("pipr=%8.8lux/%lux\n", m->iomem->pipr, pipr);
152 	pp->v3_3 = (pipr&Cbvs1)!=0;
153 	pp->voltage = (((pipr & Cbvs2)!=0)<<1) | ((pipr & Cbvs1)!=0);
154 	pp->occupied = (pipr&(Cbcd1|Cbcd2))==0;
155 	pp->powered = pcmpowered(pp->slotno);
156 	pp->battery = (pipr & (Cbbvd1|Cbbvd2))>>9;
157 	pp->wrprot = (pipr&Cbwp)!=0;
158 	pp->busy = (pipr&Cbrdy)==0;
159 }
160 
161 static void
162 pcmdelay(int ms)
163 {
164 	if(up == nil)
165 		delay(ms);
166 	else
167 		tsleep(&up->sleep, return0, nil, ms);
168 }
169 
170 /*
171  *  enable the slot card
172  */
173 static void
174 slotena(PCMslot *pp)
175 {
176 	IMM *io;
177 
178 	if(pp->enabled)
179 		return;
180 	m->iomem->pgcr[pp->slotno] &= ~Cboe;
181 	pcmpower(pp->slotno, 1);
182 	eieio();
183 	pcmdelay(300);
184 	io = m->iomem;
185 	io->pgcr[pp->slotno] |= Cbreset;	/* active high */
186 	eieio();
187 	pcmdelay(100);
188 	io->pgcr[pp->slotno] &= ~Cbreset;
189 	eieio();
190 	pcmdelay(500);	/* ludicrous delay */
191 
192 	/* get configuration */
193 	slotinfo(pp);
194 	if(pp->occupied){
195 		if(pp->cisread == 0){
196 			pcmcisread(pp);
197 			pp->cisread = 1;
198 		}
199 		pp->enabled = 1;
200 	} else{
201 		print("empty slot\n");
202 		slotdis(pp);
203 	}
204 }
205 
206 /*
207  *  disable the slot card
208  */
209 static void
210 slotdis(PCMslot *pp)
211 {
212 	int i;
213 	Ctlr *ctlr;
214 	PCMmap *pm;
215 
216 iprint("slotdis %d\n", pp->slotno);
217 	pcmpower(pp->slotno, 0);
218 	m->iomem->pgcr[pp->slotno] |= Cboe;
219 	ctlr = pp->ctlr;
220 	for(i = 0; i < nelem(ctlr->mmap); i++){
221 		pm = &ctlr->mmap[i];
222 		if(m->iomem->pcmr[i].option & Rvalid && pm->slotno == pp->slotno)
223 			pcmunmap(pp->slotno, pm);
224 	}
225 	pp->enabled = 0;
226 	pp->cisread = 0;
227 }
228 
229 static void
230 pcmciardy(Ureg *ur, void *a)
231 {
232 	PCMslot *pp;
233 	ulong w;
234 
235 	pp = a;
236 	w = (m->iomem->pipr >> pp->slotshift) & 0xFF00;
237 	if(pp->occupied && (w & Cbrdy) == 0){ /* interrupt */
238 print("PCM.%dI#%lux|", pp->slotno, w);
239 		if(pp->intr.f != nil)
240 			pp->intr.f(ur, pp->intr.arg);
241 	}
242 }
243 
244 void
245 pcmintrenable(int slotno, void (*f)(Ureg*, void*), void *arg)
246 {
247 	PCMslot *pp;
248 	IMM *io;
249 	char name[KNAMELEN];
250 
251 	if(slotno < 0 || slotno >= nslot)
252 		panic("pcmintrenable");
253 	snprint(name, sizeof(name), "pcmcia.irq%d", slotno);
254 	io = ioplock();
255 	pp = slot+slotno;
256 	pp->intr.f = f;
257 	pp->intr.arg = arg;
258 	intrenable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name);
259 	io->per |= Cbrdy_l;	/* assumes used for irq, not rdy; assumes level interrupt always right */
260 	iopunlock();
261 }
262 
263 void
264 pcmintrdisable(int slotno, void (*f)(Ureg*, void*), void *arg)
265 {
266 	PCMslot *pp;
267 	IMM *io;
268 	char name[KNAMELEN];
269 
270 	if(slotno < 0 || slotno >= nslot)
271 		panic("pcmintrdisable");
272 	snprint(name, sizeof(name), "pcmcia.irq%d", slotno);
273 	io = ioplock();
274 	pp = slot+slotno;
275 	if(pp->intr.f == f && pp->intr.arg == arg){
276 		pp->intr.f = nil;
277 		pp->intr.arg = nil;
278 		intrdisable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name);
279 		io->per &= ~Cbrdy_l;
280 	}
281 	iopunlock();
282 }
283 
284 /*
285  *  status change interrupt
286  *
287  * this wakes a monitoring process to read the CIS,
288  * rather than holding other interrupts out here.
289  */
290 
291 static Rendez pcmstate;
292 
293 static int
294 statechanged(void *a)
295 {
296 	PCMslot *pp;
297 	int in;
298 
299 	pp = a;
300 	in = (m->iomem->pipr & (Cbcd1|Cbcd2))==0;
301 	return in != pp->occupied;
302 }
303 
304 static void
305 pcmciaintr(Ureg*, void*)
306 {
307 	ulong events;
308 
309 	if(slot == 0)
310 		return;
311 	events = m->iomem->pscr & (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c);
312 	eieio();
313 	m->iomem->pscr = events;
314 	/* TO DO: other slot */
315 iprint("PCM: #%lux|", events);
316 iprint("pipr=#%lux|", m->iomem->pipr & 0xFF00);
317 	wakeup(&pcmstate);
318 }
319 
320 static void
321 pcmciaproc(void*)
322 {
323 	ulong csc;
324 	PCMslot *pp;
325 	int was;
326 
327 	for(;;){
328 		sleep(&pcmstate, statechanged, slot+1);
329 		tsleep(&up->sleep, return0, nil, Statusbounce);
330 		/*
331 		 * voltage change 1,2
332 		 * write protect change
333 		 * card detect 1,2
334 		 * battery voltage 1 change (or SPKR-bar)
335 		 * battery voltage 2 change (or STSCHG-bar)
336 		 * card B rdy / IRQ-bar low
337 		 * card B rdy / IRQ-bar high
338 		 * card B rdy / IRQ-bar rising edge
339 		 * card B rdy / IRQ-bar falling edge
340 		 *
341 		 * TO DO: currently only handle card-present changes
342 		 */
343 
344 		for(pp = slot; pp < lastslot; pp++){
345 			if(pp->memlen == 0)
346 				continue;
347 			csc = (m->iomem->pipr>>pp->slotshift) & (Cbcd1|Cbcd2);
348 			was = pp->occupied;
349 			slotinfo(pp);
350 			if(csc == 0 && was != pp->occupied){
351 				if(!pp->occupied){
352 					slotdis(pp);
353 					if(pp->special && pp->notify.f != nil)
354 						pp->notify.f(pp->notify.arg, 1);
355 				}
356 			}
357 		}
358 	}
359 }
360 
361 static uchar greycode[] = {
362 	0, 1, 3, 2, 6, 7, 5, 4, 014, 015, 017, 016, 012, 013, 011, 010,
363 	030, 031, 033, 032, 036, 037, 035, 034, 024, 025, 027
364 };
365 
366 /*
367  *  get a map for pc card region, return corrected len
368  */
369 PCMmap*
370 pcmmap(int slotno, ulong offset, int len, int attr)
371 {
372 	Ctlr *ctlr;
373 	PCMslot *pp;
374 	PCMmap *pm, *nm;
375 	IMM *io;
376 	int i;
377 	ulong e, bsize, code, opt;
378 
379 	if(0)
380 		print("pcmmap: %d #%lux %d #%x\n", slotno, offset, len, attr);
381 	pp = slot + slotno;
382 	if(!pp->occupied)
383 		return nil;
384 	if(attr == 1){	/* account for ../port/cis.c's conventions */
385 		attr = Rattrib;
386 		if(len <= 0)
387 			len = MaxCIS*2;	/* TO DO */
388 	}
389 	ctlr = pp->ctlr;
390 
391 	/* convert offset to granularity */
392 	if(len <= 0)
393 		len = 1;
394 	e = offset+len;
395 	for(i=0;; i++){
396 		if(i >= nelem(greycode))
397 			return nil;
398 		bsize = 1<<i;
399 		offset &= ~(bsize-1);
400 		if(e <= offset+bsize)
401 			break;
402 	}
403 	code = greycode[i];
404 	if(0)
405 		print("i=%d bsize=%lud code=0%luo\n", i, bsize, code);
406 	e = offset+bsize;
407 	len = bsize;
408 
409 	lock(&ctlr->mlock);
410 
411 	/* look for an existing map that covers the right area */
412 	io = m->iomem;
413 	nm = nil;
414 	for(i=0; i<Nmap; i++){
415 		pm = &ctlr->mmap[i];
416 		if(io->pcmr[i].option & Rvalid &&
417 		   pm->slotno == slotno &&
418 		   pm->attr == attr &&
419 		   offset >= pm->ca && e <= pm->cea){
420 			pm->ref++;
421 			unlock(&ctlr->mlock);
422 			return pm;
423 		}
424 		if(nm == 0 && pm->ref == 0)
425 			nm = pm;
426 	}
427 	pm = nm;
428 	if(pm == nil){
429 		unlock(&ctlr->mlock);
430 		return nil;
431 	}
432 
433 	/* set up new map */
434 	pm->isa = pcmmalloc(offset, len);
435 	if(pm->isa == 0){
436 		/* address not available: in use, or too much to map */
437 		unlock(&ctlr->mlock);
438 		return 0;
439 	}
440 	if(0)
441 		print("mx=%d isa=#%lux\n", (int)(pm - ctlr->mmap), pm->isa);
442 
443 	pm->len = len;
444 	pm->ca = offset;
445 	pm->cea = pm->ca + pm->len;
446 	pm->attr = attr;
447 	i = pm - ctlr->mmap;
448 	io->pcmr[i].option &= ~Rvalid;	/* disable map before changing it */
449 	io->pcmr[i].base = pm->isa;
450 	opt = attr;
451 	opt |= code<<27;
452 	if((attr&Rmtype) == Rio){
453 		opt |= 4<<12;	/* PSST */
454 		opt |= 8<<7;	/* PSL */
455 		opt |= 2<<16;	/* PSHT */
456 	}else{
457 		opt |= 6<<12;	/* PSST */
458 		opt |= 24<<7;	/* PSL */
459 		opt |= 8<<16;	/* PSHT */
460 	}
461 	if((attr & Rport16) == 0)
462 		opt |= Rport8;
463 	if(slotno == 1)
464 		opt |= RslotB;
465 	io->pcmr[i].option = opt | Rvalid;
466 	pm->slotno = slotno;
467 	pm->ref = 1;
468 
469 	unlock(&ctlr->mlock);
470 	return pm;
471 }
472 
473 static void
474 pcmiomap(PCMslot *pp, PCMconftab *ct, int i)
475 {
476 	int n, attr;
477 	Ctlr *ctlr;
478 
479 	if(0)
480 		print("pcm iomap #%lux %lud\n", ct->io[i].start, ct->io[i].len);
481 	if(ct->io[i].len <= 0)
482 		return;
483 	if(ct->io[i].start == 0){
484 		n = 1<<ct->nlines;
485 		ctlr = pp->ctlr;
486 		lock(&ctlr->mlock);
487 		if(ctlr->nextport == 0)
488 			ctlr->nextport = 0xF000;
489 		ctlr->nextport = (ctlr->nextport + n - 1) & ~(n-1);
490 		ct->io[i].start = ctlr->nextport;
491 		ct->io[i].len = n;
492 		ctlr->nextport += n;
493 		unlock(&ctlr->mlock);
494 	}
495 	attr = Rio;
496 	if(ct->bit16)
497 		attr |= Rport16;
498 	ct->io[i].map = pcmmap(pp->slotno, ct->io[i].start, ct->io[i].len, attr);
499 }
500 
501 void
502 pcmunmap(int slotno, PCMmap* pm)
503 {
504 	int i;
505 	PCMslot *pp;
506 	Ctlr *ctlr;
507 
508 	pp = slot + slotno;
509 	if(pp->memlen == 0)
510 		return;
511 	ctlr = pp->ctlr;
512 	lock(&ctlr->mlock);
513 	if(pp->slotno == pm->slotno && --pm->ref == 0){
514 		i = pm - ctlr->mmap;
515 		m->iomem->pcmr[i].option = 0;
516 		m->iomem->pcmr[i].base = 0;
517 		pcmfree(pm->isa, pm->len);
518 	}
519 	unlock(&ctlr->mlock);
520 }
521 
522 static void
523 increfp(PCMslot *pp)
524 {
525 	if(incref(pp) == 1)
526 		slotena(pp);
527 }
528 
529 static void
530 decrefp(PCMslot *pp)
531 {
532 	if(decref(pp) == 0)
533 		slotdis(pp);
534 }
535 
536 /*
537  *  look for a card whose version contains 'idstr'
538  */
539 int
540 pcmspecial(char *idstr, ISAConf *isa)
541 {
542 	PCMslot *pp;
543 	extern char *strstr(char*, char*);
544 
545 	pcmciareset();
546 	for(pp = slot; pp < lastslot; pp++){
547 		if(pp->special || pp->memlen == 0)
548 			continue;	/* already taken */
549 		increfp(pp);
550 		if(pp->occupied && strstr(pp->verstr, idstr)){
551 			print("PCMslot #%d: Found %s - ",pp->slotno, idstr);
552 			if(isa == 0 || pcmio(pp->slotno, isa) == 0){
553 				print("ok.\n");
554 				pp->special = 1;
555 				return pp->slotno;
556 			}
557 			print("error with isa io\n");
558 		}
559 		decrefp(pp);
560 	}
561 	return -1;
562 }
563 
564 void
565 pcmspecialclose(int slotno)
566 {
567 	PCMslot *pp;
568 
569 	if(slotno >= nslot)
570 		panic("pcmspecialclose");
571 	pp = slot + slotno;
572 	pp->special = 0;
573 	decrefp(pp);
574 }
575 
576 void
577 pcmnotify(int slotno, void (*f)(void*, int), void* a)
578 {
579 	PCMslot *pp;
580 
581 	if(slotno < 0 || slotno >= nslot)
582 		panic("pcmnotify");
583 	pp = slot + slotno;
584 	if(pp->occupied && pp->special){
585 		pp->notify.f = f;
586 		pp->notify.arg = a;
587 	}
588 }
589 
590 /*
591  * reserve pcmcia slot address space [addr, addr+size[,
592  * returning a pointer to it, or nil if the space was already reserved.
593  */
594 static ulong
595 pcmmalloc(ulong addr, long size)
596 {
597 	return rmapalloc(&pcmmaps, PHYSPCMCIA+addr, size, size);
598 }
599 
600 static void
601 pcmfree(ulong a, long size)
602 {
603 	if(a != 0 && size > 0)
604 		mapfree(&pcmmaps, a, size);
605 }
606 
607 enum
608 {
609 	Qdir,
610 	Qmem,
611 	Qattr,
612 	Qctl,
613 };
614 
615 #define SLOTNO(c)	((c->qid.path>>8)&0xff)
616 #define TYPE(c)		(c->qid.path&0xff)
617 #define QID(s,t)	(((s)<<8)|(t))
618 
619 static int
620 pcmgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
621 {
622 	int slotno;
623 	Qid qid;
624 	long len;
625 	PCMslot *pp;
626 
627 	if(i>=3*nslot)
628 		return -1;
629 	slotno = i/3;
630 	pp = slot + slotno;
631 	if(pp->memlen == 0)
632 		return 0;
633 	len = 0;
634 	switch(i%3){
635 	case 0:
636 		qid.path = QID(slotno, Qmem);
637 		sprint(up->genbuf, "pcm%dmem", slotno);
638 		len = pp->memlen;
639 		break;
640 	case 1:
641 		qid.path = QID(slotno, Qattr);
642 		sprint(up->genbuf, "pcm%dattr", slotno);
643 		len = pp->memlen;
644 		break;
645 	case 2:
646 		qid.path = QID(slotno, Qctl);
647 		sprint(up->genbuf, "pcm%dctl", slotno);
648 		break;
649 	}
650 	qid.vers = 0;
651 	devdir(c, qid, up->genbuf, len, eve, 0660, dp);
652 	return 1;
653 }
654 
655 /*
656  * used only when debugging
657  */
658 static void
659 pcmciadump(PCMslot *)
660 {
661 	IMM *io;
662 	int i;
663 
664 	io = m->iomem;
665 	print("pipr #%4.4lux pscr #%4.4lux per #%4.4lux pgcr[1] #%8.8lux\n",
666 		io->pipr & 0xFFFF, io->pscr & 0xFFFF, io->per & 0xFFFF, io->pgcr[1]);
667 	for(i=0; i<8; i++)
668 		print("pbr%d #%8.8lux por%d #%8.8lux\n", i, io->pcmr[i].base, i, io->pcmr[i].option);
669 }
670 
671 /*
672  *  set up for slot cards
673  */
674 static void
675 pcmciareset(void)
676 {
677 	static int already;
678 	int i;
679 	Ctlr *cp;
680 	IMM *io;
681 	PCMslot *pp;
682 
683 	if(already)
684 		return;
685 	already = 1;
686 
687 	cp = controller;
688 	/* TO DO: set low power mode? ... */
689 
690 	mapinit(&pcmmaps, pcmmapv, sizeof(pcmmapv));
691 	mapfree(&pcmmaps, PHYSPCMCIA, PCMCIALEN);
692 
693 	io = m->iomem;
694 
695 	for(i=0; i<8; i++){
696 		io->pcmr[i].option = 0;
697 		io->pcmr[i].base = 0;
698 	}
699 
700 	io->pscr = ~0;	/* reset status */
701 	/* TO DO: Cboe, Cbreset */
702 	/* TO DO: two slots except on 823 */
703 	pcmenable();
704 	/* TO DO: if the card is there turn on 5V power to keep its battery alive */
705 	slot = xalloc(Maxslot * sizeof(PCMslot));
706 	lastslot = slot;
707 	slot[0].slotshift = Slotashift;
708 	slot[1].slotshift = 0;
709 	for(i=0; i<Maxslot; i++){
710 		pp = &slot[i];
711 		if(!pcmslotavail(i)){
712 			pp->memlen = 0;
713 			continue;
714 		}
715 		io->per |= (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c)<<pp->slotshift;	/* enable status interrupts */
716 		io->pgcr[i] = (1<<(31-PCMCIAio)) | (1<<(23-PCMCIAstatus));
717 		pp->slotno = i;
718 		pp->memlen = 8*MB;
719 		pp->ctlr = cp;
720 		//slotdis(pp);
721 		lastslot = slot;
722 		nslot = i+1;
723 	}
724 	if(1)
725 		pcmciadump(slot);
726 	intrenable(PCMCIAstatus, pcmciaintr, cp, BUSUNKNOWN, "pcmcia");
727 	print("pcmcia reset\n");
728 }
729 
730 static void
731 pcmciainit(void)
732 {
733 	kproc("pcmcia", pcmciaproc, nil, 0);
734 }
735 
736 static Chan*
737 pcmciaattach(char *spec)
738 {
739 	return devattach('y', spec);
740 }
741 
742 static Walkqid*
743 pcmciawalk(Chan *c, Chan *nc, char **name, int nname)
744 {
745 	return devwalk(c, nc, name, nname, 0, 0, pcmgen);
746 }
747 
748 static int
749 pcmciastat(Chan *c, uchar *dp, int n)
750 {
751 	return devstat(c, dp, n, 0, 0, pcmgen);
752 }
753 
754 static Chan*
755 pcmciaopen(Chan *c, int omode)
756 {
757 	if(c->qid.type & QTDIR){
758 		if(omode != OREAD)
759 			error(Eperm);
760 	} else
761 		increfp(slot + SLOTNO(c));
762 	c->mode = openmode(omode);
763 	c->flag |= COPEN;
764 	c->offset = 0;
765 	return c;
766 }
767 
768 static void
769 pcmciaclose(Chan *c)
770 {
771 	if(c->flag & COPEN)
772 		if((c->qid.type & QTDIR) == 0)
773 			decrefp(slot+SLOTNO(c));
774 }
775 
776 /* a memmove using only bytes */
777 static void
778 memmoveb(uchar *to, uchar *from, int n)
779 {
780 	while(n-- > 0)
781 		*to++ = *from++;
782 }
783 
784 static long
785 pcmread(int slotno, int attr, void *a, long n, ulong offset)
786 {
787 	int i, len;
788 	PCMmap *m;
789 	void *ka;
790 	uchar *ac;
791 	PCMslot *pp;
792 
793 	pp = slot + slotno;
794 	if(pp->memlen < offset)
795 		return 0;
796 	if(pp->memlen < offset + n)
797 		n = pp->memlen - offset;
798 
799 	ac = a;
800 	for(len = n; len > 0; len -= i){
801 		if((i = len) > Mgran)
802 			i = Mgran;
803 		m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem);
804 		if(m == 0)
805 			error("can't map PCMCIA card");
806 		if(waserror()){
807 			if(m)
808 				pcmunmap(pp->slotno, m);
809 			nexterror();
810 		}
811 		if(offset + len > m->cea)
812 			i = m->cea - offset;
813 		else
814 			i = len;
815 		ka = (char*)KADDR(m->isa) + (offset - m->ca);
816 		memmoveb(ac, ka, i);
817 		poperror();
818 		pcmunmap(pp->slotno, m);
819 		offset += i;
820 		ac += i;
821 	}
822 
823 	return n;
824 }
825 
826 static long
827 pcmciaread(Chan *c, void *a, long n, vlong offset)
828 {
829 	char *cp, *buf;
830 	ulong p;
831 	PCMslot *pp;
832 
833 	p = TYPE(c);
834 	switch(p){
835 	case Qdir:
836 		return devdirread(c, a, n, 0, 0, pcmgen);
837 	case Qmem:
838 	case Qattr:
839 		return pcmread(SLOTNO(c), p==Qattr, a, n, offset);
840 	case Qctl:
841 		buf = malloc(READSTR);
842 		if(buf == nil)
843 			error(Enomem);
844 		if(waserror()){
845 			free(buf);
846 			nexterror();
847 		}
848 		cp = buf;
849 		pp = slot + SLOTNO(c);
850 		if(pp->occupied)
851 			cp += sprint(cp, "occupied\n");
852 		if(pp->enabled)
853 			cp += sprint(cp, "enabled\n");
854 		if(pp->powered)
855 			cp += sprint(cp, "powered\n");
856 		if(pp->configed)
857 			cp += sprint(cp, "configed\n");
858 		if(pp->wrprot)
859 			cp += sprint(cp, "write protected\n");
860 		if(pp->busy)
861 			cp += sprint(cp, "busy\n");
862 		if(pp->v3_3)
863 			cp += sprint(cp, "3.3v ok\n");
864 		cp += sprint(cp, "battery lvl %d\n", pp->battery);
865 		cp += sprint(cp, "voltage select %d\n", pp->voltage);
866 		/* TO DO: could return pgcr[] values for debugging */
867 		*cp = 0;
868 		n = readstr(offset, a, n, buf);
869 		poperror();
870 		free(buf);
871 		break;
872 	default:
873 		n=0;
874 		break;
875 	}
876 	return n;
877 }
878 
879 static long
880 pcmwrite(int dev, int attr, void *a, long n, ulong offset)
881 {
882 	int i, len;
883 	PCMmap *m;
884 	void *ka;
885 	uchar *ac;
886 	PCMslot *pp;
887 
888 	pp = slot + dev;
889 	if(pp->memlen < offset)
890 		return 0;
891 	if(pp->memlen < offset + n)
892 		n = pp->memlen - offset;
893 
894 	ac = a;
895 	for(len = n; len > 0; len -= i){
896 		if((i = len) > Mgran)
897 			i = Mgran;
898 		m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem);
899 		if(m == 0)
900 			error("can't map PCMCIA card");
901 		if(waserror()){
902 			if(m)
903 				pcmunmap(pp->slotno, m);
904 			nexterror();
905 		}
906 		if(offset + len > m->cea)
907 			i = m->cea - offset;
908 		else
909 			i = len;
910 		ka = (char*)KADDR(m->isa) + (offset - m->ca);
911 		memmoveb(ka, ac, i);
912 		poperror();
913 		pcmunmap(pp->slotno, m);
914 		offset += i;
915 		ac += i;
916 	}
917 
918 	return n;
919 }
920 
921 static long
922 pcmciawrite(Chan *c, void *a, long n, vlong offset)
923 {
924 	ulong p;
925 	PCMslot *pp;
926 	char buf[32];
927 
928 	p = TYPE(c);
929 	switch(p){
930 	case Qctl:
931 		if(n >= sizeof(buf))
932 			n = sizeof(buf) - 1;
933 		strncpy(buf, a, n);
934 		buf[n] = 0;
935 		pp = slot + SLOTNO(c);
936 		if(!pp->occupied)
937 			error(Eio);
938 
939 		/* set vpp on card */
940 		if(strncmp(buf, "vpp", 3) == 0){
941 			p = strtol(buf+3, nil, 0);
942 			pcmsetvpp(pp->slotno, p);
943 		}
944 		break;
945 	case Qmem:
946 	case Qattr:
947 		pp = slot + SLOTNO(c);
948 		if(pp->occupied == 0 || pp->enabled == 0)
949 			error(Eio);
950 		n = pcmwrite(pp->slotno, p == Qattr, a, n, offset);
951 		if(n < 0)
952 			error(Eio);
953 		break;
954 	default:
955 		error(Ebadusefd);
956 	}
957 	return n;
958 }
959 
960 Dev pcmciadevtab = {
961 	'y',
962 	"pcmcia",
963 
964 	pcmciareset,
965 	pcmciainit,
966 	devshutdown,
967 	pcmciaattach,
968 	pcmciawalk,
969 	pcmciastat,
970 	pcmciaopen,
971 	devcreate,
972 	pcmciaclose,
973 	pcmciaread,
974 	devbread,
975 	pcmciawrite,
976 	devbwrite,
977 	devremove,
978 	devwstat,
979 };
980 
981 /*
982  *  configure the PCMslot for IO.  We assume very heavily that we can read
983  *  configuration info from the CIS.  If not, we won't set up correctly.
984  */
985 static int
986 pcmio(int slotno, ISAConf *isa)
987 {
988 	PCMslot *pp;
989 	PCMconftab *ct, *et, *t;
990 	PCMmap *pm;
991 	uchar *p;
992 	int irq, i, x;
993 
994 	irq = isa->irq;
995 	if(irq == 2)
996 		irq = 9;
997 
998 	if(slotno > nslot)
999 		return -1;
1000 	pp = slot + slotno;
1001 
1002 	if(!pp->occupied)
1003 		return -1;
1004 
1005 	et = &pp->ctab[pp->nctab];
1006 
1007 	/* assume default is right */
1008 	if(pp->def)
1009 		ct = pp->def;
1010 	else
1011 		ct = pp->ctab;
1012 	/* try for best match */
1013 	if(ct->nlines == 0 || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
1014 		for(t = pp->ctab; t < et; t++)
1015 			if(t->nlines && t->io[0].start == isa->port && ((1<<irq) & t->irqs)){
1016 				ct = t;
1017 				break;
1018 			}
1019 	}
1020 	if(ct->nlines == 0 || ((1<<irq) & ct->irqs) == 0){
1021 		for(t = pp->ctab; t < et; t++)
1022 			if(t->nlines && ((1<<irq) & t->irqs)){
1023 				ct = t;
1024 				break;
1025 			}
1026 	}
1027 	if(ct->nlines == 0){
1028 		for(t = pp->ctab; t < et; t++)
1029 			if(t->nlines){
1030 				ct = t;
1031 				break;
1032 			}
1033 	}
1034 print("slot %d: nlines=%d iolen=%lud irq=%d ct->index=%d nport=%d ct->port=#%lux/%lux\n", slotno, ct->nlines, ct->io[0].len, irq, ct->index, ct->nio, ct->io[0].start, isa->port);
1035 	if(ct == et || ct->nlines == 0)
1036 		return -1;
1037 	/* route interrupts */
1038 	isa->irq = irq;
1039 	//wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
1040 	delay(2);
1041 
1042 	/* set power and enable device */
1043 	pcmsetvcc(pp->slotno, ct->vcc);
1044 	pcmsetvpp(pp->slotno, ct->vpp1);
1045 
1046 	delay(2);	/* could poll BSY during power change */
1047 
1048 	for(i=0; i<ct->nio; i++)
1049 		pcmiomap(pp, ct, i);
1050 
1051 	if(ct->nio)
1052 		isa->port = ct->io[0].start;
1053 
1054 	/* only touch Rconfig if it is present */
1055 	if(pp->cpresent & (1<<Rconfig)){
1056 print("Rconfig present: #%lux\n", pp->caddr+Rconfig);
1057 		/*  Reset adapter */
1058 		pm = pcmmap(slotno, pp->caddr + Rconfig, 1, Rattrib);
1059 		if(pm == nil)
1060 			return -1;
1061 
1062 		p = (uchar*)KADDR(pm->isa) + (pp->caddr + Rconfig - pm->ca);
1063 
1064 		/* set configuration and interrupt type */
1065 		x = ct->index;
1066 		if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7))
1067 			x |= Clevel;
1068 		*p = x;
1069 		delay(5);
1070 
1071 		pcmunmap(pp->slotno, pm);
1072 print("Adapter reset\n");
1073 	}
1074 
1075 	return 0;
1076 }
1077