In message: <5070000.1074106599_at_lerlaptop-red.iadfw.net> Larry Rosenman <ler_at_lerctr.org> writes: : With todays sources (previous sources: 1/6/2004), I can't get past : the boot. : : I either hang on cbb0: unsupported device : or (the one time it DID boot), X froze (using KDE) at initializing : peripherals. Can you try the attached patch? It should help the duplicate allocation that's causing this problem. Warner diff -ur FreeBSD/src/sys/dev/pci/pci.c p4/newcard/src/sys/dev/pci/pci.c --- FreeBSD/src/sys/dev/pci/pci.c Tue Dec 23 19:01:22 2003 +++ p4/newcard/src/sys/dev/pci/pci.c Sun Jan 11 15:47:33 2004 _at__at_ -68,7 +68,8 _at__at_ static int pci_porten(device_t pcib, int b, int s, int f); static int pci_memen(device_t pcib, int b, int s, int f); -static int pci_add_map(device_t pcib, int b, int s, int f, int reg, +static int pci_add_map(device_t pcib, device_t bus, device_t dev, + int b, int s, int f, int reg, struct resource_list *rl); static void pci_add_resources(device_t pcib, device_t bus, device_t dev); _at__at_ -82,13 +83,15 _at__at_ static void pci_hdrtypedata(device_t pcib, int b, int s, int f, pcicfgregs *cfg); static void pci_read_extcap(device_t pcib, pcicfgregs *cfg); +static void pci_cfg_restore(device_t, struct pci_devinfo *); +static void pci_cfg_save(device_t, struct pci_devinfo *, int); static device_method_t pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_probe), DEVMETHOD(device_attach, pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), - DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_suspend, pci_suspend), DEVMETHOD(device_resume, pci_resume), /* Bus interface */ _at__at_ -96,7 +99,7 _at__at_ DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), DEVMETHOD(bus_read_ivar, pci_read_ivar), DEVMETHOD(bus_write_ivar, pci_write_ivar), - DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_driver_added, pci_driver_added), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), _at__at_ -613,6 +616,7 _at__at_ return (EINVAL); } pci_set_command_bit(dev, child, bit); + /* Some devices seem to need a brief stall here, what do to? */ command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2); if (command & bit) return (0); _at__at_ -719,11 +723,12 _at__at_ * register is a 32bit map register or 2 if it is a 64bit register. */ static int -pci_add_map(device_t pcib, int b, int s, int f, int reg, - struct resource_list *rl) +pci_add_map(device_t pcib, device_t bus, device_t dev, + int b, int s, int f, int reg, struct resource_list *rl) { uint32_t map; uint64_t base; + uint64_t start, end, count; uint8_t ln2size; uint8_t ln2range; uint32_t testval; _at__at_ -731,25 +736,34 _at__at_ int type; map = PCIB_READ_CONFIG(pcib, b, s, f, reg, 4); - - if (map == 0 || map == 0xffffffff) - return (1); /* skip invalid entry */ - PCIB_WRITE_CONFIG(pcib, b, s, f, reg, 0xffffffff, 4); testval = PCIB_READ_CONFIG(pcib, b, s, f, reg, 4); PCIB_WRITE_CONFIG(pcib, b, s, f, reg, map, 4); - base = pci_mapbase(map); if (pci_maptype(map) & PCI_MAPMEM) type = SYS_RES_MEMORY; else type = SYS_RES_IOPORT; ln2size = pci_mapsize(testval); ln2range = pci_maprange(testval); - if (ln2range == 64) { + base = pci_mapbase(map); + + /* + * For I/O registers, if bottom bit is set, and the next bit up + * isn't clear, we know we have a BAR that doesn't conform to the + * spec, so ignore it. Also, sanity check the size of the data + * areas to the type of memory involved. + */ + if ((testval & 0x1) == 0x1 && + (testval & 0x2) != 0) + return (1); + if ((type == SYS_RES_MEMORY && ln2size < 5) || + (type == SYS_RES_IOPORT && ln2size < 3)) + return (1); + + if (ln2range == 64) /* Read the other half of a 64bit map register */ base |= (uint64_t) PCIB_READ_CONFIG(pcib, b, s, f, reg + 4, 4) << 32; - } if (bootverbose) { printf("\tmap[%02x]: type %x, range %2d, base %08x, size %2d", _at__at_ -765,9 +779,10 _at__at_ /* * This code theoretically does the right thing, but has - * undesirable side effects in some cases where - * peripherals respond oddly to having these bits - * enabled. Leave them alone by default. + * undesirable side effects in some cases where peripherals + * respond oddly to having these bits enabled. Let the user + * be able to turn them off (since pci_enable_io_modes is 1 by + * default). */ if (pci_enable_io_modes) { /* Turn on resources that have been left off by a lazy BIOS */ _at__at_ -787,9 +802,23 _at__at_ if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f)) return (1); } - resource_list_add(rl, type, reg, base, base + (1 << ln2size) - 1, - (1 << ln2size)); + /* + * If base is 0, then we have problems. It is best to ignore + * such entires for the moment. These will be allocated later if + * the driver specifically requests them. + */ + if (base == 0) + return 1; + start = base; + end = base + (1 << ln2size) - 1; + count = 1 << ln2size; + resource_list_add(rl, type, reg, start, end, count); + /* + * Not quite sure what to do on failure of allocating the resource + * since I can postulate several right answers. + */ + resource_list_alloc(rl, bus, dev, type, ®, start, end, count, 0); return ((ln2range == 64) ? 2 : 1); } _at__at_ -805,14 +834,13 _at__at_ b = cfg->bus; s = cfg->slot; f = cfg->func; - for (i = 0; i < cfg->nummaps;) { - i += pci_add_map(pcib, b, s, f, PCIR_BAR(i), rl); - } + for (i = 0; i < cfg->nummaps;) + i += pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(i), rl); for (q = &pci_quirks[0]; q->devid; q++) { if (q->devid == ((cfg->device << 16) | cfg->vendor) && q->type == PCI_QUIRK_MAP_REG) - pci_add_map(pcib, b, s, f, q->arg1, rl); + pci_add_map(pcib, bus, dev, b, s, f, q->arg1, rl); } if (cfg->intpin > 0 && PCI_INTERRUPT_VALID(cfg->intline)) { _at__at_ -873,6 +901,8 _at__at_ pcib = device_get_parent(bus); dinfo->cfg.dev = device_add_child(bus, NULL, -1); device_set_ivars(dinfo->cfg.dev, dinfo); + pci_cfg_save(dinfo->cfg.dev, dinfo, 0); + pci_cfg_restore(dinfo->cfg.dev, dinfo); pci_add_resources(pcib, bus, dinfo->cfg.dev); pci_print_verbose(dinfo); } _at__at_ -907,6 +937,52 _at__at_ return (bus_generic_attach(dev)); } +int +pci_suspend(device_t dev) +{ + int numdevs; + device_t *devlist; + device_t child; + struct pci_devinfo *dinfo; + int i; + + /* + * Save the pci configuration space for each child. We don't need + * to do this, unless the BIOS suspend code powers down the bus and + * the devices on the bus. + */ + device_get_children(dev, &devlist, &numdevs); + for (i = 0; i < numdevs; i++) { + child = devlist[i]; + dinfo = (struct pci_devinfo *) device_get_ivars(child); + pci_cfg_save(child, dinfo, 0); + } + free(devlist, M_TEMP); + return (bus_generic_suspend(dev)); +} + +int +pci_resume(device_t dev) +{ + int numdevs; + device_t *devlist; + device_t child; + struct pci_devinfo *dinfo; + int i; + + /* + * Restore the pci configuration space for each child. + */ + device_get_children(dev, &devlist, &numdevs); + for (i = 0; i < numdevs; i++) { + child = devlist[i]; + dinfo = (struct pci_devinfo *) device_get_ivars(child); + pci_cfg_restore(child, dinfo); + } + free(devlist, M_TEMP); + return (bus_generic_resume(dev)); +} + static void pci_load_vendor_data(void) { _at__at_ -922,6 +998,34 _at__at_ } } +void +pci_driver_added(device_t dev, driver_t *driver) +{ + int numdevs; + device_t *devlist; + device_t child; + struct pci_devinfo *dinfo; + int i; + + device_printf(dev, "driver added\n"); + DEVICE_IDENTIFY(driver, dev); + device_get_children(dev, &devlist, &numdevs); + for (i = 0; i < numdevs; i++) { + child = devlist[i]; + if (device_get_state(child) != DS_NOTPRESENT) + continue; + dinfo = device_get_ivars(child); + pci_print_verbose(dinfo); +/*XXX???*/ /* resource_list_init(&dinfo->cfg.resources); */ + printf("pci%d:%d:%d: reprobing on driver added\n", + dinfo->cfg.bus, dinfo->cfg.slot, dinfo->cfg.func); + pci_cfg_restore(child, dinfo); + if (device_probe_and_attach(child) != 0) + pci_cfg_save(child, dinfo, 1); + } + free(devlist, M_TEMP); +} + int pci_print_child(device_t dev, device_t child) { _at__at_ -1047,6 +1151,7 _at__at_ } printf(" at device %d.%d (no driver attached)\n", pci_get_slot(child), pci_get_function(child)); + pci_cfg_save(child, (struct pci_devinfo *) device_get_ivars(child), 1); return; } _at__at_ -1326,18 +1431,78 _at__at_ } #endif /* DDB */ +/* + * XXX I'm not sure the following is good for 64-bit bars. + */ +static struct resource * +pci_alloc_map(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct pci_devinfo *dinfo = device_get_ivars(child); + struct resource_list *rl = &dinfo->resources; + struct resource_list_entry *rle; + struct resource *res; + uint32_t map, testval; + int mapsize; + + /* + * Weed out the bogons, and figure out how large the BAR/map is. + */ + map = pci_read_config(child, *rid, 4); + if (pci_maptype(map) & PCI_MAPMEM) { + if (type != SYS_RES_MEMORY) { + device_printf(child, "rid %#x says memory, driver wants %d failed.\n", *rid, type); + return (NULL); + } + } else { + if (type != SYS_RES_IOPORT) { + device_printf(child, "rid %#x says ioport, driver wants %d failed.\n", *rid, type); + return (NULL); + } + } + pci_write_config(child, *rid, 0xffffffff, 4); + testval = pci_read_config(child, *rid, 4); + + /* + * Allocate enough resource, and then write back the + * appropriate bar for that resource (this is the part + * I'm not sure is good for 64-bit bars). + */ + mapsize = pci_mapsize(testval); + count = 1 << mapsize; + if (RF_ALIGNMENT(flags) < mapsize) + flags = (flags & ~RF_ALIGNMENT_MASK) | RF_ALIGNMENT_LOG2(mapsize); + res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid, + start, end, count, flags); + if (res == NULL) { + device_printf(child, "%#lx bytes of rid %#x res %d failed.\n", + count, *rid, type); + pci_write_config(child, *rid, map, 4); + return (NULL); + } + resource_list_add(rl, type, *rid, start, end, count); + rle = resource_list_find(rl, type, *rid); + if (rle == NULL) + panic("pci_alloc_map: unexpedly can't find resource."); + rle->res = res; + device_printf(child, "Lazy allocation of %#lx bytes rid %#x type %d at %#lx\n", + count, *rid, type, rman_get_start(res)); + pci_write_config(child, *rid, rman_get_start(res), 4); + return (res); +} + + struct resource * pci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; + struct resource_list_entry *rle; pcicfgregs *cfg = &dinfo->cfg; /* * Perform lazy resource allocation - * - * XXX add support here for SYS_RES_IOPORT and SYS_RES_MEMORY */ if (device_get_parent(child) == dev) { switch (type) { _at__at_ -1363,16 +1528,39 _at__at_ if (*rid < PCIR_BAR(cfg->nummaps)) { /* * Enable the I/O mode. We should - * also be allocating resources - * too. XXX + * also be assigning resources too + * when none are present. The + * resource_list_alloc kind of sorta does + * this... */ if (PCI_ENABLE_IO(dev, child, type)) return (NULL); } + rle = resource_list_find(rl, type, *rid); + if (rle == NULL) + return (pci_alloc_map(dev, child, type, rid, + start, end, count, flags)); break; } + /* + * If we've already allocated the resource, then + * return it now. But first we may need to activate + * it, since we don't allocate the resource as active + * above. Normally this would be done down in the + * nexus, but since we short-circuit that path we have + * to do its job here. Not sure if we should free the + * resource if it fails to activate. + */ + rle = resource_list_find(rl, type, *rid); + if (rle != NULL && rle->res != NULL) { + device_printf(child, "Bus reserved %#lx bytes for rid %#x type %d at %#lx\n", rman_get_size(rle->res), *rid, type, rman_get_start(rle->res)); + if ((flags & RF_ACTIVE) && + bus_generic_activate_resource(dev, child, type, + *rid, rle->res) != 0) + return NULL; + return (rle->res); + } } - return (resource_list_alloc(rl, dev, child, type, rid, start, end, count, flags)); } _at__at_ -1416,13 +1604,9 _at__at_ struct resource_list * pci_get_resource_list (device_t dev, device_t child) { - struct pci_devinfo * dinfo = device_get_ivars(child); - struct resource_list * rl = &dinfo->resources; - - if (!rl) - return (NULL); + struct pci_devinfo *dinfo = device_get_ivars(child); - return (rl); + return (&dinfo->resources); } uint32_t _at__at_ -1447,7 +1631,7 _at__at_ } int -pci_child_location_str_method(device_t cbdev, device_t child, char *buf, +pci_child_location_str_method(device_t dev, device_t child, char *buf, size_t buflen) { struct pci_devinfo *dinfo; _at__at_ -1459,7 +1643,7 _at__at_ } int -pci_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, +pci_child_pnpinfo_str_method(device_t dev, device_t child, char *buf, size_t buflen) { struct pci_devinfo *dinfo; _at__at_ -1506,34 +1690,86 _at__at_ return (0); } -int -pci_resume(device_t dev) +static void +pci_cfg_restore(device_t dev, struct pci_devinfo *dinfo) { - int numdevs; - int i; - device_t *children; - device_t child; - struct pci_devinfo *dinfo; - pcicfgregs *cfg; + int i; - device_get_children(dev, &children, &numdevs); + /* + * Only do header type 0 devices. Type 1 devices are bridges, which + * we know need special treatment. Type 2 devices are cardbus bridges + * which also require special treatment. Other types are unknown, and + * we err on the side of safety by ignoring them. + */ + if (dinfo->cfg.hdrtype != 0) + return; + printf("pci%d:%d:%d: setting power state D0\n", dinfo->cfg.bus, + dinfo->cfg.slot, dinfo->cfg.func); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + for (i = 0; i < dinfo->cfg.nummaps; i++) + pci_write_config(dev, PCIR_MAPS + i * 4, dinfo->cfg.bar[i], 4); + pci_write_config(dev, PCIR_BIOS, dinfo->cfg.bios, 4); + pci_write_config(dev, PCIR_COMMAND, dinfo->cfg.cmdreg, 2); + pci_write_config(dev, PCIR_INTLINE, dinfo->cfg.intline, 1); + pci_write_config(dev, PCIR_INTPIN, dinfo->cfg.intpin, 1); + pci_write_config(dev, PCIR_MINGNT, dinfo->cfg.mingnt, 1); + pci_write_config(dev, PCIR_MAXLAT, dinfo->cfg.maxlat, 1); + pci_write_config(dev, PCIR_CACHELNSZ, dinfo->cfg.cachelnsz, 1); + pci_write_config(dev, PCIR_LATTIMER, dinfo->cfg.lattimer, 1); +} - for (i = 0; i < numdevs; i++) { - child = children[i]; +static void +pci_cfg_save(device_t dev, struct pci_devinfo *dinfo, int setstate) +{ + int i; + uint32_t cls; - dinfo = device_get_ivars(child); - cfg = &dinfo->cfg; - if (cfg->intpin > 0 && PCI_INTERRUPT_VALID(cfg->intline)) { - cfg->intline = PCI_ASSIGN_INTERRUPT(dev, child); - if (PCI_INTERRUPT_VALID(cfg->intline)) { - pci_write_config(child, PCIR_INTLINE, - cfg->intline, 1); - } - } - } + /* + * Only do header type 0 devices. Type 1 devices are bridges, which + * we know need special treatment. Type 2 devices are cardbus bridges + * which also require special treatment. Other types are unknown, and + * we err on the side of safety by ignoring them. Powering down + * bridges should not be undertaken lightly. + */ + if (dinfo->cfg.hdrtype != 0) + return; + for (i = 0; i < dinfo->cfg.nummaps; i++) + dinfo->cfg.bar[i] = pci_read_config(dev, PCIR_MAPS + i * 4, 4); + dinfo->cfg.bios = pci_read_config(dev, PCIR_BIOS, 4); - free(children, M_TEMP); + /* + * Some drivers apparently write to these registers w/o + * updating our cahced copy. No harm happens if we update the + * copy, so do so here so we can restore them. The COMMAND + * register is modified by the bus w/o updating the cache. This + * should represent the normally writable portion of the 'defined' + * part of type 0 headers. In theory we also need to save/restore + * the PCI capability structures we know about, but apart from power + * we don't know any that are writable. + */ + dinfo->cfg.cmdreg = pci_read_config(dev, PCIR_COMMAND, 2); + dinfo->cfg.intline = pci_read_config(dev, PCIR_INTLINE, 1); + dinfo->cfg.intpin = pci_read_config(dev, PCIR_INTPIN, 1); + dinfo->cfg.mingnt = pci_read_config(dev, PCIR_MINGNT, 1); + dinfo->cfg.maxlat = pci_read_config(dev, PCIR_MAXLAT, 1); + dinfo->cfg.cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); + dinfo->cfg.lattimer = pci_read_config(dev, PCIR_LATTIMER, 1); - return (bus_generic_resume(dev)); + /* + * don't set the state for display devices and for memory devices + * since bad things happen. we should (a) have drivers that can easily + * detach and (b) use generic drivers for these devices so that some + * device actually attaches. We need to make sure that when we + * implement (a) we don't power the device down on a reattach. + * + * John and Nate also tell me that we should be running the power up + * and power down hooks when we change power state for those nodes + * that have ACPI hooks in the tree. + */ + cls = pci_get_class(dev); + if (setstate && cls != PCIC_DISPLAY && cls != PCIC_MEMORY) { + pci_set_powerstate(dev, PCI_POWERSTATE_D3); + printf("pci%d:%d:%d: setting power state D3\n", dinfo->cfg.bus, + dinfo->cfg.slot, dinfo->cfg.func); + } } - diff -ur FreeBSD/src/sys/dev/pci/pci_private.h p4/newcard/src/sys/dev/pci/pci_private.h --- FreeBSD/src/sys/dev/pci/pci_private.h Sun Oct 19 11:32:23 2003 +++ p4/newcard/src/sys/dev/pci/pci_private.h Sat Sep 27 12:22:09 2003 _at__at_ -41,6 +41,7 _at__at_ void pci_add_children(device_t dev, int busno, size_t dinfo_size); void pci_add_child(device_t bus, struct pci_devinfo *dinfo); +void pci_driver_added(device_t dev, driver_t *driver); int pci_print_child(device_t dev, device_t child); void pci_probe_nomatch(device_t dev, device_t child); int pci_read_ivar(device_t dev, device_t child, int which, _at__at_ -74,4 +75,5 _at__at_ char *buf, size_t buflen); int pci_assign_interrupt_method(device_t dev, device_t child); int pci_resume(device_t dev); +int pci_suspend(device_t dev); #endif /* _PCI_PRIVATE_H_ */ diff -ur FreeBSD/src/sys/dev/pci/pcivar.h p4/newcard/src/sys/dev/pci/pcivar.h --- FreeBSD/src/sys/dev/pci/pcivar.h Sun Oct 19 11:32:23 2003 +++ p4/newcard/src/sys/dev/pci/pcivar.h Sun Jan 4 21:01:54 2004 _at__at_ -67,9 +67,11 _at__at_ }; /* config header information common to all header types */ - typedef struct pcicfg { struct device *dev; /* device which owns this */ + + uint32_t bar[PCI_MAXMAPS_0]; /* BARs */ + uint32_t bios; /* BIOS mapping */ uint16_t subvendor; /* card vendor ID */ uint16_t subdevice; /* card device ID, assigned by card vendor */Received on Wed Jan 14 2004 - 15:15:06 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:37:38 UTC