Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 4 May 2009 20:36:00 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org
Subject:   svn commit: r191795 - in stable/7/sys: . contrib/pf dev/ath/ath_hal dev/cxgb dev/pci
Message-ID:  <200905042036.n44Ka08v056369@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Mon May  4 20:35:59 2009
New Revision: 191795
URL: http://svn.freebsd.org/changeset/base/191795

Log:
  MFC: Various fixes to the PCI resource allocation code:
  - Disabling decoding of BARs via the PCI command register before writing
    to the BARs to size them.
  - Don't free resources decoded by a BAR when the device driver releases a
    BAR.  Instead, the PCI bus now owns unclaimed BARs and gives them to
    drivers on demand.  When a driver releases a BAR, the PCI bus reclaims
    it and leaves the resource range allocated.  This also prevents drivers
    from allocating the same resource multiple times.
  - Move the activation of BARs by enabling decoding in the PCI command
    register into bus_activate_resource() so that it always happens after the
    BAR is fully programmed.
  - Always read/write the full 64-bit value of 64-bit BARs.
  - Consolidate duplicated code for reading and sizing BARs and writing base
    address to BARs.

Modified:
  stable/7/sys/   (props changed)
  stable/7/sys/contrib/pf/   (props changed)
  stable/7/sys/dev/ath/ath_hal/   (props changed)
  stable/7/sys/dev/cxgb/   (props changed)
  stable/7/sys/dev/pci/pci.c
  stable/7/sys/dev/pci/pci_private.h
  stable/7/sys/dev/pci/pcireg.h

Modified: stable/7/sys/dev/pci/pci.c
==============================================================================
--- stable/7/sys/dev/pci/pci.c	Mon May  4 20:25:56 2009	(r191794)
+++ stable/7/sys/dev/pci/pci.c	Mon May  4 20:35:59 2009	(r191795)
@@ -71,18 +71,17 @@ __FBSDID("$FreeBSD$");
 #define	ACPI_PWR_FOR_SLEEP(x, y, z)
 #endif
 
-static uint32_t		pci_mapbase(unsigned mapreg);
-static const char	*pci_maptype(unsigned mapreg);
-static int		pci_mapsize(unsigned testval);
-static int		pci_maprange(unsigned mapreg);
+static pci_addr_t	pci_mapbase(uint64_t mapreg);
+static const char	*pci_maptype(uint64_t mapreg);
+static int		pci_mapsize(uint64_t testval);
+static int		pci_maprange(uint64_t mapreg);
 static void		pci_fixancient(pcicfgregs *cfg);
 
-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_porten(device_t dev);
+static int		pci_memen(device_t dev);
 static void		pci_assign_interrupt(device_t bus, device_t dev,
 			    int force_route);
-static int		pci_add_map(device_t pcib, device_t bus, device_t dev,
-			    int b, int s, int f, int reg,
+static int		pci_add_map(device_t bus, device_t dev, int reg,
 			    struct resource_list *rl, int force, int prefetch);
 static int		pci_probe(device_t dev);
 static int		pci_attach(device_t dev);
@@ -136,7 +135,7 @@ static device_method_t pci_methods[] = {
 	DEVMETHOD(bus_delete_resource,	pci_delete_resource),
 	DEVMETHOD(bus_alloc_resource,	pci_alloc_resource),
 	DEVMETHOD(bus_release_resource,	bus_generic_rl_release_resource),
-	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+	DEVMETHOD(bus_activate_resource, pci_activate_resource),
 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
 	DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
 	DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
@@ -316,8 +315,8 @@ pci_find_device(uint16_t vendor, uint16_
 
 /* return base address of memory or port map */
 
-static uint32_t
-pci_mapbase(uint32_t mapreg)
+static pci_addr_t
+pci_mapbase(uint64_t mapreg)
 {
 
 	if (PCI_BAR_MEM(mapreg))
@@ -329,7 +328,7 @@ pci_mapbase(uint32_t mapreg)
 /* return map type of memory or port map */
 
 static const char *
-pci_maptype(unsigned mapreg)
+pci_maptype(uint64_t mapreg)
 {
 
 	if (PCI_BAR_IO(mapreg))
@@ -342,7 +341,7 @@ pci_maptype(unsigned mapreg)
 /* return log2 of map size decoded for memory or port map */
 
 static int
-pci_mapsize(uint32_t testval)
+pci_mapsize(uint64_t testval)
 {
 	int ln2size;
 
@@ -361,7 +360,7 @@ pci_mapsize(uint32_t testval)
 /* return log2 of address range supported by map register */
 
 static int
-pci_maprange(unsigned mapreg)
+pci_maprange(uint64_t mapreg)
 {
 	int ln2range = 0;
 
@@ -2257,17 +2256,75 @@ pci_print_verbose(struct pci_devinfo *di
 }
 
 static int
-pci_porten(device_t pcib, int b, int s, int f)
+pci_porten(device_t dev)
 {
-	return (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2)
-		& PCIM_CMD_PORTEN) != 0;
+	return (pci_read_config(dev, PCIR_COMMAND, 2) & PCIM_CMD_PORTEN) != 0;
 }
 
 static int
-pci_memen(device_t pcib, int b, int s, int f)
+pci_memen(device_t dev)
 {
-	return (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2)
-		& PCIM_CMD_MEMEN) != 0;
+	return (pci_read_config(dev, PCIR_COMMAND, 2) & PCIM_CMD_MEMEN) != 0;
+}
+
+static void
+pci_read_bar(device_t dev, int reg, pci_addr_t *mapp, pci_addr_t *testvalp)
+{
+	pci_addr_t map, testval;
+	int ln2range;
+	uint16_t cmd;
+
+	map = pci_read_config(dev, reg, 4);
+	ln2range = pci_maprange(map);
+	if (ln2range == 64)
+		map |= (pci_addr_t)pci_read_config(dev, reg + 4, 4) << 32;
+
+	/*
+	 * Disable decoding via the command register before
+	 * determining the BAR's length since we will be placing it in
+	 * a weird state.
+	 */
+	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
+	pci_write_config(dev, PCIR_COMMAND,
+	    cmd & ~(PCI_BAR_MEM(map) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), 2);
+
+	/*
+	 * Determine the BAR's length by writing all 1's.  The bottom
+	 * log_2(size) bits of the BAR will stick as 0 when we read
+	 * the value back.
+	 */
+	pci_write_config(dev, reg, 0xffffffff, 4);
+	testval = pci_read_config(dev, reg, 4);
+	if (ln2range == 64) {
+		pci_write_config(dev, reg + 4, 0xffffffff, 4);
+		testval |= (pci_addr_t)pci_read_config(dev, reg + 4, 4) << 32;
+	}
+
+	/*
+	 * Restore the original value of the BAR.  We may have reprogrammed
+	 * the BAR of the low-level console device and when booting verbose,
+	 * we need the console device addressable.
+	 */
+	pci_write_config(dev, reg, map, 4);
+	if (ln2range == 64)
+		pci_write_config(dev, reg + 4, map >> 32, 4);
+	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
+
+	*mapp = map;
+	*testvalp = testval;
+}
+
+static void
+pci_write_bar(device_t dev, int reg, pci_addr_t base)
+{
+	pci_addr_t map;
+	int ln2range;
+
+	map = pci_read_config(dev, reg, 4);
+	ln2range = pci_maprange(map);
+	pci_write_config(dev, reg, base, 4);
+	if (ln2range == 64)
+		pci_write_config(dev, reg + 4, base >> 32, 4);
 }
 
 /*
@@ -2275,36 +2332,26 @@ pci_memen(device_t pcib, int b, int s, i
  * register is a 32bit map register or 2 if it is a 64bit register.
  */
 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, int force,
-    int prefetch)
+pci_add_map(device_t bus, device_t dev, int reg, struct resource_list *rl,
+    int force, int prefetch)
 {
-	uint32_t map;
-	pci_addr_t base;
+	pci_addr_t base, map, testval;
 	pci_addr_t start, end, count;
-	uint8_t ln2size;
-	uint8_t ln2range;
-	uint32_t testval;
+	int barlen, maprange, mapsize, type;
 	uint16_t cmd;
-	int type;
-	int barlen;
 	struct resource *res;
 
-	map = PCIB_READ_CONFIG(pcib, b, s, f, reg, 4);
-	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);
-
+	pci_read_bar(dev, reg, &map, &testval);
 	if (PCI_BAR_MEM(map)) {
 		type = SYS_RES_MEMORY;
 		if (map & PCIM_BAR_MEM_PREFETCH)
 			prefetch = 1;
 	} else
 		type = SYS_RES_IOPORT;
-	ln2size = pci_mapsize(testval);
-	ln2range = pci_maprange(testval);
+	mapsize = pci_mapsize(testval);
 	base = pci_mapbase(map);
-	barlen = ln2range == 64 ? 2 : 1;
+	maprange = pci_maprange(map);
+	barlen = maprange == 64 ? 2 : 1;
 
 	/*
 	 * For I/O registers, if bottom bit is set, and the next bit up
@@ -2315,19 +2362,16 @@ pci_add_map(device_t pcib, device_t bus,
 	 */
 	if (PCI_BAR_IO(testval) && (testval & PCIM_BAR_IO_RESERVED) != 0)
 		return (barlen);
-	if ((type == SYS_RES_MEMORY && ln2size < 4) ||
-	    (type == SYS_RES_IOPORT && ln2size < 2))
+	if ((type == SYS_RES_MEMORY && mapsize < 4) ||
+	    (type == SYS_RES_IOPORT && mapsize < 2))
 		return (barlen);
 
-	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 %s, range %2d, base %#jx, size %2d",
-		    reg, pci_maptype(map), ln2range, (uintmax_t)base, ln2size);
-		if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f))
+		    reg, pci_maptype(map), maprange, (uintmax_t)base, mapsize);
+		if (type == SYS_RES_IOPORT && !pci_porten(dev))
 			printf(", port disabled\n");
-		else if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f))
+		else if (type == SYS_RES_MEMORY && !pci_memen(dev))
 			printf(", memory disabled\n");
 		else
 			printf(", enabled\n");
@@ -2349,7 +2393,8 @@ pci_add_map(device_t pcib, device_t bus,
 	if ((u_long)base != base) {
 		device_printf(bus,
 		    "pci%d:%d:%d:%d bar %#x too many address bits",
-		    pci_get_domain(dev), b, s, f, reg);
+		    pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev),
+		    pci_get_function(dev), reg);
 		return (barlen);
 	}
 
@@ -2362,30 +2407,30 @@ pci_add_map(device_t pcib, device_t bus,
 	 */
 	if (pci_enable_io_modes) {
 		/* Turn on resources that have been left off by a lazy BIOS */
-		if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f)) {
-			cmd = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2);
+		if (type == SYS_RES_IOPORT && !pci_porten(dev)) {
+			cmd = pci_read_config(dev, PCIR_COMMAND, 2);
 			cmd |= PCIM_CMD_PORTEN;
-			PCIB_WRITE_CONFIG(pcib, b, s, f, PCIR_COMMAND, cmd, 2);
+			pci_write_config(dev, PCIR_COMMAND, cmd, 2);
 		}
-		if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f)) {
-			cmd = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2);
+		if (type == SYS_RES_MEMORY && !pci_memen(dev)) {
+			cmd = pci_read_config(dev, PCIR_COMMAND, 2);
 			cmd |= PCIM_CMD_MEMEN;
-			PCIB_WRITE_CONFIG(pcib, b, s, f, PCIR_COMMAND, cmd, 2);
+			pci_write_config(dev, PCIR_COMMAND, cmd, 2);
 		}
 	} else {
-		if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f))
+		if (type == SYS_RES_IOPORT && !pci_porten(dev))
 			return (barlen);
-		if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f))
+		if (type == SYS_RES_MEMORY && !pci_memen(dev))
 			return (barlen);
 	}
 
-	count = 1 << ln2size;
+	count = 1 << mapsize;
 	if (base == 0 || base == pci_mapbase(testval)) {
 		start = 0;	/* Let the parent decide. */
 		end = ~0ULL;
 	} else {
 		start = base;
-		end = base + (1 << ln2size) - 1;
+		end = base + (1 << mapsize) - 1;
 	}
 	resource_list_add(rl, type, reg, start, end, count);
 
@@ -2406,11 +2451,11 @@ pci_add_map(device_t pcib, device_t bus,
 		 */
 		resource_list_delete(rl, type, reg);
 		start = 0;
-	} else
+	} else {
 		start = rman_get_start(res);
-	pci_write_config(dev, reg, start, 4);
-	if (ln2range == 64)
-		pci_write_config(dev, reg + 4, start >> 32, 4);
+		rman_set_device(res, bus);
+	}
+	pci_write_bar(dev, reg, start);
 	return (barlen);
 }
 
@@ -2422,9 +2467,10 @@ pci_add_map(device_t pcib, device_t bus,
  * addressing mode.
  */
 static void
-pci_ata_maps(device_t pcib, device_t bus, device_t dev, int b,
-    int s, int f, struct resource_list *rl, int force, uint32_t prefetchmask)
+pci_ata_maps(device_t bus, device_t dev, struct resource_list *rl, int force,
+    uint32_t prefetchmask)
 {
+	struct resource *r;
 	int rid, type, progif;
 #if 0
 	/* if this device supports PCI native addressing use it */
@@ -2440,38 +2486,42 @@ pci_ata_maps(device_t pcib, device_t bus
 	progif = pci_read_config(dev, PCIR_PROGIF, 1);
 	type = SYS_RES_IOPORT;
 	if (progif & PCIP_STORAGE_IDE_MODEPRIM) {
-		pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(0), rl, force,
+		pci_add_map(bus, dev, PCIR_BAR(0), rl, force,
 		    prefetchmask & (1 << 0));
-		pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(1), rl, force,
+		pci_add_map(bus, dev, PCIR_BAR(1), rl, force,
 		    prefetchmask & (1 << 1));
 	} else {
 		rid = PCIR_BAR(0);
 		resource_list_add(rl, type, rid, 0x1f0, 0x1f7, 8);
-		resource_list_alloc(rl, bus, dev, type, &rid, 0x1f0, 0x1f7, 8,
-		    0);
+		r = resource_list_alloc(rl, bus, dev, type, &rid, 0x1f0, 0x1f7,
+		    8, 0);
+		rman_set_device(r, bus);
 		rid = PCIR_BAR(1);
 		resource_list_add(rl, type, rid, 0x3f6, 0x3f6, 1);
-		resource_list_alloc(rl, bus, dev, type, &rid, 0x3f6, 0x3f6, 1,
-		    0);
+		r = resource_list_alloc(rl, bus, dev, type, &rid, 0x3f6, 0x3f6,
+		    1, 0);
+		rman_set_device(r, bus);
 	}
 	if (progif & PCIP_STORAGE_IDE_MODESEC) {
-		pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(2), rl, force,
+		pci_add_map(bus, dev, PCIR_BAR(2), rl, force,
 		    prefetchmask & (1 << 2));
-		pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(3), rl, force,
+		pci_add_map(bus, dev, PCIR_BAR(3), rl, force,
 		    prefetchmask & (1 << 3));
 	} else {
 		rid = PCIR_BAR(2);
 		resource_list_add(rl, type, rid, 0x170, 0x177, 8);
-		resource_list_alloc(rl, bus, dev, type, &rid, 0x170, 0x177, 8,
-		    0);
+		r = resource_list_alloc(rl, bus, dev, type, &rid, 0x170, 0x177,
+		    8, 0);
+		rman_set_device(r, bus);
 		rid = PCIR_BAR(3);
 		resource_list_add(rl, type, rid, 0x376, 0x376, 1);
-		resource_list_alloc(rl, bus, dev, type, &rid, 0x376, 0x376, 1,
-		    0);
+		r = resource_list_alloc(rl, bus, dev, type, &rid, 0x376, 0x376,
+		    1, 0);
+		rman_set_device(r, bus);
 	}
-	pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(4), rl, force,
+	pci_add_map(bus, dev, PCIR_BAR(4), rl, force,
 	    prefetchmask & (1 << 4));
-	pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(5), rl, force,
+	pci_add_map(bus, dev, PCIR_BAR(5), rl, force,
 	    prefetchmask & (1 << 5));
 }
 
@@ -2526,18 +2576,11 @@ pci_assign_interrupt(device_t bus, devic
 void
 pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask)
 {
-	device_t pcib;
 	struct pci_devinfo *dinfo = device_get_ivars(dev);
 	pcicfgregs *cfg = &dinfo->cfg;
 	struct resource_list *rl = &dinfo->resources;
 	struct pci_quirk *q;
-	int b, i, f, s;
-
-	pcib = device_get_parent(bus);
-
-	b = cfg->bus;
-	s = cfg->slot;
-	f = cfg->func;
+	int i;
 
 	/* ATA devices needs special map treatment */
 	if ((pci_get_class(dev) == PCIC_STORAGE) &&
@@ -2545,11 +2588,11 @@ pci_add_resources(device_t bus, device_t
 	    ((pci_get_progif(dev) & PCIP_STORAGE_IDE_MASTERDEV) ||
 	     (!pci_read_config(dev, PCIR_BAR(0), 4) &&
 	      !pci_read_config(dev, PCIR_BAR(2), 4))) )
-		pci_ata_maps(pcib, bus, dev, b, s, f, rl, force, prefetchmask);
+		pci_ata_maps(bus, dev, rl, force, prefetchmask);
 	else
 		for (i = 0; i < cfg->nummaps;)
-			i += pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(i),
-			    rl, force, prefetchmask & (1 << i));
+			i += pci_add_map(bus, dev, PCIR_BAR(i), rl, force,
+			    prefetchmask & (1 << i));
 
 	/*
 	 * Add additional, quirked resources.
@@ -2557,8 +2600,7 @@ pci_add_resources(device_t bus, device_t
 	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, bus, dev, b, s, f, q->arg1, rl,
-			  force, 0);
+			pci_add_map(bus, dev, q->arg1, rl, force, 0);
 	}
 
 	if (cfg->intpin > 0 && PCI_INTERRUPT_VALID(cfg->intline)) {
@@ -3401,21 +3443,12 @@ pci_alloc_map(device_t dev, device_t chi
 	 * it fails here, that other code is broken.
 	 */
 	res = NULL;
-	map = pci_read_config(child, *rid, 4);
-	pci_write_config(child, *rid, 0xffffffff, 4);
-	testval = pci_read_config(child, *rid, 4);
-	if (pci_maprange(testval) == 64)
-		map |= (pci_addr_t)pci_read_config(child, *rid + 4, 4) << 32;
+	pci_read_bar(child, *rid, &map, &testval);
+
+	/* Ignore a BAR with a base of 0. */
 	if (pci_mapbase(testval) == 0)
 		goto out;
 
-	/*
-	 * Restore the original value of the BAR.  We may have reprogrammed
-	 * the BAR of the low-level console device and when booting verbose,
-	 * we need the console device addressable.
-	 */
-	pci_write_config(child, *rid, map, 4);
-
 	if (PCI_BAR_MEM(testval)) {
 		if (type != SYS_RES_MEMORY) {
 			if (bootverbose)
@@ -3435,6 +3468,7 @@ pci_alloc_map(device_t dev, device_t chi
 			goto out;
 		}
 	}
+
 	/*
 	 * For real BARs, we need to override the size that
 	 * the driver requests, because that's what the BAR
@@ -3454,13 +3488,14 @@ pci_alloc_map(device_t dev, device_t chi
 	 * appropriate bar for that resource.
 	 */
 	res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid,
-	    start, end, count, flags);
+	    start, end, count, flags & ~RF_ACTIVE);
 	if (res == NULL) {
 		device_printf(child,
 		    "%#lx bytes of rid %#x res %d failed (%#lx, %#lx).\n",
 		    count, *rid, type, start, end);
 		goto out;
 	}
+	rman_set_device(res, dev);
 	resource_list_add(rl, type, *rid, start, end, count);
 	rle = resource_list_find(rl, type, *rid);
 	if (rle == NULL)
@@ -3474,10 +3509,8 @@ pci_alloc_map(device_t dev, device_t chi
 		    "Lazy allocation of %#lx bytes rid %#x type %d at %#lx\n",
 		    count, *rid, type, rman_get_start(res));
 	map = rman_get_start(res);
+	pci_write_bar(child, *rid, map);
 out:;
-	pci_write_config(child, *rid, map, 4);
-	if (pci_maprange(testval) == 64)
-		pci_write_config(child, *rid + 4, map >> 32, 4);
 	return (res);
 }
 
@@ -3489,68 +3522,63 @@ pci_alloc_resource(device_t dev, device_
 	struct pci_devinfo *dinfo = device_get_ivars(child);
 	struct resource_list *rl = &dinfo->resources;
 	struct resource_list_entry *rle;
+	struct resource *res;
 	pcicfgregs *cfg = &dinfo->cfg;
 
+	if (device_get_parent(child) != dev)
+		return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
+		    type, rid, start, end, count, flags));
+
 	/*
 	 * Perform lazy resource allocation
 	 */
-	if (device_get_parent(child) == dev) {
-		switch (type) {
-		case SYS_RES_IRQ:
-			/*
-			 * Can't alloc legacy interrupt once MSI messages
-			 * have been allocated.
-			 */
-			if (*rid == 0 && (cfg->msi.msi_alloc > 0 ||
-			    cfg->msix.msix_alloc > 0))
+	switch (type) {
+	case SYS_RES_IRQ:
+		/*
+		 * Can't alloc legacy interrupt once MSI messages have
+		 * been allocated.
+		 */
+		if (*rid == 0 && (cfg->msi.msi_alloc > 0 ||
+		    cfg->msix.msix_alloc > 0))
+			return (NULL);
+
+		/*
+		 * If the child device doesn't have an interrupt
+		 * routed and is deserving of an interrupt, try to
+		 * assign it one.
+		 */
+		if (*rid == 0 && !PCI_INTERRUPT_VALID(cfg->intline) &&
+		    (cfg->intpin != 0))
+			pci_assign_interrupt(dev, child, 0);
+		break;
+	case SYS_RES_IOPORT:
+	case SYS_RES_MEMORY:
+		/* Allocate resources for this BAR if needed. */
+		rle = resource_list_find(rl, type, *rid);
+		if (rle == NULL) {
+			res = pci_alloc_map(dev, child, type, rid, start, end,
+			    count, flags);
+			if (res == NULL)
 				return (NULL);
-			/*
-			 * If the child device doesn't have an
-			 * interrupt routed and is deserving of an
-			 * interrupt, try to assign it one.
-			 */
-			if (*rid == 0 && !PCI_INTERRUPT_VALID(cfg->intline) &&
-			    (cfg->intpin != 0))
-				pci_assign_interrupt(dev, child, 0);
-			break;
-		case SYS_RES_IOPORT:
-		case SYS_RES_MEMORY:
-			if (*rid < PCIR_BAR(cfg->nummaps)) {
-				/*
-				 * Enable the I/O mode.  We should
-				 * 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.
+		 * If the resource belongs to the bus, then give it to
+		 * the child.  We need to activate it if requested
+		 * since the bus always allocates inactive resources.
 		 */
-		rle = resource_list_find(rl, type, *rid);
-		if (rle != NULL && rle->res != NULL) {
+		if (rle != NULL && rle->res != NULL &&
+		    rman_get_device(rle->res) == dev) {
 			if (bootverbose)
 				device_printf(child,
 			    "Reserved %#lx bytes for rid %#x type %d at %#lx\n",
 				    rman_get_size(rle->res), *rid, type,
 				    rman_get_start(rle->res));
+			rman_set_device(rle->res, child);
 			if ((flags & RF_ACTIVE) &&
-			    bus_generic_activate_resource(dev, child, type,
-			    *rid, rle->res) != 0)
+			    bus_activate_resource(child, type, *rid,
+			    rle->res) != 0)
 				return (NULL);
 			return (rle->res);
 		}
@@ -3559,6 +3587,59 @@ pci_alloc_resource(device_t dev, device_
 	    start, end, count, flags));
 }
 
+int
+pci_release_resource(device_t dev, device_t child, int type, int rid,
+    struct resource *r)
+{
+	int error;
+
+	if (device_get_parent(child) != dev)
+		return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
+		    type, rid, r));
+
+	/*
+	 * For BARs we don't actually want to release the resource.
+	 * Instead, we deactivate the resource if needed and then give
+	 * ownership of the BAR back to the bus.
+	 */
+	switch (type) {
+	case SYS_RES_IOPORT:
+	case SYS_RES_MEMORY:
+		if (rman_get_device(r) != child)
+			return (EINVAL);
+		if (rman_get_flags(r) & RF_ACTIVE) {
+			error = bus_deactivate_resource(child, type, rid, r);
+			if (error)
+				return (error);
+		}
+		rman_set_device(r, dev);
+		return (0);
+	}
+	return (bus_generic_rl_release_resource(dev, child, type, rid, r));
+}
+
+int
+pci_activate_resource(device_t dev, device_t child, int type, int rid,
+    struct resource *r)
+{
+	int error;
+
+	error = bus_generic_activate_resource(dev, child, type, rid, r);
+	if (error)
+		return (error);
+
+	/* Enable decoding in the command register when activating BARs. */
+	if (device_get_parent(child) == dev) {
+		switch (type) {
+		case SYS_RES_IOPORT:
+		case SYS_RES_MEMORY:
+			error = PCI_ENABLE_IO(dev, child, type);
+			break;
+		}
+	}
+	return (error);
+}
+
 void
 pci_delete_resource(device_t dev, device_t child, int type, int rid)
 {
@@ -3572,27 +3653,33 @@ pci_delete_resource(device_t dev, device
 	dinfo = device_get_ivars(child);
 	rl = &dinfo->resources;
 	rle = resource_list_find(rl, type, rid);
-	if (rle) {
-		if (rle->res) {
-			if (rman_get_device(rle->res) != dev ||
-			    rman_get_flags(rle->res) & RF_ACTIVE) {
-				device_printf(dev, "delete_resource: "
-				    "Resource still owned by child, oops. "
-				    "(type=%d, rid=%d, addr=%lx)\n",
-				    rle->type, rle->rid,
-				    rman_get_start(rle->res));
-				return;
-			}
-			bus_release_resource(dev, type, rid, rle->res);
+	if (rle == NULL)
+		return;
+
+	if (rle->res) {
+		if (rman_get_device(rle->res) != dev ||
+		    rman_get_flags(rle->res) & RF_ACTIVE) {
+			device_printf(dev, "delete_resource: "
+			    "Resource still owned by child, oops. "
+			    "(type=%d, rid=%d, addr=%lx)\n",
+			    rle->type, rle->rid,
+			    rman_get_start(rle->res));
+			return;
 		}
-		resource_list_delete(rl, type, rid);
+
+		/*
+		 * If this is a BAR, clear the BAR so it stops
+		 * decoding before releasing the resource.
+		 */
+		switch (type) {
+		case SYS_RES_IOPORT:
+		case SYS_RES_MEMORY:
+			pci_write_bar(child, rid, 0);
+			break;
+		}
+		bus_release_resource(dev, type, rid, rle->res);
 	}
-	/*
-	 * Why do we turn off the PCI configuration BAR when we delete a
-	 * resource? -- imp
-	 */
-	pci_write_config(child, rid, 0, 4);
-	BUS_DELETE_RESOURCE(device_get_parent(dev), child, type, rid);
+	resource_list_delete(rl, type, rid);
 }
 
 struct resource_list *

Modified: stable/7/sys/dev/pci/pci_private.h
==============================================================================
--- stable/7/sys/dev/pci/pci_private.h	Mon May  4 20:25:56 2009	(r191794)
+++ stable/7/sys/dev/pci/pci_private.h	Mon May  4 20:35:59 2009	(r191795)
@@ -82,6 +82,10 @@ int		pci_msix_count_method(device_t dev,
 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);
+int		pci_release_resource(device_t dev, device_t child, int type,
+		    int rid, struct resource *r);
+int		pci_activate_resource(device_t dev, device_t child, int type,
+		    int rid, struct resource *r);
 void		pci_delete_resource(device_t dev, device_t child, 
 		    int type, int rid);
 struct resource_list *pci_get_resource_list (device_t dev, device_t child);

Modified: stable/7/sys/dev/pci/pcireg.h
==============================================================================
--- stable/7/sys/dev/pci/pcireg.h	Mon May  4 20:25:56 2009	(r191794)
+++ stable/7/sys/dev/pci/pcireg.h	Mon May  4 20:35:59 2009	(r191795)
@@ -130,7 +130,7 @@
 #define	PCIM_BAR_MEM_1MB	2	/* Locate below 1MB in PCI <= 2.1 */
 #define	PCIM_BAR_MEM_64		4
 #define	PCIM_BAR_MEM_PREFETCH	0x00000008
-#define	PCIM_BAR_MEM_BASE	0xfffffff0
+#define	PCIM_BAR_MEM_BASE	0xfffffffffffffff0ULL
 #define	PCIM_BAR_IO_RESERVED	0x00000002
 #define	PCIM_BAR_IO_BASE	0xfffffffc
 #define	PCIR_CIS	0x28



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200905042036.n44Ka08v056369>