int pcisetpms(Pcidev* p, int state) { int pmc, pmcsr, ptr; if((ptr = pcicap(p, PciCapPMG)) == -1) return -1; pmc = pcicfgr16(p, ptr+Pmgcap); pmcsr = pcicfgr16(p, ptr+Pmgctl); switch(state){ default: return -1; case 0: break; case 1: if(!(pmc & 0x0200)) return -1; break; case 2: if(!(pmc & 0x0400)) return -1; break; case 3: break; } pcicfgw16(p, ptr+4, (pmcsr & ~3) | state); return pmcsr & 3; }
static uint16_t cfgget16(uintptr_t p, void* r) { Reg *ro = r; Pcidev d; d.tbdf = ro->tbdf; return pcicfgr16(&d, p); }
int pcigetpms(Pcidev* p) { int ptr; if((ptr = pcicap(p, PciCapPMG)) == -1) return -1; return pcicfgr16(p, ptr+Pmgctl) & 0x0003; }
static Vdev* viopnpdevs(int typ) { Vdev *vd, *h, *t; Pcidev *p; int n, i; h = t = nil; for(p = nil; p = pcimatch(p, 0, 0);){ if(p->vid != 0x1AF4) continue; if((p->did < 0x1000) || (p->did >= 0x1040)) continue; if(p->rid != 0) continue; if(pcicfgr16(p, 0x2E) != typ) continue; if((vd = malloc(sizeof(*vd))) == nil){ print("virtio: no memory for Vdev\n"); break; } vd->port = p->mem[0].bar & ~0x1; if(ioalloc(vd->port, p->mem[0].size, 0, "virtio") < 0){ print("virtio: port %lux in use\n", vd->port); free(vd); continue; } vd->typ = typ; vd->pci = p; /* reset */ outb(vd->port+Status, 0); vd->feat = inl(vd->port+Devfeat); outb(vd->port+Status, Acknowledge|Driver); for(i=0; i<nelem(vd->queue); i++){ outs(vd->port+Qselect, i); n = ins(vd->port+Qsize); if(n == 0 || (n & (n-1)) != 0) break; if((vd->queue[i] = mkvqueue(n)) == nil) break; coherence(); outl(vd->port+Qaddr, PADDR(vd->queue[i]->desc)/BY2PG); } vd->nqueue = i; if(h == nil) h = vd; else t->next = vd; t = vd; } return h; }
/* figure out what kind of interface we could have */ void lm78reset(void) { int pcs; Pcidev *p; lm78.ifc = None; p = nil; while((p = pcimatch(p, IntelVendID, 0)) != nil){ switch(p->did){ /* these bridges use the PCSC to map the lm78 into port space. */ /* for this case the lm78's CS# select is connected to the PIIX's */ /* PCS# output and the bottom 3 bits of address are passed to the */ /* LM78's A0-A2 inputs. */ case PiixID: case Piix3ID: pcs = pcicfgr16(p, PCSC); if(pcs & 3) { /* already enabled */ lm78.port = pcs & ~3; lm78.ifc = Parallel; return; } /* enable the chip, use default address 0x50 */ pcicfgw16(p, PCSC, 0x50|PCSC8bytes); pcs = pcicfgr16(p, PCSC); lm78.port = pcs & ~3; lm78.ifc = Parallel; return; /* this bridge puts the lm78's serial interface on the smbus */ case Piix4PMID: lm78.smbus = piix4smbus(); if(lm78.smbus == nil) continue; print("found piix4 smbus, base %lud\n", lm78.smbus->base); lm78.ifc = Smbus; return; } } }
int pcicapoff(Pcidev *p) { int off; /* status register bit 4 has capabilities */ if((pcicfgr16(p, PciPSR) & 1<<4) == 0) return -1; switch(pcicfgr8(p, PciHDT) & 0x7f){ default: return -1; case 0: /* etc */ case 1: /* pci to pci bridge */ off = 0x34; break; case 2: /* cardbus bridge */ off = 0x14; break; } return off; }
static void pcicfginit(void) { int sbno, bno, n; Pcidev **list, *p; if(pcicfgmode != -1) return; lock(&pcicfginitlock); if(pcicfgmode != -1){ unlock(&pcicfginitlock); return; } fmtinstall('T', tbdffmt); /* * Try to determine if PCI Mode1 configuration implemented. * (Bits [30:24] of PciADDR must be 0, according to the spec.) * Mode2 won't appear in 64-bit machines. */ n = inl(PciADDR); if(!(n & 0x7F000000)){ outl(PciADDR, 0x80000000); outb(PciADDR+3, 0); if(inl(PciADDR) & 0x80000000) pcicfgmode = 1; } outl(PciADDR, n); if(pcicfgmode < 0){ unlock(&pcicfginitlock); return; } list = &pciroot; for(bno = 0; bno <= Maxbus; bno++) { sbno = bno; bno = pcilscan(bno, "0.0.0", list); while(*list) list = &(*list)->link; if(sbno != 0) continue; /* * If we have found a PCI-to-Cardbus bridge, make sure * it has no valid mappings anymore. */ for(p = pciroot; p != nil; p = p->link){ if (p->ccrb == 6 && p->ccru == 7) { /* reset the cardbus */ pcicfgw16(p, PciBCR, 0x40 | pcicfgr16(p, PciBCR)); delay(50); } } } pcireservemem(); unlock(&pcicfginitlock); // Bring the virtio devices live. virtiosetup(); //if(getconf("*pcihinv")) pcihinv(nil); }
static int pcilscan(int bno, char *path, Pcidev** list) { Pcidev *p, *head, *tail; int dno, fno, i, hdt, l, maxfno, maxubn, sbn, tbdf, ubn, capoff; maxubn = bno; head = nil; tail = nil; for(dno = 0; dno <= Maxdev; dno++){ maxfno = 0; for(fno = 0; fno <= maxfno; fno++){ /* * For this possible device, form the * bus+device+function triplet needed to address it * and try to read the vendor and device ID. * If successful, allocate a device struct and * start to fill it in with some useful information * from the device's configuration space. */ tbdf = MKBUS(BusPCI, bno, dno, fno); l = pcicfgrw(tbdf, PciVID, 0, Read, 4); if(l == 0xFFFFFFFF || l == 0) continue; p = malloc(sizeof(*p)); p->caplist = nil; p->capidx = nil; p->capcnt = 0; p->tbdf = tbdf; p->vid = l; p->did = l>>16; p->path = path; if(pcilist != nil) pcitail->list = p; else pcilist = p; pcitail = p; p->pcr = pcicfgr16(p, PciPCR); p->rid = pcicfgr8(p, PciRID); p->ccrp = pcicfgr8(p, PciCCRp); p->ccru = pcicfgr8(p, PciCCRu); p->ccrb = pcicfgr8(p, PciCCRb); p->cls = pcicfgr8(p, PciCLS); p->ltr = pcicfgr8(p, PciLTR); p->intl = pcicfgr8(p, PciINTL); p->intp = pcicfgr8(p, PciINTP); /* * If the device is a multi-function device adjust the * loop count so all possible functions are checked. */ hdt = pcicfgr8(p, PciHDT); if(hdt & 0x80) maxfno = Maxfn; /* * If appropriate, read the base address registers * and work out the sizes. */ switch(p->ccrb) { default: if((hdt & 0x7F) != 0) break; for(i = 0; i < nelem(p->mem); i++) { p->mem[i].bar = pcicfgr32(p, PciBAR0+4*i); p->mem[i].size = pcibarsize(p, PciBAR0+4*i); } break; /* * Some virtio-pci devices have ccrb == 0x00, their BARs and * sizes also should be picked here. */ case 0x05: /* memory controller */ case 0x06: /* bridge device */ break; } /* * Try to gather PCI capabilities. If the offset of capabilities * in the config area cannot be found, skip this step. For simplicity, * capabilities will be linked in a LIFO so we don't deal with list * heads and tails. */ capoff = pcicapoff(p); int off = capoff; while(capoff != -1) { off = pcicfgr8(p, off); if((off < 0x40) || (off & 3)) break; off &= ~3; Pcicap *c = malloc(sizeof(*c)); c->dev = p; c->link = p->caplist; p->caplist = c; p->capcnt++; c->vndr = pcicfgr8(p, off + PciCapVndr); c->caplen = pcicfgr8(p, off + PciCapLen); c->type = pcicfgr8(p, off + PciCapType); c->bar = pcicfgr8(p, off + PciCapBar); c->offset = pcicfgr32(p, off + PciCapOff); c->length = pcicfgr32(p, off + PciCapLength); off++; } if(p->capcnt > 0) { p->capidx = malloc(p->capcnt * sizeof(Pcicap *)); Pcicap *pcp = p->caplist; for(int pix = 0; ; pix++) { p->capidx[pix] = pcp; pcp = pcp->link; if(pcp == nil) break; } } if(head != nil) tail->link = p; else head = p; tail = p; } } *list = head; for(p = head; p != nil; p = p->link) { /* * Find PCI-PCI bridges and recursively descend the tree. */ if(p->ccrb != 0x06 || p->ccru != 0x04) continue; /* * If the secondary or subordinate bus number is not * initialised try to do what the PCI BIOS should have * done and fill in the numbers as the tree is descended. * On the way down the subordinate bus number is set to * the maximum as it's not known how many buses are behind * this one; the final value is set on the way back up. */ sbn = pcicfgr8(p, PciSBN); ubn = pcicfgr8(p, PciUBN); if(sbn == 0 || ubn == 0) { print("%T: unconfigured bridge\n", p->tbdf); sbn = maxubn+1; /* * Make sure memory, I/O and master enables are * off, set the primary, secondary and subordinate * bus numbers and clear the secondary status before * attempting to scan the secondary bus. * * Initialisation of the bridge should be done here. */ pcicfgw32(p, PciPCR, 0xFFFF0000); pcicfgw32(p, PciPBN, Maxbus<<16 | sbn<<8 | bno); pcicfgw16(p, PciSPSR, 0xFFFF); char *bus = mallocz(256, 1); snprint(bus, 256, "%s/%d.%d.0", path, BUSBNO(p->tbdf), BUSDNO(p->tbdf)); maxubn = pcilscan(sbn, bus, &p->bridge); pcicfgw32(p, PciPBN, maxubn<<16 | sbn<<8 | bno); } else { /* * You can't go back. * This shouldn't be possible, but the * Iwill DK8-HTX seems to have subordinate * bus numbers which get smaller on the * way down. Need to look more closely at * this. */ if(ubn > maxubn) { maxubn = ubn; } char *bus = mallocz(256, 1); snprint(bus, 256, "%s/%d.%d.0", path, BUSBNO(p->tbdf), BUSDNO(p->tbdf)); pcilscan(sbn, bus, &p->bridge); } } return maxubn; }
static Uart* uartpcipnp(void) { Pcidev *p; char *name; int ctlrno, n, subid; Uart *head, *tail, *uart; /* * Loop through all PCI devices looking for simple serial * controllers (ccrb == 0x07) and configure the ones which * are familiar. All suitable devices are configured to * simply point to the generic i8250 driver. */ head = tail = nil; ctlrno = 0; for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){ if(p->ccrb != 0x07 || p->ccru > 2) continue; switch((p->did<<16)|p->vid){ default: continue; case (0x9835<<16)|0x9710: /* StarTech PCI2S550 */ uart = uartpci(ctlrno, p, 0, 1, 1843200, "PCI2S550-0"); if(uart == nil) continue; uart->next = uartpci(ctlrno, p, 1, 1, 1843200, "PCI2S550-1"); break; case (0x950A<<16)|0x1415: /* Oxford Semi OX16PCI954 */ /* * These are common devices used by 3rd-party * manufacturers. * Must check the subsystem VID and DID for correct * match, mostly to get the clock frequency right. */ subid = pcicfgr16(p, PciSVID); subid |= pcicfgr16(p, PciSID)<<16; switch(subid){ default: continue; case (0x2000<<16)|0x131F:/* SIIG CyberSerial PCIe */ uart = uartpci(ctlrno, p, 0, 1, 18432000, "CyberSerial-1S"); if(uart == nil) continue; break; } break; case (0x9501<<16)|0x1415: /* Oxford Semi OX16PCI954 */ /* * These are common devices used by 3rd-party * manufacturers. * Should check the subsystem VID and DID for correct * match, mostly to get the clock frequency right. */ subid = pcicfgr16(p, PciSVID); subid |= pcicfgr16(p, PciSID)<<16; switch(subid){ default: continue; case (0<<16)|0x1415: /* StarTech PCI4S550 */ uart = uartpci(ctlrno, p, 0, 1, 18432000, "PCI4S550-0"); if(uart == nil) continue; break; } break; case (0x9050<<16)|0x10B5: /* Perle PCI-Fast4 series */ case (0x9030<<16)|0x10B5: /* Perle Ultraport series */ /* * These devices consists of a PLX bridge (the above * PCI VID+DID) behind which are some 16C654 UARTs. * Must check the subsystem VID and DID for correct * match. */ subid = pcicfgr16(p, PciSVID); subid |= pcicfgr16(p, PciSID)<<16; switch(subid){ default: continue; case (0x0011<<16)|0x12E0: /* Perle PCI-Fast16 */ n = 16; name = "PCI-Fast16"; break; case (0x0021<<16)|0x12E0: /* Perle PCI-Fast8 */ n = 8; name = "PCI-Fast8"; break; case (0x0031<<16)|0x12E0: /* Perle PCI-Fast4 */ n = 4; name = "PCI-Fast4"; break; case (0x0021<<16)|0x155F: /* Perle Ultraport8 */ n = 8; name = "Ultraport8"; /* 16C754 UARTs */ break; } uart = uartpci(ctlrno, p, 2, n, 7372800, name); if(uart == nil) continue; break; } if(head != nil) tail->next = uart; else head = uart; for(tail = uart; tail->next != nil; tail = tail->next) ; ctlrno++; } return head; }