diff -ru /paco/imp/FreeBSD/src/sys/dev/acpica/acpi.c ./dev/acpica/acpi.c --- /paco/imp/FreeBSD/src/sys/dev/acpica/acpi.c Thu Jan 1 12:58:40 2004 +++ ./dev/acpica/acpi.c Thu Jan 1 13:10:00 2004 @@ -1501,6 +1501,16 @@ } /* + * Get a specified device's power state. + */ +ACPI_STATUS +acpi_GetPowerState(ACPI_HANDLE handle, int *state) +{ + + return (acpi_EvaluateInteger(handle, "_PSC", state)); +} + +/* * Set the system sleep state * * Currently we support S1-S5 but S4 is only S4BIOS diff -ru /paco/imp/FreeBSD/src/sys/dev/acpica/acpi_pci.c ./dev/acpica/acpi_pci.c --- /paco/imp/FreeBSD/src/sys/dev/acpica/acpi_pci.c Mon Sep 22 23:15:20 2003 +++ ./dev/acpica/acpi_pci.c Thu Jan 1 09:54:50 2004 @@ -65,11 +65,8 @@ static int acpi_pci_attach(device_t dev); static int acpi_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result); -#if 0 static int acpi_pci_set_powerstate_method(device_t dev, device_t child, int state); -static int acpi_pci_get_powerstate_method(device_t dev, device_t child); -#endif static ACPI_STATUS acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context, void **status); @@ -78,7 +75,7 @@ DEVMETHOD(device_probe, acpi_pci_probe), DEVMETHOD(device_attach, acpi_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 */ @@ -108,9 +105,8 @@ DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method), DEVMETHOD(pci_enable_io, pci_enable_io_method), DEVMETHOD(pci_disable_io, pci_disable_io_method), - /* XXX: We should override these two. */ DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method), - DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method), + DEVMETHOD(pci_set_powerstate, acpi_pci_set_powerstate_method), DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method), { 0, 0 } @@ -140,24 +136,58 @@ return(pci_read_ivar(dev, child, which, result)); } -#if 0 /* * PCI power manangement */ static int acpi_pci_set_powerstate_method(device_t dev, device_t child, int state) { - /* XXX: TODO */ - return (ENXIO); -} + ACPI_STATUS status; + int acpi_state, old_state, error; -static int -acpi_pci_get_powerstate_method(device_t dev, device_t child) -{ - /* XXX: TODO */ - return (ENXIO); + switch (state) { + case PCI_POWERSTATE_D0: + acpi_state = ACPI_STATE_D0; + break; + case PCI_POWERSTATE_D1: + acpi_state = ACPI_STATE_D1; + break; + case PCI_POWERSTATE_D2: + acpi_state = ACPI_STATE_D2; + break; + case PCI_POWERSTATE_D3: + acpi_state = ACPI_STATE_D3; + break; + default: + return (EINVAL); + } + + /* + * We set the state using PCI Power Management outside of setting + * the ACPI state. This means that when powering down a device, we + * first shut it down using PCI, and then using ACPI, which lets ACPI + * try to power down any Power Resources that are now no longer used. + * When powering up a device, we let ACPI set the state first so that + * it can enable any needed Power Resources before changing the PCI + * power state. + */ + old_state = pci_get_powerstate(child); + if (old_state < state) { + error = pci_set_powerstate_method(dev, child, state); + if (error) + return (error); + } + status = acpi_pwr_switch_consumer(acpi_get_handle(child), acpi_state); + if (ACPI_FAILURE(status)) + device_printf(dev, + "Failed to set ACPI power state D%d on %s: %s\n", + acpi_state, device_get_nameunit(child), + AcpiFormatException(status)); + if (state > old_state) + return (pci_set_powerstate_method(dev, child, state)); + else + return (0); } -#endif static ACPI_STATUS acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context, diff -ru /paco/imp/FreeBSD/src/sys/dev/acpica/acpivar.h ./dev/acpica/acpivar.h --- /paco/imp/FreeBSD/src/sys/dev/acpica/acpivar.h Thu Jan 1 12:58:41 2004 +++ ./dev/acpica/acpivar.h Thu Jan 1 13:10:02 2004 @@ -174,6 +174,7 @@ extern BOOLEAN acpi_MatchHid(device_t dev, char *hid); extern ACPI_STATUS acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result); +extern ACPI_STATUS acpi_GetPowerState(ACPI_HANDLE handle, int *state); extern ACPI_BUFFER *acpi_AllocBuffer(int size); extern ACPI_STATUS acpi_EvaluateInteger(ACPI_HANDLE handle, char *path, int *number); diff -ru /paco/imp/FreeBSD/src/sys/dev/pccbb/pccbb.c ./dev/pccbb/pccbb.c --- /paco/imp/FreeBSD/src/sys/dev/pccbb/pccbb.c Thu Jan 1 12:59:03 2004 +++ ./dev/pccbb/pccbb.c Thu Jan 1 13:41:51 2004 @@ -1,6 +1,6 @@ /* * Copyright (c) 2002-2003 M. Warner Losh. - * Copyright (c) 2000,2001 Jonathan Chen. + * Copyright (c) 2000-2001 Jonathan Chen. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -683,8 +683,9 @@ static int cbb_attach(device_t brdev) { + static int curr_bus_number = 1; /* XXX EVILE BAD (see below) */ struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(brdev); - int rid; + int rid, bus; mtx_init(&sc->mtx, device_get_nameunit(brdev), "cbb", MTX_DEF); cv_init(&sc->cv, "cbb cv"); @@ -698,55 +699,16 @@ STAILQ_INIT(&sc->intr_handlers); #ifndef BURN_BRIDGES cbb_powerstate_d0(brdev); - - /* - * The PCI bus code should assign us memory in the absense - * of the BIOS doing so. However, 'should' isn't 'is,' so we kludge - * up something here until the PCI/acpi code properly assigns the - * resource. - */ #endif + rid = CBBR_SOCKBASE; sc->base_res = bus_alloc_resource(brdev, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE); if (!sc->base_res) { -#ifdef BURN_BRIDGES device_printf(brdev, "Could not map register memory\n"); mtx_destroy(&sc->mtx); cv_destroy(&sc->cv); return (ENOMEM); -#else - uint32_t sockbase; - /* - * Generally, the BIOS will assign this memory for us. - * However, newer BIOSes do not because the MS design - * documents have mandated that this is for the OS - * to assign rather than the BIOS. This driver shouldn't - * be doing this, but until the pci bus code (or acpi) - * does this, we allow CardBus bridges to work on more - * machines. - */ - pci_write_config(brdev, rid, 0xfffffffful, 4); - sockbase = pci_read_config(brdev, rid, 4); - sockbase = (sockbase & 0xfffffff0ul) & - -(sockbase & 0xfffffff0ul); - sc->base_res = bus_generic_alloc_resource( - device_get_parent(brdev), brdev, SYS_RES_MEMORY, - &rid, cbb_start_mem, ~0, sockbase, - RF_ACTIVE | rman_make_alignment_flags(sockbase)); - if (!sc->base_res) { - device_printf(brdev, - "Could not grab register memory\n"); - mtx_destroy(&sc->mtx); - cv_destroy(&sc->cv); - return (ENOMEM); - } - sc->flags |= CBB_KLUDGE_ALLOC; - pci_write_config(brdev, CBBR_SOCKBASE, - rman_get_start(sc->base_res), 4); - DEVPRINTF((brdev, "PCI Memory allocated: %08lx\n", - rman_get_start(sc->base_res))); -#endif } else { DEVPRINTF((brdev, "Found memory at %08lx\n", rman_get_start(sc->base_res))); @@ -759,6 +721,25 @@ sc->exca.chipset = EXCA_CARDBUS; cbb_chipinit(sc); + /* + * This is a gross hack. We should be scanning the entire pci + * tree, assigning bus numbers in a way such that we (1) can + * reserve 1 extra bus just in case and (2) all sub busses + * are in an appropriate range. + */ + bus = pci_read_config(brdev, PCIR_SECBUS_2, 1); + DEVPRINTF((brdev, "Secondary bus is %d\n", bus)); + if (bus == 0) { + bus = curr_bus_number; + DEVPRINTF((brdev, "Secondary bus set to %d subbus %d\n", bus, + bus + 1)); + sc->secbus = bus; + sc->subbus = bus + 1; + pci_write_config(brdev, PCIR_SECBUS_2, bus, 1); + pci_write_config(brdev, PCIR_SUBBUS_2, bus + 1, 1); + curr_bus_number += 2; + } + /* attach children */ sc->cbdev = device_add_child(brdev, "cardbus", -1); if (sc->cbdev == NULL) @@ -812,19 +793,13 @@ device_printf(brdev, "unable to create event thread.\n"); panic("cbb_create_event_thread"); } - return (0); err: if (sc->irq_res) bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->base_res) { - if (sc->flags & CBB_KLUDGE_ALLOC) - bus_generic_release_resource(device_get_parent(brdev), - brdev, SYS_RES_MEMORY, CBBR_SOCKBASE, - sc->base_res); - else - bus_release_resource(brdev, SYS_RES_MEMORY, - CBBR_SOCKBASE, sc->base_res); + bus_release_resource(brdev, SYS_RES_MEMORY, CBBR_SOCKBASE, + sc->base_res); } mtx_destroy(&sc->mtx); cv_destroy(&sc->cv); @@ -863,12 +838,8 @@ mtx_unlock(&sc->mtx); bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->irq_res); - if (sc->flags & CBB_KLUDGE_ALLOC) - bus_generic_release_resource(device_get_parent(brdev), - brdev, SYS_RES_MEMORY, CBBR_SOCKBASE, sc->base_res); - else - bus_release_resource(brdev, SYS_RES_MEMORY, - CBBR_SOCKBASE, sc->base_res); + bus_release_resource(brdev, SYS_RES_MEMORY, CBBR_SOCKBASE, + sc->base_res); mtx_destroy(&sc->mtx); cv_destroy(&sc->cv); return (0); @@ -1124,7 +1095,6 @@ */ sockevent = cbb_get(sc, CBB_SOCKET_EVENT); if (sockevent != 0) { - DPRINTF(("CBB EVENT 0x%x\n", sockevent)); /* ack the interrupt */ cbb_setb(sc, CBB_SOCKET_EVENT, sockevent); @@ -1146,7 +1116,6 @@ cbb_setb(sc, CBB_SOCKET_MASK, CBB_SOCKET_MASK_CD); sc->flags &= ~CBB_CARD_OK; cbb_disable_func_intr(sc); - DPRINTF(("Waking up thread\n")); cv_signal(&sc->cv); mtx_unlock(&sc->mtx); } @@ -1340,13 +1309,17 @@ * detect the voltage for the card, and set it. Since the power * used is the square of the voltage, lower voltages is a big win * and what Windows does (and what Microsoft prefers). The MS paper - * also talks about preferring the CIS entry as well. + * also talks about preferring the CIS entry as well. In addition, + * we power up with OE disabled. We'll set it later in the power + * up sequence. */ static int cbb_do_power(device_t brdev) { + struct cbb_softc *sc = device_get_softc(brdev); int voltage; + exca_clrb(&sc->exca, EXCA_PWRCTL, EXCA_PWRCTL_OE); /* Prefer lowest voltage supported */ voltage = cbb_detect_voltage(brdev); cbb_power(brdev, CARD_OFF); @@ -1632,6 +1605,7 @@ start = cbb_start_mem; if (end < start) end = start; + /* This is now suspect: */ if (RF_ALIGNMENT(flags) < CBB_MEMALIGN_BITS) flags = (flags & ~RF_ALIGNMENT_MASK) | rman_make_alignment_flags(CBB_MEMALIGN); @@ -1956,11 +1930,14 @@ static uint32_t cbb_read_config(device_t brdev, int b, int s, int f, int reg, int width) { + uint32_t rv; + /* * Pass through to the next ppb up the chain (i.e. our grandparent). */ - return (PCIB_READ_CONFIG(device_get_parent(device_get_parent(brdev)), - b, s, f, reg, width)); + rv = PCIB_READ_CONFIG(device_get_parent(device_get_parent(brdev)), + b, s, f, reg, width); + return (rv); } static void diff -ru /paco/imp/FreeBSD/src/sys/dev/pccbb/pccbbvar.h ./dev/pccbb/pccbbvar.h --- /paco/imp/FreeBSD/src/sys/dev/pccbb/pccbbvar.h Sat Jun 14 10:24:51 2003 +++ ./dev/pccbb/pccbbvar.h Thu Jan 1 13:41:51 2004 @@ -67,7 +67,6 @@ struct cv cv; u_int32_t flags; #define CBB_CARD_OK 0x08000000 -#define CBB_KLUDGE_ALLOC 0x10000000 #define CBB_16BIT_CARD 0x20000000 #define CBB_KTHREAD_RUNNING 0x40000000 #define CBB_KTHREAD_DONE 0x80000000 Only in ./dev/pccbb: tmp.71124.11~ Only in ./dev/pccbb: tmp.71124.2~ Only in /paco/imp/FreeBSD/src/sys/dev/pci: CVS diff -ru /paco/imp/FreeBSD/src/sys/dev/pci/pci.c ./dev/pci/pci.c --- /paco/imp/FreeBSD/src/sys/dev/pci/pci.c Thu Jan 1 12:59:03 2004 +++ ./dev/pci/pci.c Thu Jan 1 13:33:42 2004 @@ -68,7 +68,8 @@ 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); @@ -82,13 +83,15 @@ 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 */ @@ -96,7 +99,7 @@ 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), @@ -613,6 +616,7 @@ 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); @@ -719,11 +723,12 @@ * 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; @@ -731,25 +736,34 @@ 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", @@ -765,9 +779,10 @@ /* * 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 */ @@ -787,9 +802,23 @@ 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); } @@ -805,14 +834,13 @@ 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)) { @@ -873,6 +901,8 @@ 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); } @@ -907,6 +937,52 @@ 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) { @@ -922,6 +998,34 @@ } } +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) { @@ -1047,6 +1151,7 @@ } 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; } @@ -1326,18 +1431,74 @@ } #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; + + /* + * 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). + */ + count = 1 << pci_mapsize(testval); + 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) { @@ -1363,16 +1524,39 @@ 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)); } @@ -1416,13 +1600,9 @@ 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 @@ -1447,7 +1627,7 @@ } 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; @@ -1459,7 +1639,7 @@ } 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; @@ -1506,34 +1686,84 @@ 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_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); - 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 -ru /paco/imp/FreeBSD/src/sys/dev/pci/pci_pci.c ./dev/pci/pci_pci.c --- /paco/imp/FreeBSD/src/sys/dev/pci/pci_pci.c Thu Oct 23 01:47:15 2003 +++ ./dev/pci/pci_pci.c Thu Jan 1 13:33:43 2004 @@ -311,144 +311,133 @@ pcib_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 pcib_softc *sc = device_get_softc(dev); - int ok; + struct pcib_softc *sc = device_get_softc(dev); + int ok; - /* - * If this is a "default" allocation against this rid, we can't work - * out where it's coming from (we should actually never see these) so we - * just have to punt. - */ - if ((start == 0) && (end == ~0)) { - device_printf(dev, "can't decode default resource id %d for %s%d, bypassing\n", - *rid, device_get_name(child), device_get_unit(child)); - } else { /* * Fail the allocation for this range if it's not supported. */ switch (type) { case SYS_RES_IOPORT: - ok = 1; - if (!pcib_is_isa_io(start)) { - ok = 0; - if (pcib_is_io_open(sc)) - ok = (start >= sc->iobase && end <= sc->iolimit); - if (!pci_allow_unsupported_io_range) { - if (!ok) { - if (start < sc->iobase) - start = sc->iobase; - if (end > sc->iolimit) - end = sc->iolimit; - } - } else { - if (start < sc->iobase) - printf("start (%lx) < sc->iobase (%x)\n", start, - sc->iobase); - if (end > sc->iolimit) - printf("end (%lx) > sc->iolimit (%x)\n", - end, sc->iolimit); - if (end < start) - printf("end (%lx) < start (%lx)\n", end, start); + ok = 1; + if (!pcib_is_isa_io(start)) { + ok = 0; + if (pcib_is_io_open(sc)) + ok = (start >= sc->iobase && end <= sc->iolimit); + if (!pci_allow_unsupported_io_range) { + if (!ok) { + if (start < sc->iobase) + start = sc->iobase; + if (end > sc->iolimit) + end = sc->iolimit; + } + } else { + if (start < sc->iobase) + printf("start (%lx) < sc->iobase (%x)\n", start, + sc->iobase); + if (end > sc->iolimit) + printf("end (%lx) > sc->iolimit (%x)\n", + end, sc->iolimit); + if (end < start) + printf("end (%lx) < start (%lx)\n", end, start); + } + } + if (end < start) { + start = 0; + end = 0; + ok = 0; } - } - if (end < start) { - start = 0; - end = 0; - ok = 0; - } - if (!ok) { - device_printf(dev, "device %s%d requested unsupported I/O " - "range 0x%lx-0x%lx (decoding 0x%x-0x%x)\n", - device_get_name(child), device_get_unit(child), start, end, - sc->iobase, sc->iolimit); - return (NULL); - } - if (bootverbose) - device_printf(sc->dev, "device %s%d requested decoded I/O range 0x%lx-0x%lx\n", - device_get_name(child), device_get_unit(child), start, end); - break; + if (!ok) { + device_printf(dev, "device %s%d requested unsupported I/O " + "range 0x%lx-0x%lx (decoding 0x%x-0x%x)\n", + device_get_name(child), device_get_unit(child), start, end, + sc->iobase, sc->iolimit); + return (NULL); + } + if (bootverbose) + device_printf(sc->dev, "device %s%d requested decoded I/O range 0x%lx-0x%lx\n", + device_get_name(child), device_get_unit(child), start, end); + break; case SYS_RES_MEMORY: - ok = 1; - if (!pcib_is_isa_mem(start)) { - ok = 0; - if (pcib_is_nonprefetch_open(sc)) - ok = ok || (start >= sc->membase && end <= sc->memlimit); - if (pcib_is_prefetch_open(sc)) - ok = ok || (start >= sc->pmembase && end <= sc->pmemlimit); - if (!pci_allow_unsupported_io_range) { - if (!ok) { - ok = 1; - if (flags & RF_PREFETCHABLE) { - if (pcib_is_prefetch_open(sc)) { - if (start < sc->pmembase) - start = sc->pmembase; - if (end > sc->pmemlimit) - end = sc->pmemlimit; - } else { - ok = 0; - } - } else { /* non-prefetchable */ - if (pcib_is_nonprefetch_open(sc)) { - if (start < sc->membase) - start = sc->membase; - if (end > sc->memlimit) - end = sc->memlimit; - } else { - ok = 0; - } + ok = 1; + if (!pcib_is_isa_mem(start)) { + ok = 0; + if (pcib_is_nonprefetch_open(sc)) + ok = ok || (start >= sc->membase && end <= sc->memlimit); + if (pcib_is_prefetch_open(sc)) + ok = ok || (start >= sc->pmembase && end <= sc->pmemlimit); + if (!pci_allow_unsupported_io_range) { + if (!ok) { + ok = 1; + if (flags & RF_PREFETCHABLE) { + if (pcib_is_prefetch_open(sc)) { + if (start < sc->pmembase) + start = sc->pmembase; + if (end > sc->pmemlimit) + end = sc->pmemlimit; + } else { + ok = 0; + } + } else { /* non-prefetchable */ + if (pcib_is_nonprefetch_open(sc)) { + if (start < sc->membase) + start = sc->membase; + if (end > sc->memlimit) + end = sc->memlimit; + } else { + ok = 0; + } + } + } + } else if (!ok) { + ok = 1; /* pci_allow_unsupported_ranges -> always ok */ + if (pcib_is_nonprefetch_open(sc)) { + if (start < sc->membase) + printf("start (%lx) < sc->membase (%x)\n", + start, sc->membase); + if (end > sc->memlimit) + printf("end (%lx) > sc->memlimit (%x)\n", + end, sc->memlimit); + } + if (pcib_is_prefetch_open(sc)) { + if (start < sc->pmembase) + printf("start (%lx) < sc->pmembase (%x)\n", + start, sc->pmembase); + if (end > sc->pmemlimit) + printf("end (%lx) > sc->pmemlimit (%x)\n", + end, sc->memlimit); + } + if (end < start) + printf("end (%lx) < start (%lx)\n", end, start); } - } - } else if (!ok) { - ok = 1; /* pci_allow_unsupported_ranges -> always ok */ - if (pcib_is_nonprefetch_open(sc)) { - if (start < sc->membase) - printf("start (%lx) < sc->membase (%x)\n", - start, sc->membase); - if (end > sc->memlimit) - printf("end (%lx) > sc->memlimit (%x)\n", - end, sc->memlimit); - } - if (pcib_is_prefetch_open(sc)) { - if (start < sc->pmembase) - printf("start (%lx) < sc->pmembase (%x)\n", - start, sc->pmembase); - if (end > sc->pmemlimit) - printf("end (%lx) > sc->pmemlimit (%x)\n", - end, sc->memlimit); - } - if (end < start) - printf("end (%lx) < start (%lx)\n", end, start); } - } - if (end < start) { - start = 0; - end = 0; - ok = 0; - } - if (!ok && bootverbose) - device_printf(dev, - "device %s%d requested unsupported memory range " - "0x%lx-0x%lx (decoding 0x%x-0x%x, 0x%x-0x%x)\n", - device_get_name(child), device_get_unit(child), start, - end, sc->membase, sc->memlimit, sc->pmembase, - sc->pmemlimit); - if (!ok) - return (NULL); - if (bootverbose) - device_printf(sc->dev, "device %s%d requested decoded memory range 0x%lx-0x%lx\n", - device_get_name(child), device_get_unit(child), start, end); - break; + if (end < start) { + start = 0; + end = 0; + ok = 0; + } + if (!ok && bootverbose) + device_printf(dev, + "device %s%d requested unsupported memory range " + "0x%lx-0x%lx (decoding 0x%x-0x%x, 0x%x-0x%x)\n", + device_get_name(child), device_get_unit(child), start, + end, sc->membase, sc->memlimit, sc->pmembase, + sc->pmemlimit); + if (!ok) + return (NULL); + if (bootverbose) + device_printf(sc->dev,"device %s%d requested decoded memory range 0x%lx-0x%lx\n", + device_get_name(child), device_get_unit(child), start, end); + break; default: - break; + break; } - } - - /* - * Bridge is OK decoding this resource, so pass it up. - */ - return(bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); + /* + * Bridge is OK decoding this resource, so pass it up. + */ + return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); } /* diff -ru /paco/imp/FreeBSD/src/sys/dev/pci/pci_private.h ./dev/pci/pci_private.h --- /paco/imp/FreeBSD/src/sys/dev/pci/pci_private.h Mon Sep 22 23:15:32 2003 +++ ./dev/pci/pci_private.h Thu Jan 1 13:33:43 2004 @@ -41,6 +41,7 @@ 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, @@ -74,4 +75,5 @@ 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 -ru /paco/imp/FreeBSD/src/sys/dev/pci/pci_user.c ./dev/pci/pci_user.c --- /paco/imp/FreeBSD/src/sys/dev/pci/pci_user.c Sun Oct 19 21:36:06 2003 +++ ./dev/pci/pci_user.c Thu Jan 1 13:33:43 2004 @@ -429,4 +429,3 @@ return (error); } - diff -ru /paco/imp/FreeBSD/src/sys/dev/pci/pcivar.h ./dev/pci/pcivar.h --- /paco/imp/FreeBSD/src/sys/dev/pci/pcivar.h Mon Sep 22 23:15:32 2003 +++ ./dev/pci/pcivar.h Thu Jan 1 13:33:43 2004 @@ -67,9 +67,10 @@ }; /* 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 */ uint16_t subvendor; /* card vendor ID */ uint16_t subdevice; /* card device ID, assigned by card vendor */ diff -ru /paco/imp/FreeBSD/src/sys/sparc64/pci/ofw_pcibus.c ./sparc64/pci/ofw_pcibus.c --- /paco/imp/FreeBSD/src/sys/sparc64/pci/ofw_pcibus.c Fri Aug 22 01:48:44 2003 +++ ./sparc64/pci/ofw_pcibus.c Thu Jan 1 10:11:37 2004 @@ -69,8 +69,8 @@ DEVMETHOD(device_probe, ofw_pcibus_probe), DEVMETHOD(device_attach, ofw_pcibus_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_suspend, pci_suspend), + DEVMETHOD(device_resume, pci_resume), /* Bus interface */ DEVMETHOD(bus_print_child, pci_print_child),