PERFORCE change 38012 for review
Warner Losh
imp at FreeBSD.org
Sat Sep 13 14:34:30 PDT 2003
http://perforce.freebsd.org/chv.cgi?CH=38012
Change 38012 by imp at imp_koguchi on 2003/09/13 14:34:06
Integrate these from newcard. This is the basis for much of
power management stuff that I've added to the pci bus. Extra
files integrated because I'm too lame to revert them.
Affected files ...
.. //depot/projects/power/sys/dev/pci/eisa_pci.c#2 integrate
.. //depot/projects/power/sys/dev/pci/fixup_pci.c#2 integrate
.. //depot/projects/power/sys/dev/pci/ignore_pci.c#2 integrate
.. //depot/projects/power/sys/dev/pci/isa_pci.c#2 integrate
.. //depot/projects/power/sys/dev/pci/pci.c#2 integrate
.. //depot/projects/power/sys/dev/pci/pci_if.m#2 integrate
.. //depot/projects/power/sys/dev/pci/pci_pci.c#2 integrate
.. //depot/projects/power/sys/dev/pci/pci_private.h#2 integrate
.. //depot/projects/power/sys/dev/pci/pci_user.c#2 integrate
.. //depot/projects/power/sys/dev/pci/pcib_if.m#2 integrate
.. //depot/projects/power/sys/dev/pci/pcib_private.h#2 integrate
.. //depot/projects/power/sys/dev/pci/pcireg.h#2 integrate
.. //depot/projects/power/sys/dev/pci/pcivar.h#2 integrate
Differences ...
==== //depot/projects/power/sys/dev/pci/eisa_pci.c#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/fixup_pci.c#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/ignore_pci.c#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/isa_pci.c#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/pci.c#2 (text+ko) ====
@@ -68,35 +68,39 @@
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,
- struct resource_list *rl);
+static int pci_add_map(device_t pcib, 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);
+ device_t dev);
static int pci_probe(device_t dev);
static int pci_attach(device_t dev);
+static int pci_suspend(device_t dev);
+static int pci_resume(device_t dev);
static void pci_load_vendor_data(void);
static int pci_describe_parse_line(char **ptr, int *vendor,
- int *device, char **desc);
+ int *device, char **desc);
static char *pci_describe_device(device_t dev);
static int pci_modevent(module_t mod, int what, void *arg);
static void pci_hdrtypedata(device_t pcib, int b, int s, int f,
- pcicfgregs *cfg);
+ 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_resume, bus_generic_resume),
+ DEVMETHOD(device_suspend, pci_suspend),
+ DEVMETHOD(device_resume, pci_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, pci_print_child),
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),
@@ -716,20 +720,31 @@
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);
+ 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;
@@ -771,6 +786,12 @@
if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f))
return (1);
}
+ /*
+ * If base is 0, then we have problems. It is best to ignore
+ * such entires for the moment. XXX
+ */
+ if (base == 0)
+ return 1;
resource_list_add(rl, type, reg, base, base + (1 << ln2size) - 1,
(1 << ln2size));
@@ -857,6 +878,7 @@
pcib = device_get_parent(bus);
dinfo->cfg.dev = device_add_child(bus, NULL, -1);
device_set_ivars(dinfo->cfg.dev, dinfo);
+ pci_cfg_restore(dinfo->cfg.dev, dinfo);
pci_add_resources(pcib, bus, dinfo->cfg.dev);
pci_print_verbose(dinfo);
}
@@ -891,6 +913,52 @@
return (bus_generic_attach(dev));
}
+static 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));
+}
+
+static 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)
{
@@ -906,6 +974,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)
{
@@ -1031,6 +1127,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;
}
@@ -1431,7 +1528,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;
@@ -1443,7 +1540,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;
@@ -1489,3 +1586,40 @@
return (0);
}
+
+static void
+pci_cfg_restore(device_t dev, struct pci_devinfo *dinfo)
+{
+#define NBAR 7
+ int i;
+ uint32_t bar[NBAR];
+
+ if (dinfo->cfg.hdrtype != 0)
+ return;
+ for (i = 0; i < NBAR; i++)
+ bar[i] = pci_read_config(dev, PCIR_MAPS + i * 4, 4);
+ 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 < NBAR; i++)
+ pci_write_config(dev, PCIR_MAPS + i * 4, bar[i], 4);
+ 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);
+}
+
+static void
+pci_cfg_save(device_t dev, struct pci_devinfo *dinfo, int setstate)
+{
+ uint32_t cls;
+
+ if (dinfo->cfg.hdrtype != 0)
+ return;
+ 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);
+ }
+}
==== //depot/projects/power/sys/dev/pci/pci_if.m#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/pci_pci.c#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/pci_private.h#2 (text+ko) ====
@@ -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,
==== //depot/projects/power/sys/dev/pci/pci_user.c#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/pcib_if.m#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/pcib_private.h#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/pcireg.h#2 (text+ko) ====
==== //depot/projects/power/sys/dev/pci/pcivar.h#2 (text+ko) ====
@@ -51,8 +51,13 @@
typedef uint32_t pci_addr_t; /* uint64_t for system with 64bit addresses */
#endif
+/* Additional data saved on power events */
+struct pci_save
+{
+ uint32_t bar[7]; /* 7 bars to save */
+};
+
/* config header information common to all header types */
-
typedef struct pcicfg {
struct device *dev; /* device which owns this */
More information about the p4-projects
mailing list