1 /*
2 * PCI support code.
3 * Needs a massive rewrite.
4 */
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11
12 #define DBG if(0) pcilog
13
14 typedef struct Pci Pci;
15
16 struct
17 {
18 char output[PCICONSSIZE];
19 int ptr;
20 }PCICONS;
21
22 int
pcilog(char * fmt,...)23 pcilog(char *fmt, ...)
24 {
25 int n;
26 va_list arg;
27 char buf[PRINTSIZE];
28
29 va_start(arg, fmt);
30 n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
31 va_end(arg);
32
33 memmove(PCICONS.output+PCICONS.ptr, buf, n);
34 PCICONS.ptr += n;
35 return n;
36 }
37
38 enum
39 {
40 MaxFNO = 7,
41 MaxUBN = 255,
42 };
43
44 enum
45 { /* command register */
46 IOen = (1<<0),
47 MEMen = (1<<1),
48 MASen = (1<<2),
49 MemWrInv = (1<<4),
50 PErrEn = (1<<6),
51 SErrEn = (1<<8),
52 };
53
54 typedef struct {
55 ulong cap;
56 ulong ctl;
57 } Capctl;
58 typedef struct {
59 Capctl dev;
60 Capctl link;
61 Capctl slot;
62 } Devlinkslot;
63
64 /* capability list id 0x10 is pci-e */
65 struct Pci {
66 /* pci-compatible config */
67 /* what io.h calls type 0 & type 1 pre-defined header */
68 ulong id;
69 ulong cs;
70 ulong revclass;
71 ulong misc; /* cache line size, latency timer, header type, bist */
72 ulong bar[2]; /* always 0 on tegra 2 */
73
74 /* types 1 & 2 pre-defined header */
75 ulong bus;
76 ulong ioaddrs;
77 ulong memaddrs;
78 ulong prefmem;
79 ulong prefbasehi;
80 ulong preflimhi;
81 /* type 2 pre-defined header only */
82 ulong ioaddrhi;
83 ulong cfgcapoff; /* offset in cfg. space to cap. list (0x40) */
84 ulong rom;
85 ulong intr; /* PciINT[LP] */
86 /* subsystem capability regs */
87 ulong subsysid;
88 ulong subsyscap;
89 /* */
90
91 Capctl pwrmgmt;
92
93 /* msi */
94 ulong msictlcap;
95 ulong msimsgaddr[2]; /* little-endian */
96 ulong msimsgdata;
97
98 /* pci-e cap. */
99 uchar _pad0[0x80-0x60];
100 ulong pciecap;
101 Devlinkslot port0;
102 ulong rootctl;
103 ulong rootsts;
104 Devlinkslot port1;
105
106 /* 0xbc */
107
108 };
109
110 enum {
111 /* offsets from soc.pci */
112 Port0 = 0,
113 Port1 = 0x1000,
114 Pads = 0x3000,
115 Afi = 0x3800,
116 Aficfg = Afi + 0xac,
117 Cfgspace = 0x4000,
118 Ecfgspace = 0x104000,
119
120 /* cs bits */
121 Iospace = 1<<0,
122 Memspace = 1<<1,
123 Busmaster = 1<<2,
124
125 /* Aficfg bits */
126 Fpcion = 1<<0,
127 };
128
129 struct Pcictlr {
130 union {
131 uchar _padpci[0x1000];
132 Pci;
133 } ports[2];
134 uchar _padpads[0x1000];
135 uchar pads[0x800];
136 uchar afi[0x800];
137 ulong cfg[0x1000];
138 ulong extcfg[0x1000];
139 };
140
141 static Lock pcicfglock;
142 static Lock pcicfginitlock;
143 static int pcicfgmode = -1;
144 static int pcimaxbno = 1; /* was 7; only 2 pci buses; touching 3rd hangs */
145 static int pcimaxdno;
146 static Pcidev* pciroot;
147 static Pcidev* pcilist;
148 static Pcidev* pcitail;
149
150 static int pcicfgrw8(int, int, int, int);
151 static int pcicfgrw16(int, int, int, int);
152 static int pcicfgrw32(int, int, int, int);
153
154 static char* bustypes[] = {
155 "CBUSI",
156 "CBUSII",
157 "EISA",
158 "FUTURE",
159 "INTERN",
160 "ISA",
161 "MBI",
162 "MBII",
163 "MCA",
164 "MPI",
165 "MPSA",
166 "NUBUS",
167 "PCI",
168 "PCMCIA",
169 "TC",
170 "VL",
171 "VME",
172 "XPRESS",
173 };
174
175 static int
tbdffmt(Fmt * fmt)176 tbdffmt(Fmt* fmt)
177 {
178 char *p;
179 int l, r;
180 uint type, tbdf;
181
182 if((p = malloc(READSTR)) == nil)
183 return fmtstrcpy(fmt, "(tbdfconv)");
184
185 switch(fmt->r){
186 case 'T':
187 tbdf = va_arg(fmt->args, int);
188 if(tbdf == BUSUNKNOWN)
189 snprint(p, READSTR, "unknown");
190 else{
191 type = BUSTYPE(tbdf);
192 if(type < nelem(bustypes))
193 l = snprint(p, READSTR, bustypes[type]);
194 else
195 l = snprint(p, READSTR, "%d", type);
196 snprint(p+l, READSTR-l, ".%d.%d.%d",
197 BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
198 }
199 break;
200
201 default:
202 snprint(p, READSTR, "(tbdfconv)");
203 break;
204 }
205 r = fmtstrcpy(fmt, p);
206 free(p);
207
208 return r;
209 }
210
211 ulong
pcibarsize(Pcidev * p,int rno)212 pcibarsize(Pcidev *p, int rno)
213 {
214 ulong v, size;
215
216 v = pcicfgrw32(p->tbdf, rno, 0, 1);
217 pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
218 size = pcicfgrw32(p->tbdf, rno, 0, 1);
219 if(v & 1)
220 size |= 0xFFFF0000;
221 pcicfgrw32(p->tbdf, rno, v, 0);
222
223 return -(size & ~0x0F);
224 }
225
226 static int
pcilscan(int bno,Pcidev ** list)227 pcilscan(int bno, Pcidev** list)
228 {
229 Pcidev *p, *head, *tail;
230 int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
231
232 maxubn = bno;
233 head = nil;
234 tail = nil;
235 for(dno = 0; dno <= pcimaxdno; dno++){
236 maxfno = 0;
237 for(fno = 0; fno <= maxfno; fno++){
238 /*
239 * For this possible device, form the
240 * bus+device+function triplet needed to address it
241 * and try to read the vendor and device ID.
242 * If successful, allocate a device struct and
243 * start to fill it in with some useful information
244 * from the device's configuration space.
245 */
246 tbdf = MKBUS(BusPCI, bno, dno, fno);
247 l = pcicfgrw32(tbdf, PciVID, 0, 1);
248 if(l == 0xFFFFFFFF || l == 0)
249 continue;
250 p = malloc(sizeof(*p));
251 if(p == nil)
252 panic("pcilscan: no memory");
253 p->tbdf = tbdf;
254 p->vid = l;
255 p->did = l>>16;
256
257 if(pcilist != nil)
258 pcitail->list = p;
259 else
260 pcilist = p;
261 pcitail = p;
262
263 p->pcr = pcicfgr16(p, PciPCR);
264 p->rid = pcicfgr8(p, PciRID);
265 p->ccrp = pcicfgr8(p, PciCCRp);
266 p->ccru = pcicfgr8(p, PciCCRu);
267 p->ccrb = pcicfgr8(p, PciCCRb);
268 p->cls = pcicfgr8(p, PciCLS);
269 p->ltr = pcicfgr8(p, PciLTR);
270
271 p->intl = pcicfgr8(p, PciINTL);
272
273 /*
274 * If the device is a multi-function device adjust the
275 * loop count so all possible functions are checked.
276 */
277 hdt = pcicfgr8(p, PciHDT);
278 if(hdt & 0x80)
279 maxfno = MaxFNO;
280
281 /*
282 * If appropriate, read the base address registers
283 * and work out the sizes.
284 */
285 switch(p->ccrb) {
286 case 0x03: /* display controller */
287 /* fall through */
288 case 0x01: /* mass storage controller */
289 case 0x02: /* network controller */
290 case 0x04: /* multimedia device */
291 case 0x07: /* simple comm. controllers */
292 case 0x08: /* base system peripherals */
293 case 0x09: /* input devices */
294 case 0x0A: /* docking stations */
295 case 0x0B: /* processors */
296 case 0x0C: /* serial bus controllers */
297 if((hdt & 0x7F) != 0)
298 break;
299 rno = PciBAR0 - 4;
300 for(i = 0; i < nelem(p->mem); i++) {
301 rno += 4;
302 p->mem[i].bar = pcicfgr32(p, rno);
303 p->mem[i].size = pcibarsize(p, rno);
304 }
305 break;
306
307 case 0x00:
308 case 0x05: /* memory controller */
309 case 0x06: /* bridge device */
310 default:
311 break;
312 }
313
314 if(head != nil)
315 tail->link = p;
316 else
317 head = p;
318 tail = p;
319 }
320 }
321
322 *list = head;
323 for(p = head; p != nil; p = p->link){
324 /*
325 * Find PCI-PCI bridges and recursively descend the tree.
326 */
327 if(p->ccrb != 0x06 || p->ccru != 0x04)
328 continue;
329
330 /*
331 * If the secondary or subordinate bus number is not
332 * initialised try to do what the PCI BIOS should have
333 * done and fill in the numbers as the tree is descended.
334 * On the way down the subordinate bus number is set to
335 * the maximum as it's not known how many buses are behind
336 * this one; the final value is set on the way back up.
337 */
338 sbn = pcicfgr8(p, PciSBN);
339 ubn = pcicfgr8(p, PciUBN);
340
341 if(sbn == 0 || ubn == 0) {
342 sbn = maxubn+1;
343 /*
344 * Make sure memory, I/O and master enables are
345 * off, set the primary, secondary and subordinate
346 * bus numbers and clear the secondary status before
347 * attempting to scan the secondary bus.
348 *
349 * Initialisation of the bridge should be done here.
350 */
351 pcicfgw32(p, PciPCR, 0xFFFF0000);
352 l = (MaxUBN<<16)|(sbn<<8)|bno;
353 pcicfgw32(p, PciPBN, l);
354 pcicfgw16(p, PciSPSR, 0xFFFF);
355 maxubn = pcilscan(sbn, &p->bridge);
356 l = (maxubn<<16)|(sbn<<8)|bno;
357
358 pcicfgw32(p, PciPBN, l);
359 }
360 else {
361 if(ubn > maxubn)
362 maxubn = ubn;
363 pcilscan(sbn, &p->bridge);
364 }
365 }
366
367 return maxubn;
368 }
369
370 extern void rtl8169interrupt(Ureg*, void* arg);
371
372 /* not used yet */
373 static void
pciintr(Ureg * ureg,void * p)374 pciintr(Ureg *ureg, void *p)
375 {
376 rtl8169interrupt(ureg, p); /* HACK */
377 }
378
379 static void
pcicfginit(void)380 pcicfginit(void)
381 {
382 char *p;
383 Pci *pci = (Pci *)soc.pci;
384 Pcidev **list;
385 int bno, n;
386
387 lock(&pcicfginitlock);
388 if(pcicfgmode != -1) {
389 unlock(&pcicfginitlock);
390 return;
391 }
392
393 /*
394 * TrimSlice # pci 0 1
395 * Scanning PCI devices on bus 0 1
396 * BusDevFun VendorId DeviceId Device Class Sub-Class
397 * _____________________________________________________________
398 * 00.00.00 0x10de 0x0bf0 Bridge device 0x04
399 * 01.00.00 0x10ec 0x8168 Network controller 0x00
400 *
401 * thus pci bus 0 has a bridge with, perhaps, an ide/sata ctlr behind,
402 * and pci bus 1 has the realtek 8169 on it:
403 *
404 * TrimSlice # pci 1 long
405 * Scanning PCI devices on bus 1
406 *
407 * Found PCI device 01.00.00:
408 * vendor ID = 0x10ec
409 * device ID = 0x8168
410 * command register = 0x0007
411 * status register = 0x0010
412 * revision ID = 0x03
413 * class code = 0x02 (Network controller)
414 * sub class code = 0x00
415 * programming interface = 0x00
416 * cache line = 0x08
417 * base address 0 = 0x80400001 config
418 * base address 1 = 0x00000000 (ext. config)
419 * base address 2 = 0xa000000c "downstream"
420 * base address 3 = 0x00000000 (prefetchable)
421 * base address 4 = 0xa000400c not "
422 * base address 5 = 0x00000000 (unused)
423 */
424 n = pci->id >> 16;
425 if (((pci->id & MASK(16)) != Vnvidia || (n != 0xbf0 && n != 0xbf1)) &&
426 (pci->id & MASK(16)) != Vrealtek) {
427 print("no pci controller at %#p\n", pci);
428 unlock(&pcicfginitlock);
429 return;
430 }
431 if (0)
432 iprint("pci: %#p: nvidia, rev %#ux class %#6.6lux misc %#8.8lux\n",
433 pci, (uchar)pci->revclass, pci->revclass >> 8,
434 pci->misc);
435
436 pci->cs &= Iospace;
437 pci->cs |= Memspace | Busmaster;
438 coherence();
439
440 pcicfgmode = 1;
441 // pcimaxdno = 31;
442 pcimaxdno = 15; /* for trimslice */
443
444 fmtinstall('T', tbdffmt);
445
446 if(p = getconf("*pcimaxbno")){
447 n = strtoul(p, 0, 0);
448 if(n < pcimaxbno)
449 pcimaxbno = n;
450 }
451 if(p = getconf("*pcimaxdno")){
452 n = strtoul(p, 0, 0);
453 if(n < pcimaxdno)
454 pcimaxdno = n;
455 }
456
457 list = &pciroot;
458 /* was bno = 0; trimslice needs to start at 1 */
459 for(bno = 1; bno <= pcimaxbno; bno++) {
460 bno = pcilscan(bno, list);
461 while(*list)
462 list = &(*list)->link;
463 }
464 unlock(&pcicfginitlock);
465
466 if(getconf("*pcihinv"))
467 pcihinv(nil);
468 }
469
470 enum {
471 Afiintrcode = 0xb8,
472 };
473
474 void
pcieintrdone(void)475 pcieintrdone(void) /* dismiss pci-e intr */
476 {
477 ulong *afi;
478
479 afi = (ulong *)(soc.pci + Afi);
480 afi[Afiintrcode/sizeof *afi] = 0; /* magic */
481 coherence();
482 }
483
484 /*
485 * whole config space for tbdf should be at (return address - rno).
486 */
487 static void *
tegracfgaddr(int tbdf,int rno)488 tegracfgaddr(int tbdf, int rno)
489 {
490 uintptr addr;
491
492 addr = soc.pci + (rno < 256? Cfgspace: Ecfgspace) + BUSBDF(tbdf) + rno;
493 // if (BUSBNO(tbdf) == 1)
494 // addr += Port1;
495 return (void *)addr;
496 }
497
498 static int
pcicfgrw8(int tbdf,int rno,int data,int read)499 pcicfgrw8(int tbdf, int rno, int data, int read)
500 {
501 int x;
502 void *addr;
503
504 if(pcicfgmode == -1)
505 pcicfginit();
506
507 x = -1;
508 if(BUSDNO(tbdf) > pcimaxdno)
509 return x;
510
511 addr = tegracfgaddr(tbdf, rno);
512
513 lock(&pcicfglock);
514 if(read)
515 x = *(uchar *)addr;
516 else
517 *(uchar *)addr = data;
518 unlock(&pcicfglock);
519
520 return x;
521 }
522
523 int
pcicfgr8(Pcidev * pcidev,int rno)524 pcicfgr8(Pcidev* pcidev, int rno)
525 {
526 return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
527 }
528
529 void
pcicfgw8(Pcidev * pcidev,int rno,int data)530 pcicfgw8(Pcidev* pcidev, int rno, int data)
531 {
532 pcicfgrw8(pcidev->tbdf, rno, data, 0);
533 }
534
535 static int
pcicfgrw16(int tbdf,int rno,int data,int read)536 pcicfgrw16(int tbdf, int rno, int data, int read)
537 {
538 int x;
539 void *addr;
540
541 if(pcicfgmode == -1)
542 pcicfginit();
543
544 x = -1;
545 if(BUSDNO(tbdf) > pcimaxdno)
546 return x;
547
548 addr = tegracfgaddr(tbdf, rno);
549
550 lock(&pcicfglock);
551 if(read)
552 x = *(ushort *)addr;
553 else
554 *(ushort *)addr = data;
555 unlock(&pcicfglock);
556
557 return x;
558 }
559
560 int
pcicfgr16(Pcidev * pcidev,int rno)561 pcicfgr16(Pcidev* pcidev, int rno)
562 {
563 return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
564 }
565
566 void
pcicfgw16(Pcidev * pcidev,int rno,int data)567 pcicfgw16(Pcidev* pcidev, int rno, int data)
568 {
569 pcicfgrw16(pcidev->tbdf, rno, data, 0);
570 }
571
572 static int
pcicfgrw32(int tbdf,int rno,int data,int read)573 pcicfgrw32(int tbdf, int rno, int data, int read)
574 {
575 int x;
576 vlong v;
577 void *addr;
578
579 if(pcicfgmode == -1)
580 pcicfginit();
581
582 x = -1;
583 if(BUSDNO(tbdf) > pcimaxdno)
584 return x;
585
586 addr = tegracfgaddr(tbdf, rno);
587 v = probeaddr((uintptr)addr);
588 if (v < 0)
589 return -1;
590
591 lock(&pcicfglock);
592 if(read)
593 x = *(ulong *)addr;
594 else
595 *(ulong *)addr = data;
596 unlock(&pcicfglock);
597
598 return x;
599 }
600
601 int
pcicfgr32(Pcidev * pcidev,int rno)602 pcicfgr32(Pcidev* pcidev, int rno)
603 {
604 return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
605 }
606
607 void
pcicfgw32(Pcidev * pcidev,int rno,int data)608 pcicfgw32(Pcidev* pcidev, int rno, int data)
609 {
610 pcicfgrw32(pcidev->tbdf, rno, data, 0);
611 }
612
613 Pcidev*
pcimatch(Pcidev * prev,int vid,int did)614 pcimatch(Pcidev* prev, int vid, int did)
615 {
616 if(pcicfgmode == -1)
617 pcicfginit();
618
619 if(prev == nil)
620 prev = pcilist;
621 else
622 prev = prev->list;
623
624 while(prev != nil){
625 if((vid == 0 || prev->vid == vid)
626 && (did == 0 || prev->did == did))
627 break;
628 prev = prev->list;
629 }
630 return prev;
631 }
632
633 Pcidev*
pcimatchtbdf(int tbdf)634 pcimatchtbdf(int tbdf)
635 {
636 Pcidev *pcidev;
637
638 if(pcicfgmode == -1)
639 pcicfginit();
640
641 for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
642 if(pcidev->tbdf == tbdf)
643 break;
644 }
645 return pcidev;
646 }
647
648 static void
pcilhinv(Pcidev * p)649 pcilhinv(Pcidev* p)
650 {
651 int i;
652 Pcidev *t;
653
654 if(p == nil) {
655 putstrn(PCICONS.output, PCICONS.ptr);
656 p = pciroot;
657 print("bus dev type vid did intl memory\n");
658 }
659 for(t = p; t != nil; t = t->link) {
660 print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
661 BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
662 t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
663
664 for(i = 0; i < nelem(p->mem); i++) {
665 if(t->mem[i].size == 0)
666 continue;
667 print("%d:%.8lux %d ", i,
668 t->mem[i].bar, t->mem[i].size);
669 }
670 if(t->bridge)
671 print("->%d", BUSBNO(t->bridge->tbdf));
672 print("\n");
673 }
674 while(p != nil) {
675 if(p->bridge != nil)
676 pcilhinv(p->bridge);
677 p = p->link;
678 }
679 }
680
681 void
pcihinv(Pcidev * p)682 pcihinv(Pcidev* p)
683 {
684 if(pcicfgmode == -1)
685 pcicfginit();
686 lock(&pcicfginitlock);
687 pcilhinv(p);
688 unlock(&pcicfginitlock);
689 }
690
691 void
pcireset(void)692 pcireset(void)
693 {
694 Pcidev *p;
695
696 if(pcicfgmode == -1)
697 pcicfginit();
698
699 for(p = pcilist; p != nil; p = p->list) {
700 /* don't mess with the bridges */
701 if(p->ccrb == 0x06)
702 continue;
703 pciclrbme(p);
704 }
705 }
706
707 void
pcisetioe(Pcidev * p)708 pcisetioe(Pcidev* p)
709 {
710 p->pcr |= IOen;
711 pcicfgw16(p, PciPCR, p->pcr);
712 }
713
714 void
pciclrioe(Pcidev * p)715 pciclrioe(Pcidev* p)
716 {
717 p->pcr &= ~IOen;
718 pcicfgw16(p, PciPCR, p->pcr);
719 }
720
721 void
pcisetbme(Pcidev * p)722 pcisetbme(Pcidev* p)
723 {
724 p->pcr |= MASen;
725 pcicfgw16(p, PciPCR, p->pcr);
726 }
727
728 void
pciclrbme(Pcidev * p)729 pciclrbme(Pcidev* p)
730 {
731 p->pcr &= ~MASen;
732 pcicfgw16(p, PciPCR, p->pcr);
733 }
734
735 void
pcisetmwi(Pcidev * p)736 pcisetmwi(Pcidev* p)
737 {
738 p->pcr |= MemWrInv;
739 pcicfgw16(p, PciPCR, p->pcr);
740 }
741
742 void
pciclrmwi(Pcidev * p)743 pciclrmwi(Pcidev* p)
744 {
745 p->pcr &= ~MemWrInv;
746 pcicfgw16(p, PciPCR, p->pcr);
747 }
748
749 static int
pcigetpmrb(Pcidev * p)750 pcigetpmrb(Pcidev* p)
751 {
752 int ptr;
753
754 if(p->pmrb != 0)
755 return p->pmrb;
756 p->pmrb = -1;
757
758 /*
759 * If there are no extended capabilities implemented,
760 * (bit 4 in the status register) assume there's no standard
761 * power management method.
762 * Find the capabilities pointer based on PCI header type.
763 */
764 if(!(pcicfgr16(p, PciPSR) & 0x0010))
765 return -1;
766 switch(pcicfgr8(p, PciHDT)){
767 default:
768 return -1;
769 case 0: /* all other */
770 case 1: /* PCI to PCI bridge */
771 ptr = 0x34;
772 break;
773 case 2: /* CardBus bridge */
774 ptr = 0x14;
775 break;
776 }
777 ptr = pcicfgr32(p, ptr);
778
779 while(ptr != 0){
780 /*
781 * Check for validity.
782 * Can't be in standard header and must be double
783 * word aligned.
784 */
785 if(ptr < 0x40 || (ptr & ~0xFC))
786 return -1;
787 if(pcicfgr8(p, ptr) == 0x01){
788 p->pmrb = ptr;
789 return ptr;
790 }
791
792 ptr = pcicfgr8(p, ptr+1);
793 }
794
795 return -1;
796 }
797
798 int
pcigetpms(Pcidev * p)799 pcigetpms(Pcidev* p)
800 {
801 int pmcsr, ptr;
802
803 if((ptr = pcigetpmrb(p)) == -1)
804 return -1;
805
806 /*
807 * Power Management Register Block:
808 * offset 0: Capability ID
809 * 1: next item pointer
810 * 2: capabilities
811 * 4: control/status
812 * 6: bridge support extensions
813 * 7: data
814 */
815 pmcsr = pcicfgr16(p, ptr+4);
816
817 return pmcsr & 0x0003;
818 }
819
820 int
pcisetpms(Pcidev * p,int state)821 pcisetpms(Pcidev* p, int state)
822 {
823 int ostate, pmc, pmcsr, ptr;
824
825 if((ptr = pcigetpmrb(p)) == -1)
826 return -1;
827
828 pmc = pcicfgr16(p, ptr+2);
829 pmcsr = pcicfgr16(p, ptr+4);
830 ostate = pmcsr & 0x0003;
831 pmcsr &= ~0x0003;
832
833 switch(state){
834 default:
835 return -1;
836 case 0:
837 break;
838 case 1:
839 if(!(pmc & 0x0200))
840 return -1;
841 break;
842 case 2:
843 if(!(pmc & 0x0400))
844 return -1;
845 break;
846 case 3:
847 break;
848 }
849 pmcsr |= state;
850 pcicfgw16(p, ptr+4, pmcsr);
851
852 return ostate;
853 }
854