PCI (ATA) lazy resource allocation patch

Thomas E. Spanjaard tgen at netphreax.net
Mon Dec 4 07:01:15 PST 2006


The attached patch adds a new option to the PCI code (options 
PCI_MAP_FIXUP) which enables code that does lazy allocation for 
memory/ioport resources in pci_alloc_resource(), and adds and allocates 
certain resources for PCI ATA controllers in legacy mode in 
pci_add_resources(). These resources are missing from the BARs, and as 
it's part of the PCI spec as special case [1], it should go here. The 
code comes from FreeBSD, who reasoned the same [2].

Patch instructions:
cd /usr/src && patch -p1 < /tmp/pci_map_fixup.diff
1: http://www.google.com/search?q=PCI+2.2+specification+pdf
2: http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/dev/pci/pci.c#rev1.247
Cheers,
--
        Thomas E. Spanjaard
        tgen at netphreax.net
diff -r 00b2cbd30a2f sys/bus/pci/pci.c
--- a/sys/bus/pci/pci.c	Wed Nov 29 21:08:50 2006 +0000
+++ b/sys/bus/pci/pci.c	Sun Dec 03 17:33:33 2006 +0000
@@ -1349,6 +1349,68 @@ pci_add_map(device_t pcib, int b, int s,
 	return (ln2range == 64) ? 2 : 1;
 }
 
+#ifdef PCI_MAP_FIXUP
+/*
+ * For ATA devices we need to decide early on what addressing mode to use.
+ * Legacy demands that the primary and secondary ATA ports sits on the
+ * same addresses that old ISA hardware did. This dictates that we use
+ * those addresses and ignore the BARs if we cannot set PCI native
+ * 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 rid, type, progif;
+#if 0
+	/* if this device supports PCI native addressing use it */
+	progif = pci_read_config(dev, PCIR_PROGIF, 1);
+	if ((progif &0x8a) == 0x8a) {
+		if (pci_mapbase(pci_read_config(dev, PCIR_BAR(0), 4)) &&
+		    pci_mapbase(pci_read_config(dev, PCIR_BAR(2), 4))) {
+			printf("Trying ATA native PCI addressing mode\n");
+			pci_write_config(dev, PCIR_PROGIF, progif | 0x05, 1);
+		}
+	}
+#endif
+	/*
+	 * Because we return any preallocated resources for lazy
+	 * allocation for PCI devices in pci_alloc_resource(), we can
+	 * allocate our legacy resources here.
+	 */
+	progif = pci_read_config(dev, PCIR_PROGIF, 1);
+	type = SYS_RES_IOPORT;
+	if (progif & PCIP_STORAGE_IDE_MODEPRIM) {
+		pci_add_map(pcib, b, s, f, PCIR_BAR(0), rl);
+		pci_add_map(pcib, b, s, f, PCIR_BAR(1), rl);
+	} 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);
+		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);
+	}
+	if (progif & PCIP_STORAGE_IDE_MODESEC) {
+		pci_add_map(pcib, b, s, f, PCIR_BAR(2), rl);
+		pci_add_map(pcib, b, s, f, PCIR_BAR(3), rl);
+	} 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);
+		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);
+	}
+	pci_add_map(pcib, b, s, f, PCIR_BAR(4), rl);
+	pci_add_map(pcib, b, s, f, PCIR_BAR(5), rl);
+}
+#endif /* PCI_MAP_FIXUP */
+
 static void
 pci_add_resources(device_t pcib, device_t bus, device_t dev)
 {
@@ -1364,9 +1426,17 @@ pci_add_resources(device_t pcib, device_
 	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);
-	}
+#ifdef PCI_MAP_FIXUP
+	/* atapci devices in legacy mode need special map treatment */
+	if ((pci_get_class(dev) == PCIC_STORAGE) &&
+	    (pci_get_subclass(dev) == PCIS_STORAGE_IDE) &&
+	    (pci_get_progif(dev) & PCIP_STORAGE_IDE_MASTERDEV))
+		pci_ata_maps(pcib, bus, dev, b, s, f, rl);
+	else
+#endif /* PCI_MAP_FIXUP */
+		for (i = 0; i < cfg->nummaps;) {
+			i += pci_add_map(pcib, b, s, f, PCIR_BAR(i),rl);
+		}
 
 	for (q = &pci_quirks[0]; q->devid; q++) {
 		if (q->devid == ((cfg->device << 16) | cfg->vendor)
@@ -1670,18 +1740,108 @@ pci_write_ivar(device_t dev, device_t ch
 	return 0;
 }
 
+#ifdef PCI_MAP_FIXUP
+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. BARs that read back 0 here are bogus and unimplemented.
+	 *
+	 * Note: atapci in legacy mode are special and handled elsewhere
+	 * in the code. If you have an atapci device in legacy mode and
+	 * 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_mapbase(testval) == 0)
+		goto out;
+	if (pci_maptype(testval) & PCI_MAPMEM) {
+		if (type != SYS_RES_MEMORY) {
+			if (bootverbose)
+				device_printf(dev, "child %s requested type %d"
+					      " for rid %#x, but the BAR says "
+					      "it is a memio\n",
+					      device_get_nameunit(child), type,
+					      *rid);
+			goto out;
+		}
+	} else {
+		if (type != SYS_RES_IOPORT) {
+			if (bootverbose)
+				device_printf(dev, "child %s requested type %d"
+					      " for rid %#x, but the BAR says "
+					      "it is an ioport\n",
+					      device_get_nameunit(child), type,
+					      *rid);
+			goto out;
+		}
+	}
+	/*
+	 * For real BARs, we need to override the size that
+	 * the driver requests, because that's what the BAR
+	 * actually uses and we would otherwise have a
+	 * situation where we might allocate the excess to
+	 * another driver, which won't work.
+	 */
+	mapsize = pci_mapsize(testval);
+	count = 1 << mapsize;
+	if (RF_ALIGNMENT(flags) < mapsize)
+		flags = (flags & ~RF_ALIGNMENT_MASK) |
+		   RF_ALIGNMENT_LOG2(mapsize);
+	/*
+	 * Allocate enough resource, and then write back the
+	 * appropriate BAR for that resource.
+	 */
+	res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid,
+				 start, end, count, flags);
+	if (res == NULL) {
+		device_printf(child, "%#lx bytes at rid %#x res %d failed "
+			      "(%#lx, %#lx)\n", count, *rid, type, start, end);
+		goto out;
+	}
+	resource_list_add(rl, type, *rid, start, end, count);
+	rle = resource_list_find(rl, type, *rid);
+	if (rle == NULL)
+		panic("pci_alloc_map: unexpectedly can't find resource.");
+	rle->res = res;
+	rle->start = rman_get_start(res);
+	rle->end = rman_get_end(res);
+	rle->count = count;
+	if (bootverbose)
+		device_printf(child, "lazy allocation of %#lx bytes rid %#x "
+			      "type %d at %#lx\n", count, *rid, type,
+			      rman_get_start(res));
+	map = rman_get_start(res);
+out:;
+	pci_write_config(child, *rid, map, 4);
+	return res;
+}
+#endif /* PCI_MAP_FIXUP */
+
 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;
+#ifdef PCI_MAP_FIXUP
+	struct resource_list_entry *rle;
+#endif /* PCI_MAP_FIXUP */
 	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) {
@@ -1707,6 +1867,7 @@ pci_alloc_resource(device_t dev, device_
 			break;
 #endif
 		case SYS_RES_IOPORT:
+			/* FALLTHROUGH */
 		case SYS_RES_MEMORY:
 			if (*rid < PCIR_BAR(cfg->nummaps)) {
 				/*
@@ -1719,8 +1880,42 @@ pci_alloc_resource(device_t dev, device_
 				if (PCI_ENABLE_IO(dev, child, type))
 					return (NULL);
 			}
-			break;
-		}
+#ifdef PCI_MAP_FIXUP
+			rle = resource_list_find(rl, type, *rid);
+			if (rle == NULL)
+				return pci_alloc_map(dev, child, type, rid,
+						     start, end, count, flags);
+#endif /* PCI_MAP_FIXUP */
+			break;
+		}
+#ifdef PCI_MAP_FIXUP
+		/*
+		 * 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.
+		 *
+		 * Note: this also finds and returns resources for
+		 * atapci devices in legacy mode as allocated in
+		 * pci_ata_maps().
+		 */
+		rle = resource_list_find(rl, type, *rid);
+		if (rle != NULL && rle->res != NULL) {
+			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));
+			if ((flags & RF_ACTIVE) &&
+			    bus_generic_activate_resource(dev, child, type,
+							  *rid, rle->res) != 0)
+				return NULL;
+			return rle->res;
+		}
+#endif /* PCI_MAP_FIXUP */
 	}
 	return resource_list_alloc(rl, dev, child, type, rid,
 				   start, end, count, flags);
diff -r 00b2cbd30a2f sys/conf/options
--- a/sys/conf/options	Wed Nov 29 21:08:50 2006 +0000
+++ b/sys/conf/options	Thu Nov 30 13:47:28 2006 +0000
@@ -421,6 +421,7 @@ MSGBUF_SIZE		opt_msgbuf.h
 # PCI related options
 PCI_QUIET		opt_pci.h
 PCI_ENABLE_IO_MODES	opt_pci.h
+PCI_MAP_FIXUP   opt_pci.h
 COMPAT_OLDPCI
 
 # NFS options
Attachment:
signature.asc
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pgp00002.pgp
Type: application/octet-stream
Size: 186 bytes
Desc: "Description: OpenPGP digital signature"
URL: <http://lists.dragonflybsd.org/pipermail/kernel/attachments/20061204/d1ad4b20/attachment-0017.obj>


More information about the Kernel mailing list