summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Loptien <mike.loptien@se-eng.com>2014-06-06 15:16:29 -0600
committerMike Loptien <mike.loptien@se-eng.com>2014-06-11 17:07:50 +0200
commitc93a75a5ab067f86104028b74d92fc54cb939cd5 (patch)
tree8f91538cc2b45d7df3d049c443d66e8617c6a641
parentce740c474c3590dcb0da184d7663adf1f1d78ea8 (diff)
downloadcoreboot-c93a75a5ab067f86104028b74d92fc54cb939cd5.tar.xz
AMD/CIMx: Add functions for AMD PCI IRQ routing
The PCI_INTR table is an Index/Data pair of I/O ports 0xC00 and 0xC01. This table is responsible for physically routing IRQs to the PIC and IOAPIC. The settings given in this table are chipset and mainboard dependent, so the table values will reside in the mainboard.c file. This allows for a system to uniquely set its IRQ routing. The function to write the PCI_INTR table resides in cimx_util.c because the indices into the table have the same definitions for all SBx00 FCH chipsets. The next piece is a function that will read the PCI_INTR table and program the INT_LINE and INT_PIN registers in PCI config space appropriately. This function will read a devices' INT_PIN register, which is always hardcoded to a value if it uses hardware interrupts. It then uses this value, along with the device and function numbers to determine an index into the PCI_INTR table. It will read the table and program the corresponding value into the PCI config space register 0x3C, INT_LINE. Finally, it will set this IRQ number to LEVEL_TRIGGERED on the PIC because it is a PCI device interrupt and the must be level triggered. For example, the SB800 USB EHCI device 0:18.2 has an INT_PIN value hardcoded to 2. This corresponds to PIN B. On the Persimmon mainboard, I want the USB device to use IRQ 11. I will program the PCI_INTR table at index 0x31 (this USB device index) to 11. This function will then read the INT_PIN register, read the PCI_INTR table, and then program the INT_LINE register with the value it read. It will then set the IRQ on the PIC to LEVEL_TRIGGERED by writing a 1 to I/O port 0x4D1 at bit position 4. Also, the SB700 has slightly different register definitions than the newer SB800 and SB900 so it needs its own set of #defines for the pci_intr registers. Only the Persimmon mainboard is adapted to this change as an example for other mainboards. Change-Id: I6de858289a17fa1e1abacf6328ea5099be74b1d6 Signed-off-by: Mike Loptien <mike.loptien@se-eng.com> Reviewed-on: http://review.coreboot.org/5877 Tested-by: build bot (Jenkins) Reviewed-by: Kyösti Mälkki <kyosti.malkki@gmail.com> Reviewed-by: Edward O'Callaghan <eocallaghan@alterapraxis.com>
-rw-r--r--src/mainboard/amd/persimmon/mainboard.c104
-rw-r--r--src/northbridge/amd/agesa/family14/pci_devs.h54
-rw-r--r--src/southbridge/amd/cimx/cimx_util.c201
-rw-r--r--src/southbridge/amd/cimx/cimx_util.h100
-rw-r--r--src/southbridge/amd/cimx/sb800/late.c25
-rw-r--r--src/southbridge/amd/cimx/sb800/pci_devs.h105
6 files changed, 582 insertions, 7 deletions
diff --git a/src/mainboard/amd/persimmon/mainboard.c b/src/mainboard/amd/persimmon/mainboard.c
index b17bc6a0be..746b6c2772 100644
--- a/src/mainboard/amd/persimmon/mainboard.c
+++ b/src/mainboard/amd/persimmon/mainboard.c
@@ -2,6 +2,7 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2011 Advanced Micro Devices, Inc.
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,16 +24,112 @@
#include <arch/io.h>
#include <cpu/x86/msr.h>
#include <device/pci_def.h>
-#include <southbridge/amd/sb800/sb800.h>
+#include <southbridge/amd/cimx/cimx_util.h>
#include <arch/acpi.h>
#include "BiosCallOuts.h"
#include <cpu/amd/agesa/s3_resume.h>
#include <cpu/amd/mtrr.h>
#include "SBPLATFORM.h"
+#include <southbridge/amd/cimx/sb800/pci_devs.h>
+#include <northbridge/amd/agesa/family14/pci_devs.h>
void set_pcie_reset(void);
void set_pcie_dereset(void);
+/***********************************************************
+ * These arrays set up the FCH PCI_INTR registers 0xC00/0xC01.
+ * This table is responsible for physically routing the PIC and
+ * IOAPIC IRQs to the different PCI devices on the system. It
+ * is read and written via registers 0xC00/0xC01 as an
+ * Index/Data pair. These values are chipset and mainboard
+ * dependent and should be updated accordingly.
+ *
+ * These values are used by the PCI configuration space,
+ * MP Tables. TODO: Make ACPI use these values too.
+ *
+ * The Persimmon PCI INTA/B/C/D pins are connected to
+ * FCH pins INTE/F/G/H on the schematic so these need
+ * to be routed as well.
+ */
+static const u8 mainboard_picr_data[FCH_INT_TABLE_SIZE] = {
+ /* INTA# - INTH# */
+ [0x00] = 0x0A,0x0B,0x0A,0x0B,0x0A,0x0B,0x0A,0x0B,
+ /* Misc-nil,0,1,2, INT from Serial irq */
+ [0x08] = 0x00,0xF0,0x00,0x00,0x1F,0x1F,0x1F,0x1F,
+ /* SCI, SMBUS0, ASF, HDA, FC, GEC, PerfMon */
+ [0x10] = 0x1F,0x1F,0x1F,0x0A,0x1F,0x1F,0x1F,
+ /* IMC INT0 - 5 */
+ [0x20] = 0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,
+ /* USB Devs 18/19/20/22 INTA-C */
+ [0x30] = 0x0A,0x0B,0x0A,0x0B,0x0A,0x0B,0x0A,
+ /* IDE, SATA */
+ [0x40] = 0x0B,0x0B,
+ /* GPPInt0 - 3 */
+ [0x50] = 0x0A,0x0B,0x0A,0x0B
+};
+
+static const u8 mainboard_intr_data[FCH_INT_TABLE_SIZE] = {
+ /* INTA# - INTH# */
+ [0x00] = 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
+ /* Misc-nil,0,1,2, INT from Serial irq */
+ [0x08] = 0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F,
+ /* SCI, SMBUS0, ASF, HDA, FC, GEC, PerMon */
+ [0x10] = 0x09,0x1F,0x1F,0x10,0x1F,0x12,0x1F,
+ /* IMC INT0 - 5 */
+ [0x20] = 0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,
+ /* USB Devs 18/19/22/20 INTA-C */
+ [0x30] = 0x12,0x11,0x12,0x11,0x12,0x11,0x12,
+ /* IDE, SATA */
+ [0x40] = 0x11,0x13,
+ /* GPPInt0 - 3 */
+ [0x50] = 0x10,0x11,0x12,0x13
+};
+
+/*
+ * This table defines the index into the picr/intr_data
+ * tables for each device. Any enabled device and slot
+ * that uses hardware interrupts should have an entry
+ * in this table to define its index into the FCH
+ * PCI_INTR register 0xC00/0xC01. This index will define
+ * the interrupt that it should use. Putting PIRQ_A into
+ * the PIN A index for a device will tell that device to
+ * use PIC IRQ 10 if it uses PIN A for its hardware INT.
+ */
+/*
+ * Persimmon has PCI slot INTA/B/C/D connected to PIRQE/F/G/H
+ * but because PCI INT_PIN swizzling isnt implemented to match
+ * the IDSEL (dev 3) of the slot, the table is adjusted for the
+ * swizzle and INTA is connected to PIRQH so PINA/B/C/D on
+ * off-chip devices should get mapped to PIRQH/E/F/G.
+ */
+static const struct pirq_struct mainboard_pirq_data[] = {
+ /* {PCI_devfn, {PIN A, PIN B, PIN C, PIN D}}, */
+ {GFX_DEVFN, {PIRQ_A, PIRQ_B, PIRQ_NC, PIRQ_NC}}, /* VGA: 01.0 */
+ {NB_PCIE_PORT1_DEVFN, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D}}, /* NIC: 04.0 */
+ {NB_PCIE_PORT3_DEVFN, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D}}, /* PCIe bdg:06.0 */
+ {SATA_DEVFN, {PIRQ_SATA, PIRQ_NC, PIRQ_NC, PIRQ_NC}}, /* SATA: 11.0 */
+ {OHCI1_DEVFN, {PIRQ_OHCI1, PIRQ_NC, PIRQ_NC, PIRQ_NC}}, /* OHCI1: 12.0 */
+ {EHCI1_DEVFN, {PIRQ_NC, PIRQ_EHCI1, PIRQ_NC, PIRQ_NC}}, /* EHCI1: 12.2 */
+ {OHCI2_DEVFN, {PIRQ_OHCI2, PIRQ_NC, PIRQ_NC, PIRQ_NC}}, /* OHCI2: 13.0 */
+ {EHCI2_DEVFN, {PIRQ_NC, PIRQ_EHCI2, PIRQ_NC, PIRQ_NC}}, /* EHCI2: 13.2 */
+ {SMBUS_DEVFN, {PIRQ_SMBUS, PIRQ_NC, PIRQ_NC, PIRQ_NC}}, /* SMBUS: 14.0 */
+ {IDE_DEVFN, {PIRQ_NC, PIRQ_IDE, PIRQ_NC, PIRQ_NC}}, /* IDE: 14.1 */
+ {HDA_DEVFN, {PIRQ_HDA, PIRQ_NC, PIRQ_NC, PIRQ_NC}}, /* HDA: 14.2 */
+ {SB_PCI_PORT_DEVFN, {PIRQ_H, PIRQ_E, PIRQ_F, PIRQ_G}}, /* PCI bdg: 14.4 */
+ {OHCI4_DEVFN, {PIRQ_NC, PIRQ_NC, PIRQ_OHCI4, PIRQ_NC}}, /* OHCI4: 14.5 */
+ {OHCI3_DEVFN, {PIRQ_OHCI3, PIRQ_NC, PIRQ_NC, PIRQ_NC}}, /* OHCI3: 16.0 */
+ {EHCI3_DEVFN, {PIRQ_NC, PIRQ_EHCI3, PIRQ_NC, PIRQ_NC}}, /* EHCI3: 16.2 */
+};
+
+/* PIRQ Setup */
+static void pirq_setup(void)
+{
+ pirq_data_ptr = mainboard_pirq_data;
+ pirq_data_size = sizeof(mainboard_pirq_data) / sizeof(struct pirq_struct);
+ intr_data_ptr = mainboard_intr_data;
+ picr_data_ptr = mainboard_picr_data;
+}
+
/**
* TODO
* SB CIMx callback
@@ -59,7 +156,7 @@ static void mainboard_enable(device_t dev)
/*
* The mainboard is the first place that we get control in ramstage. Check
- * for S3 resume and call the approriate AGESA/CIMx resume functions.
+ * for S3 resume and call the appropriate AGESA/CIMx resume functions.
*/
#if CONFIG_HAVE_ACPI_RESUME
acpi_slp_type = acpi_get_sleep_type();
@@ -82,6 +179,9 @@ static void mainboard_enable(device_t dev)
*/
pm_iowrite(0x29, 0x80);
pm_iowrite(0x28, 0x61);
+
+ /* Initialize the PIRQ data structures for consumption */
+ pirq_setup();
}
struct chip_operations mainboard_ops = {
diff --git a/src/northbridge/amd/agesa/family14/pci_devs.h b/src/northbridge/amd/agesa/family14/pci_devs.h
new file mode 100644
index 0000000000..a9e95d1f94
--- /dev/null
+++ b/src/northbridge/amd/agesa/family14/pci_devs.h
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _AMD_FAM14_PCI_DEVS_H_
+#define _AMD_FAM14_PCI_DEVS_H_
+
+#define BUS0 0
+
+/* Graphics and Display */
+#define GFX_DEV 0x1
+#define GFX_FUNC 0
+# define GFX_DEVFN PCI_DEVFN(GFX_DEV,GFX_FUNC)
+
+/* PCI Ports */
+#define PCI_PORT_DEV 0x14
+#define PCI_PORT_FUNC 4
+# define PCI_PORT_DEVID 0x4384
+# define PCI_PORT_DEVFN PCI_DEVFN(PCI_PORT_DEV,PCI_PORT_FUNC)
+
+/* PCIe Ports */
+#define NB_PCIE_PORT1_DEV 0x4
+#define NB_PCIE_PORT2_DEV 0x5
+#define NB_PCIE_PORT3_DEV 0x6
+#define NB_PCIE_PORT4_DEV 0x7
+#define NB_PCIE_PORT5_DEV 0x8
+#define NB_PCIE_FUNC 0
+# define NB_PCIE_PORT1_DEVID 0x1512
+# define NB_PCIE_PORT2_DEVID 0x1513
+# define NB_PCIE_PORT3_DEVID 0x1514
+# define NB_PCIE_PORT4_DEVID 0x1515
+# define NB_PCIE_PORT5_DEVID 0x1516
+# define NB_PCIE_PORT1_DEVFN PCI_DEVFN(NB_PCIE_PORT1_DEV,NB_PCIE_FUNC)
+# define NB_PCIE_PORT2_DEVFN PCI_DEVFN(NB_PCIE_PORT2_DEV,NB_PCIE_FUNC)
+# define NB_PCIE_PORT3_DEVFN PCI_DEVFN(NB_PCIE_PORT3_DEV,NB_PCIE_FUNC)
+# define NB_PCIE_PORT4_DEVFN PCI_DEVFN(NB_PCIE_PORT4_DEV,NB_PCIE_FUNC)
+# define NB_PCIE_PORT5_DEVFN PCI_DEVFN(NB_PCIE_PORT5_DEV,NB_PCIE_FUNC)
+
+#endif /* _AMD_FAM14_PCI_DEVS_H_ */
diff --git a/src/southbridge/amd/cimx/cimx_util.c b/src/southbridge/amd/cimx/cimx_util.c
index a3c6ad7d12..5d9276f78b 100644
--- a/src/southbridge/amd/cimx/cimx_util.c
+++ b/src/southbridge/amd/cimx/cimx_util.c
@@ -2,6 +2,7 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,8 +17,208 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+
+#include <console/console.h>
+#include <device/pci.h>
#include <arch/io.h>
+#include <string.h>
#include "cimx_util.h"
+#include <pc80/i8259.h>
+
+#ifndef __PRE_RAM__
+#if IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_CIMX_SB800) || \
+ IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_CIMX_SB900)
+const char * intr_types[] = {
+ [0x00] = "INTA#\t", "INTB#\t", "INTC#\t", "INTD#\t", "INTE#\t", "INTF#\t", "INTG#\t", "INTH#\t",
+ [0x08] = "Misc\t", "Misc0\t", "Misc1\t", "Misc2\t", "Ser IRQ INTA", "Ser IRQ INTB", "Ser IRQ INTC", "Ser IRQ INTD",
+ [0x10] = "SCI\t", "SMBUS0\t", "ASF\t", "HDA\t", "FC\t\t", "GEC\t", "PerMon\t",
+ [0x20] = "IMC INT0\t", "IMC INT1\t", "IMC INT2\t", "IMC INT3\t", "IMC INT4\t", "IMC INT5\t",
+ [0x30] = "Dev18.0 INTA", "Dev18.2 INTB", "Dev19.0 INTA", "Dev19.2 INTB", "Dev22.0 INTA", "Dev22.2 INTB", "Dev20.5 INTC",
+ [0x40] = "IDE\t", "SATA\t",
+ [0x50] = "GPPInt0\t", "GPPInt1\t", "GPPInt2\t", "GPPInt3\t"
+};
+#elif IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_CIMX_SB700)
+const char * intr_types[] = {
+ [0x00] = "INTA#\t", "INTB#\t", "INTC#\t", "INTD#\t",
+ [0x04] = "ACPI\t", "SMBUS\t", "RSVD\t", "RSVD\t", "RSVD\t",
+ [0x09] = "INTE#\t", "INTF#\t", "INTG#\t", "INTH#\t",
+};
+#endif
+
+const struct pirq_struct * pirq_data_ptr = NULL;
+u32 pirq_data_size = 0;
+const u8 * intr_data_ptr = NULL;
+const u8 * picr_data_ptr = NULL;
+
+/*
+ * Read the FCH PCI_INTR registers 0xC00/0xC01 at a
+ * given index and a given PIC (0) or IOAPIC (1) mode
+ */
+u8 read_pci_int_idx(u8 index, int mode)
+{
+ outb((mode << 7) | index, PCI_INTR_INDEX);
+ return inb(PCI_INTR_DATA);
+}
+
+/*
+ * Write a value to the FCH PCI_INTR registers 0xC00/0xC01
+ * at a given index and PIC (0) or IOAPIC (1) mode
+ */
+void write_pci_int_idx(u8 index, int mode, u8 data)
+{
+ outb((mode << 7) | index, PCI_INTR_INDEX);
+ outb(data, PCI_INTR_DATA);
+}
+
+/*
+ * Write the FCH PCI_INTR registers 0xC00/0xC01 with values
+ * given in global variables intr_data and picr_data.
+ * These variables are defined in mainboard.c
+ */
+void write_pci_int_table (void)
+{
+ u8 byte;
+
+ if(picr_data_ptr == NULL || intr_data_ptr == NULL){
+ printk(BIOS_ERR, "Warning: Can't write PCI_INTR 0xC00/0xC01 registers because\n"
+ "'mainboard_picr_data' or 'mainboard_intr_data' tables are NULL\n");
+ return;
+ }
+
+ /* PIC IRQ routine */
+ printk(BIOS_DEBUG, "PCI_INTR tables: Writing registers C00/C01 for PIC mode PCI IRQ routing:\n"
+ "\tPCI_INTR_INDEX\t\tPCI_INTR_DATA\n");
+ for (byte = 0; byte < FCH_INT_TABLE_SIZE; byte++) {
+ if (intr_types[byte]) {
+ write_pci_int_idx(byte, 0, (u8) picr_data_ptr[byte]);
+ printk(BIOS_DEBUG, "\t0x%02X %s\t: 0x%02X\n",
+ byte, intr_types[byte], read_pci_int_idx(byte, 0));
+ }
+ }
+
+ /* APIC IRQ routine */
+ printk(BIOS_DEBUG, "PCI_INTR tables: Writing registers C00/C01 for APIC mode PCI IRQ routing:\n"
+ "\tPCI_INTR_INDEX\t\tPCI_INTR_DATA\n");
+ for (byte = 0; byte < FCH_INT_TABLE_SIZE; byte++) {
+ if (intr_types[byte]) {
+ write_pci_int_idx(byte, 1, (u8) intr_data_ptr[byte]);
+ printk(BIOS_DEBUG, "\t0x%02X %s\t: 0x%02X\n",
+ byte, intr_types[byte], read_pci_int_idx(byte, 1));
+ }
+ }
+}
+
+/*
+ * Function to write the PCI config space Interrupt
+ * registers based on the values given in PCI_INTR
+ * table at I/O port 0xC00/0xC01
+ */
+void write_pci_cfg_irqs(void)
+{
+ device_t dev = NULL; /* Our current device to route IRQs to */
+ device_t target_dev = NULL; /* The bridge that a device may be connected to */
+ u16 int_pin = 0; /* Value of the INT_PIN register 0x3D */
+ u16 target_pin = 0; /* Pin we will search our tables for */
+ u16 int_line = 0; /* IRQ number read from PCI_INTR table and programmed to INT_LINE reg 0x3C */
+ u16 pci_intr_idx = 0; /* Index into PCI_INTR table, 0xC00/0xC01 */
+ u8 bus = 0; /* A PCI Device Bus number */
+ u16 devfn = 0; /* A PCI Device and Function number */
+ u8 bridged_device = 0; /* This device is on a PCI bridge */
+ u32 i = 0;
+
+ if (pirq_data_ptr == NULL) {
+ printk(BIOS_WARNING, "Warning: Can't write PCI IRQ assignments because"
+ " 'mainboard_pirq_data' structure does not exist\n");
+ return;
+ }
+
+ /* Populate the PCI cfg space header with the IRQ assignment */
+ printk(BIOS_DEBUG, "PCI_CFG IRQ: Write PCI config space IRQ assignments\n");
+
+ for (dev = all_devices; dev; dev = dev->next) {
+ /*
+ * Step 1: Get the INT_PIN and device structure to look for in the
+ * PCI_INTR table pirq_data
+ */
+ target_dev = NULL;
+ target_pin = get_pci_irq_pins(dev, &target_dev);
+ if (target_dev == NULL)
+ continue;
+
+ if (target_pin < 1)
+ continue;
+
+ /* Get the original INT_PIN for record keeping */
+ int_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN);
+ if (int_pin < 1 || int_pin > 4)
+ continue; /* Device has invalid INT_PIN so skip it */
+
+ bus = target_dev->bus->secondary;
+ devfn = target_dev->path.pci.devfn;
+
+ /*
+ * Step 2: Use the INT_PIN and DevFn number to find the PCI_INTR
+ * register (0xC00) index for this device
+ */
+ pci_intr_idx = 0xBAD; /* Will check to make sure it changed */
+ for (i = 0; i < pirq_data_size; i++) {
+ if (pirq_data_ptr[i].devfn != devfn)
+ continue;
+
+ /* PIN_A is index 0 in pirq_data array but 1 in PCI cfg reg */
+ pci_intr_idx = pirq_data_ptr[i].PIN[target_pin - 1];
+ printk(BIOS_SPEW, "\tFound this device in pirq_data table entry %d\n", i);
+ break;
+ }
+
+ /*
+ * Step 3: Make sure we got a valid index and use it to get
+ * the IRQ number from the PCI_INTR register table
+ */
+ if (pci_intr_idx == 0xBAD) { /* Not on a bridge or in pirq_data table, skip it */
+ printk(BIOS_SPEW, "PCI Devfn (0x%x) not found in pirq_data table\n", devfn);
+ continue;
+ } else if (pci_intr_idx == 0x1F) { /* Index found is not defined */
+ printk(BIOS_SPEW, "Got index 0x1F (Not Connected), perhaps this device was defined wrong?\n");
+ continue;
+ } else if (pci_intr_idx >= FCH_INT_TABLE_SIZE) { /* Index out of bounds */
+ printk(BIOS_ERR, "%s: got 0xC00/0xC01 table index 0x%x, max is 0x%x\n",
+ __func__, pci_intr_idx, FCH_INT_TABLE_SIZE);
+ continue;
+ }
+
+ /* Find the value to program into the INT_LINE register from the PCI_INTR registers */
+ int_line = read_pci_int_idx(pci_intr_idx, 0);
+ if (int_line == PIRQ_NC) { /* The IRQ found is disabled */
+ printk(BIOS_SPEW, "Got IRQ 0x1F (disabled), perhaps this device was defined wrong?\n");
+ continue;
+ }
+
+ /*
+ * Step 4: Program the INT_LINE register in this device's
+ * PCI config space with the IRQ number we found in step 3
+ * and make it Level Triggered
+ */
+ pci_write_config8(dev, PCI_INTERRUPT_LINE, int_line);
+
+ /* Set this IRQ to level triggered since it is used by a PCI device */
+ i8259_configure_irq_trigger(int_line, IRQ_LEVEL_TRIGGERED);
+
+ /*
+ * Step 5: Print out debug info and move on to next device
+ */
+ printk(BIOS_SPEW, "\tOrig INT_PIN\t: %d (%s)\n",
+ int_pin, pin_to_str(int_pin));
+ if (bridged_device)
+ printk(BIOS_SPEW, "\tSwizzled to\t: %d (%s)\n",
+ target_pin, pin_to_str(target_pin));
+ printk(BIOS_SPEW, "\tPCI_INTR idx\t: 0x%02x (%s)\n"
+ "\tINT_LINE\t: 0x%X (IRQ %d)\n",
+ pci_intr_idx, intr_types[pci_intr_idx], int_line, int_line);
+ } /* for (dev = all_devices) */
+ printk(BIOS_DEBUG, "PCI_CFG IRQ: Finished writing PCI config space IRQ assignments\n");
+}
+#endif /* __PRE_RAM__ */
static void pmio_write_index(u16 port_base, u8 reg, u8 value)
{
diff --git a/src/southbridge/amd/cimx/cimx_util.h b/src/southbridge/amd/cimx/cimx_util.h
index 1e806ba407..841325143f 100644
--- a/src/southbridge/amd/cimx/cimx_util.h
+++ b/src/southbridge/amd/cimx/cimx_util.h
@@ -2,6 +2,7 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,15 +23,104 @@
#include <stdint.h>
-/* Power management index/data registers */
-#define PM_INDEX 0xcd6
-#define PM_DATA 0xcd7
-#define PM2_INDEX 0xcd0
-#define PM2_DATA 0xcd1
+/*
+ * PIRQ and device routing - these define the index
+ * into the FCH PCI_INTR 0xC00/0xC01 interrupt
+ * routing table
+ */
+#if IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_CIMX_SB800) || \
+ IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_CIMX_SB900)
+#define FCH_INT_TABLE_SIZE 0x54
+#define PIRQ_NC 0x1F /* Not Used */
+#define PIRQ_A 0x00 /* INT A */
+#define PIRQ_B 0x01 /* INT B */
+#define PIRQ_C 0x02 /* INT C */
+#define PIRQ_D 0x03 /* INT D */
+#define PIRQ_E 0x04 /* INT E */
+#define PIRQ_F 0x05 /* INT F */
+#define PIRQ_G 0x06 /* INT G */
+#define PIRQ_H 0x07 /* INT H */
+#define PIRQ_MISC 0x08 /* Miscellaneous IRQ Settings - See FCH Spec */
+#define PIRQ_MISC0 0x09 /* Miscellaneous0 IRQ Settings */
+#define PIRQ_MISC1 0x0A /* Miscellaneous1 IRQ Settings */
+#define PIRQ_MISC2 0x0B /* Miscellaneous2 IRQ Settings */
+#define PIRQ_SIRQA 0x0C /* Serial IRQ INTA */
+#define PIRQ_SIRQB 0x0D /* Serial IRQ INTA */
+#define PIRQ_SIRQC 0x0E /* Serial IRQ INTA */
+#define PIRQ_SIRQD 0x0F /* Serial IRQ INTA */
+#define PIRQ_SCI 0x10 /* SCI IRQ */
+#define PIRQ_SMBUS 0x11 /* SMBUS 14h.0 */
+#define PIRQ_ASF 0x12 /* ASF */
+#define PIRQ_HDA 0x13 /* HDA 14h.2 */
+#define PIRQ_FC 0x14 /* FC */
+#define PIRQ_GEC 0x15 /* GEC */
+#define PIRQ_PMON 0x16 /* Performance Monitor */
+#define PIRQ_IMC0 0x20 /* IMC INT0 */
+#define PIRQ_IMC1 0x21 /* IMC INT1 */
+#define PIRQ_IMC2 0x22 /* IMC INT2 */
+#define PIRQ_IMC3 0x23 /* IMC INT3 */
+#define PIRQ_IMC4 0x24 /* IMC INT4 */
+#define PIRQ_IMC5 0x25 /* IMC INT5 */
+#define PIRQ_OHCI1 0x30 /* USB OHCI 12h.0 */
+#define PIRQ_EHCI1 0x31 /* USB EHCI 12h.2 */
+#define PIRQ_OHCI2 0x32 /* USB OHCI 13h.0 */
+#define PIRQ_EHCI2 0x33 /* USB EHCI 13h.2 */
+#define PIRQ_OHCI3 0x34 /* USB OHCI 16h.0 */
+#define PIRQ_EHCI3 0x35 /* USB EHCI 16h.2 */
+#define PIRQ_OHCI4 0x36 /* USB OHCI 14h.5 */
+#define PIRQ_IDE 0x40 /* IDE 14h.1 */
+#define PIRQ_SATA 0x41 /* SATA 11h.0 */
+#define PIRQ_GPP0 0x50 /* GPP INT 0 */
+#define PIRQ_GPP1 0x51 /* GPP INT 1 */
+#define PIRQ_GPP2 0x52 /* GPP INT 2 */
+#define PIRQ_GPP3 0x53 /* GPP INT 3 */
+#elif IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_CIMX_SB700)
+#define FCH_INT_TABLE_SIZE 0xD
+#define PIRQ_NC 0x1F /* Not Used */
+#define PIRQ_A 0x00 /* INT A */
+#define PIRQ_B 0x01 /* INT B */
+#define PIRQ_C 0x02 /* INT C */
+#define PIRQ_D 0x03 /* INT D */
+#define PIRQ_ACPI 0x04 /* ACPI */
+#define PIRQ_SMBUS 0x05 /* SMBUS */
+/* Index 6, 7, 8 are all reserved */
+#define PIRQ_E 0x09 /* INT E */
+#define PIRQ_F 0x0A /* INT F */
+#define PIRQ_G 0x0B /* INT G */
+#define PIRQ_H 0x0C /* INT H */
+#endif
+
+/* FCH index/data registers */
+#define BIOSRAM_INDEX 0xcd4
+#define BIOSRAM_DATA 0xcd5
+#define PM_INDEX 0xcd6
+#define PM_DATA 0xcd7
+#define PM2_INDEX 0xcd0
+#define PM2_DATA 0xcd1
+#define PCI_INTR_INDEX 0xc00
+#define PCI_INTR_DATA 0xc01
void pm_iowrite(u8 reg, u8 value);
u8 pm_ioread(u8 reg);
void pm2_iowrite(u8 reg, u8 value);
u8 pm2_ioread(u8 reg);
+#ifndef __PRE_RAM__
+
+struct pirq_struct {
+u8 devfn;
+u8 PIN[4]; /* PINA/B/C/D are index 0/1/2/3 */
+};
+
+extern const struct pirq_struct * pirq_data_ptr;
+extern u32 pirq_data_size;
+extern const u8 * intr_data_ptr;
+extern const u8 * picr_data_ptr;
+
+u8 read_pci_int_idx(u8 index, int mode);
+void write_pci_int_idx(u8 index, int mode, u8 data);
+void write_pci_cfg_irqs(void);
+void write_pci_int_table (void);
+#endif /* __PRE_RAM */
+
#endif /* CIMX_UTIL_H */
diff --git a/src/southbridge/amd/cimx/sb800/late.c b/src/southbridge/amd/cimx/sb800/late.c
index 40b422bc83..f4c5fd44e2 100644
--- a/src/southbridge/amd/cimx/sb800/late.c
+++ b/src/southbridge/amd/cimx/sb800/late.c
@@ -2,6 +2,7 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2011 Advanced Micro Devices, Inc.
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +22,7 @@
#include <device/device.h> /* device_t */
#include <device/pci.h> /* device_operations */
#include <device/pci_ids.h>
+#include <bootstate.h>
#include <arch/ioapic.h>
#include <device/smbus.h> /* smbus_bus_operations */
#include <pc80/mc146818rtc.h>
@@ -36,6 +38,7 @@
#include "sb_cimx.h" /* AMD CIMX wrapper entries */
#include "smbus.h"
#include "fan.h"
+#include <southbridge/amd/cimx/cimx_util.h>
/*implement in mainboard.c*/
void set_pcie_reset(void);
@@ -331,6 +334,28 @@ void sb_After_Pci_Restore_Init(void)
AmdSbDispatcher(sb_config);
}
+/*
+ * Update the PCI devices with a valid IRQ number
+ * that is set in the mainboard PCI_IRQ structures.
+ */
+static void set_pci_irqs(void *unused)
+{
+ /* Write PCI_INTR regs 0xC00/0xC01 */
+ write_pci_int_table();
+
+ /* Write IRQs for all devicetree enabled devices */
+ write_pci_cfg_irqs();
+}
+
+/*
+ * Hook this function into the PCI state machine
+ * on entry into BS_DEV_ENABLE.
+ */
+BOOT_STATE_INIT_ENTRIES(pci_irq_update) = {
+ BOOT_STATE_INIT_ENTRY(BS_DEV_ENABLE, BS_ON_ENTRY,
+ set_pci_irqs, NULL),
+};
+
/**
* @brief SB Cimx entry point sbBeforePciInit wrapper
*/
diff --git a/src/southbridge/amd/cimx/sb800/pci_devs.h b/src/southbridge/amd/cimx/sb800/pci_devs.h
new file mode 100644
index 0000000000..1ecff314b4
--- /dev/null
+++ b/src/southbridge/amd/cimx/sb800/pci_devs.h
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CIMX_SB00_PCI_DEVS_H_
+#define _CIMX_SB00_PCI_DEVS_H_
+
+#define BUS0 0
+
+/* SATA */
+#define SATA_DEV 0x11
+#define SATA_FUNC 0
+# define SATA_IDE_DEVID 0x4390
+# define AHCI_DEVID 0x4391
+# define RAID_DEVID 0x4392
+# define RAID5_DEVID 0x4393
+# define SATA_DEVFN PCI_DEVFN(SATA_DEV,SATA_FUNC)
+
+/* OHCI */
+#define OHCI1_DEV 0x12
+#define OHCI1_FUNC 0
+#define OHCI2_DEV 0x13
+#define OHCI2_FUNC 0
+#define OHCI3_DEV 0x16
+#define OHCI3_FUNC 0
+#define OHCI4_DEV 0x14
+#define OHCI4_FUNC 5
+# define OHCI_DEVID 0x4397
+# define OHCI1_DEVFN PCI_DEVFN(OHCI1_DEV,OHCI1_FUNC)
+# define OHCI2_DEVFN PCI_DEVFN(OHCI2_DEV,OHCI2_FUNC)
+# define OHCI3_DEVFN PCI_DEVFN(OHCI3_DEV,OHCI3_FUNC)
+# define OHCI4_DEVFN PCI_DEVFN(OHCI4_DEV,OHCI4_FUNC)
+
+/* EHCI */
+#define EHCI1_DEV 0x12
+#define EHCI1_FUNC 2
+#define EHCI2_DEV 0x13
+#define EHCI2_FUNC 2
+#define EHCI3_DEV 0x16
+#define EHCI3_FUNC 2
+# define EHCI_DEVID 0x4396
+# define EHCI1_DEVFN PCI_DEVFN(EHCI1_DEV,EHCI1_FUNC)
+# define EHCI2_DEVFN PCI_DEVFN(EHCI2_DEV,EHCI2_FUNC)
+# define EHCI3_DEVFN PCI_DEVFN(EHCI3_DEV,EHCI3_FUNC)
+
+/* IDE */
+#define IDE_DEV 0x14
+#define IDE_FUNC 1
+# define IDE_DEVID 0x439C
+# define IDE_DEVFN PCI_DEVFN(IDE_DEV,IDE_FUNC)
+
+/* HD Audio */
+#define HDA_DEV 0x14
+#define HDA_FUNC 2
+# define HDA_DEVID 0x4383
+# define HDA_DEVFN PCI_DEVFN(HDA_DEV,HDA_FUNC)
+
+/* PCI Ports */
+#define SB_PCI_PORT_DEV 0x14
+#define SB_PCI_PORT_FUNC 4
+# define SB_PCI_PORT_DEVID 0x4384
+# define SB_PCI_PORT_DEVFN PCI_DEVFN(SB_PCI_PORT_DEV,SB_PCI_PORT_FUNC)
+
+/* PCIe Ports */
+#define SB_PCIE_DEV 0x15
+#define SB_PCIE_PORT1_FUNC 0
+#define SB_PCIE_PORT2_FUNC 1
+#define SB_PCIE_PORT3_FUNC 2
+#define SB_PCIE_PORT4_FUNC 3
+# define SB_PCIE_PORT1_DEVID 0x43A0
+# define SB_PCIE_PORT2_DEVID 0x43A1
+# define SB_PCIE_PORT3_DEVID 0x43A2
+# define SB_PCIE_PORT4_DEVID 0x43A3
+# define SB_PCIE_PORT1_DEVFN PCI_DEVFN(SB_PCIE_DEV,SB_PCIE_PORT1_FUNC)
+# define SB_PCIE_PORT2_DEVFN PCI_DEVFN(SB_PCIE_DEV,SB_PCIE_PORT2_FUNC)
+# define SB_PCIE_PORT3_DEVFN PCI_DEVFN(SB_PCIE_DEV,SB_PCIE_PORT3_FUNC)
+# define SB_PCIE_PORT4_DEVFN PCI_DEVFN(SB_PCIE_DEV,SB_PCIE_PORT4_FUNC)
+
+/* Fusion Controller Hub */
+#define PCU_DEV 0x14
+#define LPC_DEV PCU_DEV
+#define LPC_FUNC 3
+#define SMBUS_DEV 0x14
+#define SMBUS_FUNC 0
+# define LPC_DEVID 0x439D
+# define SMBUS_DEVID 0x4385
+# define LPC_DEVFN PCI_DEVFN(LPC_DEV,LPC_FUNC)
+# define SMBUS_DEVFN PCI_DEVFN(SMBUS_DEV,SMBUS_FUNC)
+
+#endif /* _CIMX_SB800_PCI_DEVS_H_ */