[coreboot-gerrit] Patch set updated for coreboot: 12523e2 PCI IRQs: Swizzle PCI IRQs for PCI bridges

Martin Roth (gaumless@gmail.com) gerrit at coreboot.org
Thu May 29 23:10:09 CEST 2014


Martin Roth (gaumless at gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/5734

-gerrit

commit 12523e206e456290c7f7012dfa24627a63f828cc
Author: Mike Loptien <loptienm at gmail.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  | 151 +++++++++++++++++++++++++++++++++++++++++++++++
 src/include/device/pci.h |   2 +
 2 files changed, 153 insertions(+)

diff --git a/src/device/pci_device.c b/src/device/pci_device.c
index f09fcaa..3a54c2d 100644
--- a/src/device/pci_device.c
+++ b/src/device/pci_device.c
@@ -12,6 +12,7 @@
  * (Written by Yinghai Lu <yhlu at tyan.com> for Tyan)
  * Copyright (C) 2005-2009 coresystems GmbH
  * (Written by Stefan Reinauer <stepan at coresystems.de> for coresystems GmbH)
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
  */
 
 /*
@@ -1303,6 +1304,156 @@ 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 corresponding to the pin number or "Invalid"
+ */
+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 + 1
+ *   where PIN A = 1 ... PIN_D = 4
+ *
+ * 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
+ */
+static int swizzle_irq_pins(device_t dev, device_t *parent_bridge)
+{
+	device_t parent;	/* Our current device's parent device */
+	device_t child;		/* The child device of the parent */
+	uint8_t parent_bus = 0;		/* Parent Bus number */
+	uint16_t parent_devfn = 0;	/* Parent Device and Function number */
+	uint16_t child_devfn = 0;	/* Child Device and Function number */
+	uint8_t 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_bridge = 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 get 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.
+ *         Errors: -1 is returned if the device is not enabled
+ *                 -2 is returned if a parent bridge could not be found.
+ */
+int get_pci_irq_pins(device_t dev, device_t *parent_bdg)
+{
+	uint8_t bus = 0;	/* The bus this device is on */
+	uint16_t devfn = 0;	/* This device's device and function numbers */
+	uint8_t int_pin = 0;	/* Interrupt pin used by the device */
+	uint8_t target_pin = 0;	/* Interrupt pin we want to assign an IRQ to */
+
+	/* Make sure this device is enabled */
+	if (!(dev->enabled && (dev->path.type == DEVICE_PATH_PCI)))
+		return -1;
+
+	bus = dev->bus->secondary;
+	devfn = dev->path.pci.devfn;
+
+	/* Get and validate the interrupt pin used. 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:%02X.%02X 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..8175970 100644
--- a/src/include/device/pci.h
+++ b/src/include/device/pci.h
@@ -82,6 +82,8 @@ 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_pci_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