static uint8_t cfgget8(uintptr_t p, void* r) { Reg *ro = r; Pcidev d; d.tbdf = ro->tbdf; return pcicfgr8(&d, p); }
int pcicap(Pcidev *p, int cap) { int i, c, off; off = pcicapoff(p); if(off == -1) return -1; for(i = 48; i--;){ off = pcicfgr8(p, off); if(off < 0x40 || (off & 3)) break; off &= ~3; c = pcicfgr8(p, off); if(c == 0xff) break; if(c == cap) return off; off++; } return -1; }
static char* powermode(Ctlr *ctlr) { uchar c[Tcmdsize]; int capoff, reg; memset(c, 0, sizeof(c)); capoff = pcicap(ctlr->pdev, PciCapPCIe); if(capoff >= 0){ reg = pcicfgr8(ctlr->pdev, capoff+1); if((reg & 1) == 0) /* PCI_PCIE_LCR_ASPM_L0S */ c[0] |= 1<<3; /* WPI_PS_PCI_PMGT */ } return cmd(ctlr, 119, c, 4*(3+5)); }
static void wpipci(void) { Pcidev *pdev; pdev = nil; while(pdev = pcimatch(pdev, 0x8086, 0)){ Ctlr *ctlr; void *mem; switch(pdev->did){ default: continue; case 0x4227: break; } /* Clear device-specific "PCI retry timeout" register (41h). */ if(pcicfgr8(pdev, 0x41) != 0) pcicfgw8(pdev, 0x41, 0); pcisetbme(pdev); pcisetpms(pdev, 0); ctlr = malloc(sizeof(Ctlr)); if(ctlr == nil) { print("wpi: unable to alloc Ctlr\n"); continue; } ctlr->port = pdev->mem[0].bar & ~0x0F; mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size); if(mem == nil) { print("wpi: can't map %8.8luX\n", pdev->mem[0].bar); free(ctlr); continue; } ctlr->nic = mem; ctlr->pdev = pdev; if(wpihead != nil) wpitail->link = ctlr; else wpihead = ctlr; wpitail = ctlr; } }
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 int mpintrenablex(Vctl* v, int tbdf) { Bus *bus; Aintr *aintr; Apic *apic; Pcidev *pcidev; int bno, dno, hi, irq, lo, n, type, vno; char *typenm; /* * Find the bus. */ type = BUSTYPE(tbdf); bno = BUSBNO(tbdf); dno = BUSDNO(tbdf); if(type == BusISA) bno = mpisabus; vno = -1; for(bus = mpbus; bus != nil; bus = bus->next){ if(bus->type != type) continue; if(bus->busno == bno) break; } if(bus == nil){ typenm = type < 0 || type >= nelem(buses)? "": buses[type]; print("mpintrenablex: can't find bus type %d (%s) for irq %d " "%s busno %d\n", type, typenm, v->irq, v->name, bno); return -1; } /* * For PCI devices the interrupt pin (INT[ABCD]) and device * number are encoded into the entry irq field, so create something * to match on. The interrupt pin used by the device has to be * obtained from the PCI config space. */ if(bus->type == BusPCI){ pcidev = pcimatchtbdf(tbdf); if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0) irq = (dno<<2)|(n-1); else irq = -1; //print("pcidev %#uX: irq %#uX v->irq %#uX\n", tbdf, irq, v->irq); } else irq = v->irq; /* * Find a matching interrupt entry from the list of interrupts * attached to this bus. */ for(aintr = bus->aintr; aintr; aintr = aintr->next){ if(aintr->intr->irq != irq) continue; if (0) { PCMPintr* p = aintr->intr; print("mpintrenablex: bus %d intin %d irq %d\n", p->busno, p->intin, p->irq); } /* * Check if already enabled. Multifunction devices may share * INT[A-D]# so, if already enabled, check the polarity matches * and the trigger is level. * * Should check the devices differ only in the function number, * but that can wait for the planned enable/disable rewrite. * The RDT read here is safe for now as currently interrupts * are never disabled once enabled. */ apic = aintr->apic; ioapicrdtr(apic, aintr->intr->intin, 0, &lo); if(!(lo & ApicIMASK)){ vno = lo & 0xFF; //print("%s vector %d (!imask)\n", v->name, vno); n = mpintrinit(bus, aintr->intr, vno, v->irq); n |= ApicPHYSICAL; /* no-op */ lo &= ~(ApicRemoteIRR|ApicDELIVS); if(n != lo || !(n & ApicLEVEL)){ print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n", v->irq, tbdf, lo, n); return -1; } break; } /* * With the APIC a unique vector can be assigned to each * request to enable an interrupt. There are two reasons this * is a good idea: * 1) to prevent lost interrupts, no more than 2 interrupts * should be assigned per block of 16 vectors (there is an * in-service entry and a holding entry for each priority * level and there is one priority level per block of 16 * interrupts). * 2) each input pin on the IOAPIC will receive a different * vector regardless of whether the devices on that pin use * the same IRQ as devices on another pin. */ vno = VectorAPIC + (incref(&mpvnoref)-1)*8; //print("%s vector %d (imask)\n", v->name, vno); if(vno > MaxVectorAPIC){ print("mpintrenable: vno %d, irq %d, tbdf %uX\n", vno, v->irq, tbdf); return -1; } hi = mpintrcpu()<<24; lo = mpintrinit(bus, aintr->intr, vno, v->irq); //print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n", // lo, bus->busno, aintr->intr->irq, vno, // v->irq, i8259elcr); if(lo & ApicIMASK) return -1; lo |= ApicPHYSICAL; /* no-op */ if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC) ioapicrdtw(apic, aintr->intr->intin, hi, lo); //else // print("lo not enabled 0x%uX %d\n", // apic->flags, apic->type); break; } if (aintr) { v->isr = lapicisr; v->eoi = lapiceoi; } return vno; }
static void i82563pci(void) { int port, type, cls; Pcidev *p; Ctlr *ctlr; static int first = 1; if (first) first = 0; else return; p = nil; while(p = pcimatch(p, 0x8086, 0)){ if(p->ccrb != 0x02 || p->ccru != 0) continue; //print("i82563pci: did %4.4#x\n", p->did); switch(p->did){ case 0x1096: case 0x10ba: type = i82563; break; case 0x1049: /* mm */ case 0x104a: /* dm */ case 0x104d: /* v */ case 0x10bd: /* dm */ type = i82566; break; case 0x10cd: /* lf */ type = i82567; break; case 0x10a4: case 0x105e: type = i82571; break; case 0x10b9: /* sic, 82572 */ type = i82572; break; case 0x108b: /* e */ case 0x108c: /* e (iamt) */ case 0x109a: /* l */ type = i82573; break; case 0x10a7: /* 82575eb */ type = i82575; break; default: continue; } port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); if(port == 0){ print("%s: can't map %d @ 0x%8.8lux\n", tname[type], p->mem[0].size, p->mem[0].bar); continue; } if(p->pcr & MemWrInv){ cls = pcicfgr8(p, PciCLS) * 4; if(cls != CACHELINESZ) pcicfgw8(p, PciCLS, CACHELINESZ/4); } cls = pcicfgr8(p, PciCLS); switch(cls){ default: print("%s: unexpected CLS - %d bytes\n", tname[type], cls*sizeof(long)); break; case 0x00: case 0xFF: /* alphapc 164lx returns 0 */ print("%s: unusable PciCLS: %d, using %d longs\n", tname[type], cls, CACHELINESZ/sizeof(long)); cls = CACHELINESZ/sizeof(long); pcicfgw8(p, PciCLS, cls); break; case 0x08: case 0x10: break; } ctlr = malloc(sizeof(Ctlr)); ctlr->port = port; ctlr->pcidev = p; ctlr->cls = cls*4; ctlr->type = type; ctlr->nic = KADDR(ctlr->port); if(i82563reset(ctlr)){ free(ctlr); continue; } pcisetbme(p); if(ctlrhead != nil) ctlrtail->next = ctlr; else ctlrhead = ctlr; ctlrtail = ctlr; } }
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 void pcicfginit(void) { int fd, i, j, n, bno, dno, fno; char buf[1024], *base, *s; Pcidev *p; Dir *d; trace("pcicfginit\n"); if((fd = open(base = "/dev/pci", OREAD)) < 0) if((fd = open(base = "#$/pci", OREAD)) < 0) return; n = dirreadall(fd, &d); close(fd); for(i=0; i<n; i++) { if(strstr(d[i].name, "ctl") == nil) continue; strncpy(buf, d[i].name, sizeof(buf)); bno = strtoul(buf, &s, 10); dno = strtoul(s+1, &s, 10); fno = strtoul(s+1, nil, 10); p = mallocz(sizeof(*p), 1); p->tbdf = MKBUS(BusPCI, bno, dno, fno); sprint(buf, "%s/%d.%d.%draw", base, bno, dno, fno); if((p->rawfd = open(buf, ORDWR)) < 0){ free(p); continue; } sprint(buf, "%s/%d.%d.%dctl", base, bno, dno, fno); if((fd = open(buf, OREAD)) < 0){ close(p->rawfd); free(p); continue; } if((j = read(fd, buf, sizeof(buf)-1)) <= 0){ close(p->rawfd); close(fd); free(p); continue; } buf[j] = 0; close(fd); p->ccrb = strtol(buf, nil, 16); p->ccru = strtol(buf + 3, nil, 16); p->vid = strtol(buf + 9, &s, 16); p->did = strtol(s + 1, &s, 16); p->intl = strtol(s + 1, &s, 10); p->rid = pcicfgr8(p, PciRID); trace("%d.%d.%d: did=%X vid=%X rid=%X intl=%d ccru=%X\n", bno, dno, fno, p->did, p->vid, p->rid, p->intl, p->ccru); while(*s == ' '){ j = strtol(s+1, &s, 10); if(j < 0 || j >= nelem(p->mem)) break; p->mem[j].bar = strtoul(s+1, &s, 16); p->mem[j].size = strtoul(s+1, &s, 10); trace("\tmem[%d] = %p %d\n", j, p->mem[j].bar, p->mem[j].size); } if(pcilist != nil) pcitail->list = p; else pcilist = p; pcitail = p; } }