[coreboot-gerrit] Patch set updated for coreboot: 6b974a8 PCI IRQs: Swizzle PCI IRQs for PCI bridges

Martin Roth (martin.roth@se-eng.com) gerrit at coreboot.org
Mon May 19 21:52:33 CEST 2014


Martin Roth (martin.roth at se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/5734

-gerrit

commit 6b974a851643301d0f4523878e1f7b5e7cf425f2
Author: Mike Loptien <mike.loptien at se-eng.com>
Date:   Mon May 12 21:46:31 2014 -0600

    PCI IRQs: Swizzle PCI IRQs for PCI bridges
    
    The PCI Specification states that devices that implement
    a bridge and a secondary bus must swizzle (rotate) the
    interrupt pins according to the table below:
    	Child Dev #     Child PIN       Parent PIN
    	0,4,8,12...     A/B/C/D         A/B/C/D
    	1,5,9,13...     A/B/C/D         B/C/D/A
    	2,6,10,14..     A/B/C/D         C/D/A/B
    	3,7,11,15..     A/B/C/D         D/A/B/C
    
    Which is also described by this equation:
    	PIN_parent = (Pin_child + Dev_child) % 4
    
    When a device is found and its bus number is greater than 0,
    it is on a bridge and needs to be swizzled.  Following the
    string of parents up to the root bus and swizzling as we go
    gives us the desired swizzling result.  When BIOS_SPEW is
    defined, it will print out each step of the swizzling process.
    
    Change-Id: Icafeadd01983282c86e25f560c831c9482c74e68
    Signed-off-by: Martin Roth <gaumless at gmail.com>
---
 src/device/pci_device.c  | 141 +++++++++++++++++++++++++++++++++++++++++++++++
 src/include/device/pci.h |   3 +
 2 files changed, 144 insertions(+)

diff --git a/src/device/pci_device.c b/src/device/pci_device.c
index f09fcaa..588e297 100644
--- a/src/device/pci_device.c
+++ b/src/device/pci_device.c
@@ -1303,6 +1303,147 @@ unsigned int pci_domain_scan_bus(device_t dev, unsigned int max)
 	return max;
 }
 
+/**
+ * Take an INT_PIN number (0, 1 - 4) and convert
+ * it to a string ("NO PIN", "PIN A" - "PIN D")
+ *
+ * @param pin PCI Interrupt Pin number (0, 1 - 4)
+ * @return A string ("NO PIN", "PIN A" - "PIN D") corresponding to the pin number
+ */
+const char * pin_to_str(int pin)
+{
+	const char * str[5] = {
+			"NO PIN",
+			"PIN A",
+			"PIN B",
+			"PIN C",
+			"PIN D",
+	};
+
+	if (pin >= 0 || pin <= 4)
+		return str[pin];
+	else
+		return "Invalid PIN, not 0 - 4";
+}
+
+/**
+ * Get the PCI INT_PIN swizzle for a device defined as:
+ *   pin_parent = (pin_child + devn_child) % 4
+ *   where PIN A = 0 ... PIN_D = 3
+ *
+ * Given a PCI device structure 'dev', find the interrupt pin
+ * that will be triggered on its parent bridge device when
+ * generating an interrupt.  For example: Device 1:3.2 may
+ * use INT_PIN A but will trigger PIN D on its parent bridge
+ * device.  In this case, this function will return 4 (PIN D).
+ *
+ * @param dev A PCI device structure to swizzle interrupt pins for
+ * @param *parent_bdg The PCI device structure for the bridge
+ *        device 'dev' is attached to
+ * @return The interrupt pin number (1 - 4) that 'dev' will
+ *         trigger when generating an interrupt
+ */
+int swizzle_irq_pins(device_t dev, device_t *parent_bdg)
+{
+	device_t parent;	/* Our current devices parent device */
+	device_t child;		/* The child device of the parent */
+	int parent_bus = 0;	/* Parent Bus number */
+	int parent_devfn = 0;	/* Parent Device and Function number */
+	int child_devfn = 0;	/* Child Device and Function number */
+	int swizzled_pin = 0;	/* Pin swizzled across a bridge */
+
+	/* Start with PIN A = 0 ... D = 3 */
+	swizzled_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN) - 1;
+
+	/* While our current device has parent devices */
+	child = dev;
+	for (parent = child->bus->dev; parent; parent = parent->bus->dev) {
+		parent_bus = parent->bus->secondary;
+		parent_devfn = parent->path.pci.devfn;
+		child_devfn = child->path.pci.devfn;
+
+		/* Swizzle the INT_PIN for any bridges not on root bus */
+			swizzled_pin = (PCI_SLOT(child_devfn) + swizzled_pin) % 4;
+			printk(BIOS_SPEW, "\tWith INT_PIN swizzled to %s\n"
+					"\tAttached to bridge device %01X:%02Xh.%02Xh\n",
+					pin_to_str(swizzled_pin + 1), parent_bus,
+					PCI_SLOT(parent_devfn), PCI_FUNC(parent_devfn));
+
+		/* Continue until we find the root bus */
+		if (parent_bus > 0) {
+			/* We will go on to the next parent so this parent becomes the child */
+			child = parent;
+			continue;
+		} else {
+			/* Found the root bridge device, fill in the structure and exit */
+			*parent_bdg = parent;
+			break;
+		}
+	}
+
+	/* End with PIN A = 1 ... D = 4 */
+	return swizzled_pin + 1;
+}
+
+/**
+ * Given a device structure 'dev', find its interrupt pin
+ * and its parent bridge 'parent_bdg' device structure.
+ * If it is behind a bridge, it will return the interrupt
+ * pin number (1 - 4) of the parent bridge that the device
+ * interrupt pin has been swizzled to, otherwise it will
+ * return the interrupt pin that is programmed into the
+ * PCI config space of the target device.  If 'dev' is
+ * behind a bridge, it will fill in 'parent_bdg' with the
+ * device structure of the bridge it is behind, otherwise
+ * it will copy 'dev' into 'parent_bdg'.
+ *
+ * @param dev A PCI device structure to swizzle interrupt pins for.
+ * @param *parent_bdg The PCI device structure for the bridge
+ *        device 'dev' is attached to.
+ * @return The interrupt pin number (1 - 4) that 'dev' will
+ *         trigger when generating an interrupt.
+ */
+int get_irq_pins(device_t dev, device_t *parent_bdg)
+{
+	int bus = 0;	/* The bus this device is on */
+	int devfn = 0;	/* This device's device and function numbers */
+	int int_pin = 0;	/* Interrupt pin used by the device */
+	int target_pin = 0;	/* Interrupt pin we want to assign an IRQ to */
+
+	bus   = dev->bus->secondary;
+	devfn = dev->path.pci.devfn;
+
+	/* Make sure this device is enabled and has a valid INT_PIN */
+	if (!(dev->enabled && (dev->path.type == DEVICE_PATH_PCI)))
+		return -1;
+
+	/* Get the interrupt pin that this dev uses, only 1-4 are allowed */
+	int_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN);
+	if (int_pin < 1 || int_pin > 4)
+		return -1;
+
+	printk(BIOS_SPEW, "PCI IRQ: Found device %01X:%02Xh.%02Xh using %s\n",
+			bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pin_to_str(int_pin));
+
+	/* If this device is on a bridge, swizzle its INT_PIN */
+	if (bus) {
+		/* Swizzle its INT_PINs */
+		target_pin = swizzle_irq_pins(dev, parent_bdg);
+
+		/* Make sure the swizzle returned valid structures */
+		if (parent_bdg == NULL) {
+			printk(BIOS_WARNING, "Warning: Could not find parent bridge for this device!\n");
+			return -2;
+		}
+	} else {	/* Device is not behind a bridge */
+		target_pin = int_pin;	/* Return its own interrupt pin */
+		*parent_bdg = dev;		/* Return its own structure */
+	}
+
+	/* Target pin is the interrupt pin we want to assign an IRQ to */
+	return target_pin;
+}
+
 #if CONFIG_PC80_SYSTEM
 /**
  * Assign IRQ numbers.
diff --git a/src/include/device/pci.h b/src/include/device/pci.h
index 5594d29..f4dcd4d 100644
--- a/src/include/device/pci.h
+++ b/src/include/device/pci.h
@@ -82,6 +82,9 @@ void pci_dev_set_subsystem(device_t dev, unsigned vendor, unsigned device);
 void pci_dev_init(struct device *dev);
 unsigned int pci_match_simple_dev(device_t dev, pci_devfn_t sdev);
 
+const char * pin_to_str(int pin);
+int get_irq_pins(device_t dev, device_t *parent_bdg);
+int swizzle_irq_pins(device_t dev, device_t *parent_bdg);
 void pci_assign_irqs(unsigned bus, unsigned slot,
 		     const unsigned char pIntAtoD[4]);
 



More information about the coreboot-gerrit mailing list