[PATCH 2/15] yenta: fix hidden PCI bus numbers

Dominik Brodowski linux at dominikbrodowski.net
Fri Jun 30 15:52:55 EDT 2006


From: Bernhard Kaindl <bk at suse.de>
Date: Tue, 30 May 2006 18:00:34 +0200
Subject: [PATCH] yenta: fix hidden PCI bus numbers

Fixup the subordinate number parent bridge of yenta Cardbus Bridges
before the PCI bus scan starts to make the cardbus cards which are
otherwise hidden for PCI scans work.

Signed-off-by: Bernhard Kaindl <bk at suse.de>
Signed-off-by: Dominik Brodowski <linux at dominikbrodowski.net>
---
 drivers/pcmcia/yenta_socket.c |   73 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
index 006749d..d9d005e 100644
--- a/drivers/pcmcia/yenta_socket.c
+++ b/drivers/pcmcia/yenta_socket.c
@@ -1005,6 +1005,77 @@ static void yenta_config_init(struct yen
 	config_writew(socket, CB_BRIDGE_CONTROL, bridge);
 }
 
+/**
+ * yenta_fixup_parent_subordinate - Fix subordinate bus# of the parent bridge
+ * @cardbus_bridge: The PCI bus which the CardBus bridge bridges to
+ *
+ * Checks if devices on the bus which the CardBus bridge bridges to would be
+ * invisible during PCI scans because of a misconfigured subordinate number
+ * of the parent brige - some BIOSes seem to be too lazy to set it right.
+ * Does the fixup carefully by checking how far it can go without conflicts.
+ * See http://bugzilla.kernel.org/show_bug.cgi?id=2944 for more information.
+ */
+static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge)
+{
+	struct list_head *tmp;
+	unsigned char upper_limit;
+ 	/*
+	 * We only check and fix the parent bridge: All systems which need
+	 * this fixup that have been reviewed are laptops and the only bridge
+	 * which needed fixing was the parent bridge of the CardBus bridge:
+	 */
+	struct pci_bus *bridge_to_fix = cardbus_bridge->parent;
+
+	/* Check bus numbers are already set up correctly: */
+	if (bridge_to_fix->subordinate >= cardbus_bridge->subordinate)
+		return; /* The subordinate number is ok, nothing to do */
+
+	if (!bridge_to_fix->parent)
+		return; /* Root bridges are ok */
+
+	/* stay within the limits of the bus range of the parent: */
+	upper_limit = bridge_to_fix->parent->subordinate;
+
+	/* check the bus ranges of all silbling bridges to prevent overlap */
+	list_for_each(tmp, &bridge_to_fix->parent->children) {
+		struct pci_bus * silbling = pci_bus_b(tmp);
+		/*
+		 * If the silbling has a higher secondary bus number
+		 * and it's secondary is equal or smaller than our
+		 * current upper limit, set the new upper limit to
+		 * the bus number below the silbling's range:
+		 */
+		if (silbling->secondary > bridge_to_fix->subordinate
+		    && silbling->secondary <= upper_limit)
+			upper_limit = silbling->secondary - 1;
+	}
+
+	/* Show that the wanted subordinate number is not possible: */
+	if (cardbus_bridge->subordinate > upper_limit)
+		printk(KERN_WARNING "Yenta: Upper limit for fixing this "
+			"bridge's parent bridge: #%02x\n", upper_limit);
+
+	/* If we have room to increase the bridge's subordinate number, */
+	if (bridge_to_fix->subordinate < upper_limit) {
+
+		/* use the highest number of the hidden bus, within limits */
+		unsigned char subordinate_to_assign =
+			min(cardbus_bridge->subordinate, upper_limit);
+
+		printk(KERN_INFO "Yenta: Raising subordinate bus# of parent "
+			"bus (#%02x) from #%02x to #%02x\n",
+			bridge_to_fix->number,
+			bridge_to_fix->subordinate, subordinate_to_assign);
+
+		/* Save the new subordinate in the bus struct of the bridge */
+		bridge_to_fix->subordinate = subordinate_to_assign;
+
+		/* and update the PCI config space with the new subordinate */
+		pci_write_config_byte(bridge_to_fix->self,
+			PCI_SUBORDINATE_BUS, bridge_to_fix->subordinate);
+	}
+}
+
 /*
  * Initialize a cardbus controller. Make sure we have a usable
  * interrupt, and that we can map the cardbus area. Fill in the
@@ -1120,6 +1191,8 @@ static int __devinit yenta_probe (struct
 	yenta_get_socket_capabilities(socket, isa_interrupts);
 	printk(KERN_INFO "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
 
+	yenta_fixup_parent_bridge(dev->subordinate);
+
 	/* Register it with the pcmcia layer.. */
 	ret = pcmcia_register_socket(&socket->socket);
 	if (ret == 0) {
-- 
1.4.0




More information about the linux-pcmcia mailing list