soc/intel/braswell: Correct configuration of interrupts

The level/edge mode of PIRQ is not configured and i8259 PIC not initialized.
Add calls to:
- i8259_configure_irq_trigger()
- setup_i8259()
- write_pci_config_irqs()
to correct the configuration of interrupts.

BUG=N/A
TEST=Intel CherryHill CRB

Change-Id: I128cb35dd0e348a9cd9fb162651e0aa2b7e4a3ef
Signed-off-by: Frans Hendriks <fhendriks@eltan.com>
Reviewed-on: https://review.coreboot.org/c/29419
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Michał Żygowski <michal.zygowski@3mdeb.com>
This commit is contained in:
Frans Hendriks 2018-12-10 12:38:16 +01:00 committed by Patrick Georgi
parent 3e8504a325
commit 9348413c61
2 changed files with 121 additions and 3 deletions

View File

@ -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

View File

@ -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();
}