summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/soc/intel/braswell/include/soc/irq.h1
-rw-r--r--src/soc/intel/braswell/southcluster.c123
2 files changed, 121 insertions, 3 deletions
diff --git a/src/soc/intel/braswell/include/soc/irq.h b/src/soc/intel/braswell/include/soc/irq.h
index a2327550a9..f9d3700be0 100644
--- a/src/soc/intel/braswell/include/soc/irq.h
+++ b/src/soc/intel/braswell/include/soc/irq.h
@@ -155,6 +155,7 @@
#define PIRQ_PIC_IRQ12 0xc
#define PIRQ_PIC_IRQ14 0xe
#define PIRQ_PIC_IRQ15 0xf
+#define PIRQ_PIC_UNKNOWN_UNUSED 0xff
/* Overloaded term, but these values determine the per device route. */
#define PIRQA 0
diff --git a/src/soc/intel/braswell/southcluster.c b/src/soc/intel/braswell/southcluster.c
index 6ea2ea9aae..418035f493 100644
--- a/src/soc/intel/braswell/southcluster.c
+++ b/src/soc/intel/braswell/southcluster.c
@@ -27,6 +27,7 @@
#include <device/pci.h>
#include <device/pci_ids.h>
#include <pc80/i8254.h>
+#include <pc80/i8259.h>
#include <romstage_handoff.h>
#include <soc/acpi.h>
#include <soc/iomap.h>
@@ -78,6 +79,112 @@ static void sc_add_mmio_resources(struct device *dev)
#define LPC_DEFAULT_IO_RANGE_LOWER 0
#define LPC_DEFAULT_IO_RANGE_UPPER 0x1000
+/*
+ * Write PCI config space IRQ assignments. PCI devices have the INT_LINE
+ * (0x3C) and INT_PIN (0x3D) registers which report interrupt routing
+ * information to operating systems and drivers. The INT_PIN register is
+ * generally read only and reports which interrupt pin A - D it uses. The
+ * INT_LINE register is configurable and reports which IRQ (generally the
+ * PIC IRQs 1 - 15) it will use. This needs to take interrupt pin swizzling
+ * on devices that are downstream on a PCI bridge into account.
+ *
+ * This function will loop through all enabled PCI devices and program the
+ * INT_LINE register with the correct PIC IRQ number for the INT_PIN that it
+ * uses. It then configures each interrupt in the pic to be level triggered.
+ */
+static void write_pci_config_irqs(void)
+{
+ struct device *irq_dev;
+ struct device *targ_dev;
+ uint8_t int_line = 0;
+ uint8_t original_int_pin = 0;
+ uint8_t new_int_pin = 0;
+ uint16_t current_bdf = 0;
+ uint16_t parent_bdf = 0;
+ uint8_t pirq = 0;
+ uint8_t device_num = 0;
+ const struct soc_irq_route *ir = &global_soc_irq_route;
+
+ if (ir == NULL) {
+ printk(BIOS_WARNING, "Warning: Can't write PCI IRQ assignments"
+ " because 'global_braswell_irq_route' structure does"
+ " not exist\n");
+ return;
+ }
+
+ /*
+ * Loop through all enabled devices and program their
+ * INT_LINE, INT_PIN registers from values taken from
+ * the Interrupt Route registers in the ILB
+ */
+ printk(BIOS_DEBUG, "PCI_CFG IRQ: Write PIRQ assignments\n");
+ for (irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) {
+
+ if ((irq_dev->path.type != DEVICE_PATH_PCI) ||
+ (!irq_dev->enabled))
+ continue;
+
+ current_bdf = irq_dev->path.pci.devfn |
+ irq_dev->bus->secondary << 8;
+
+ /*
+ * Step 1: Get the INT_PIN and device structure to look for
+ * in the pirq_data table defined in the mainboard directory.
+ */
+ targ_dev = NULL;
+ new_int_pin = get_pci_irq_pins(irq_dev, &targ_dev);
+ if (targ_dev == NULL || new_int_pin < 1)
+ continue;
+
+ /* Get the original INT_PIN for record keeping */
+ original_int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN);
+
+ parent_bdf = targ_dev->path.pci.devfn
+ | targ_dev->bus->secondary << 8;
+ device_num = PCI_SLOT(parent_bdf);
+
+ if (ir->pcidev[device_num] == 0) {
+ printk(BIOS_WARNING,
+ "Warning: PCI Device %d does not have an IRQ "
+ "entry, skipping it\n", device_num);
+ continue;
+ }
+
+ /* Find the PIRQ that is attached to the INT_PIN */
+ pirq = (ir->pcidev[device_num] >> ((new_int_pin - 1) * 4))
+ & 0x7;
+
+ /* Get the INT_LINE this device/function will use */
+ int_line = ir->pic[pirq];
+
+ if (int_line != PIRQ_PIC_IRQDISABLE) {
+ /* Set this IRQ to level triggered */
+ i8259_configure_irq_trigger(int_line,
+ IRQ_LEVEL_TRIGGERED);
+ /* Set the Interrupt Line register */
+ pci_write_config8(irq_dev, PCI_INTERRUPT_LINE,
+ int_line);
+ } else {
+ /*
+ * Set the Interrupt line register as 'unknown' or
+ * 'unused'
+ */
+ pci_write_config8(irq_dev, PCI_INTERRUPT_LINE,
+ PIRQ_PIC_UNKNOWN_UNUSED);
+ }
+
+ printk(BIOS_SPEW, "\tINT_PIN\t\t: %d (%s)\n",
+ original_int_pin, pin_to_str(original_int_pin));
+ if (parent_bdf != current_bdf)
+ printk(BIOS_SPEW, "\tSwizzled to\t: %d (%s)\n",
+ new_int_pin, pin_to_str(new_int_pin));
+ printk(BIOS_SPEW, "\tPIRQ\t\t: %c\n"
+ "\tINT_LINE\t: 0x%X (IRQ %d)\n",
+ 'A' + pirq, int_line, int_line);
+ }
+ printk(BIOS_DEBUG, "PCI_CFG IRQ: Finished writing PIRQ assignments\n");
+}
+
static inline int io_range_in_default(int base, int size)
{
/* Does it start above the range? */
@@ -155,7 +262,6 @@ static void sc_init(struct device *dev)
const unsigned long pr_base = ILB_BASE_ADDRESS + 0x08;
const unsigned long ir_base = ILB_BASE_ADDRESS + 0x20;
void *gen_pmcon1 = (void *)(PMC_BASE_ADDRESS + GEN_PMCON1);
- void *actl = (void *)(ILB_BASE_ADDRESS + ACTL);
const struct soc_irq_route *ir = &global_soc_irq_route;
struct soc_intel_braswell_config *config = dev->chip_info;
@@ -172,8 +278,13 @@ static void sc_init(struct device *dev)
write16((void *)(ir_base + i*sizeof(ir->pcidev[i])),
ir->pcidev[i]);
- /* Route SCI to IRQ9 */
- write32(actl, (read32(actl) & ~SCIS_MASK) | SCIS_IRQ9);
+ /* Interrupt 9 should be level triggered (SCI) */
+ i8259_configure_irq_trigger(9, 1);
+
+ for (i = 0; i < NUM_PIRQS; i++) {
+ if (ir->pic[i])
+ i8259_configure_irq_trigger(ir->pic[i], 1);
+ }
if (config->disable_slp_x_stretch_sus_fail) {
printk(BIOS_DEBUG, "Disabling slp_x stretching.\n");
@@ -184,6 +295,12 @@ static void sc_init(struct device *dev)
read32(gen_pmcon1) & ~DIS_SLP_X_STRCH_SUS_UP);
}
+ /* Write IRQ assignments to PCI config space */
+ write_pci_config_irqs();
+
+ /* Initialize i8259 pic */
+ setup_i8259();
+
/* Initialize i8254 timers */
setup_i8254();
}