blob: 33d4033bfec6954cd2ed49b9d77e577388464871 [file] [log] [blame]
Angel Pons182dbde2020-04-02 23:49:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
Dave Frodin2093c4f2014-06-13 08:12:48 -06003
4#include <console/console.h>
5#include <device/pci.h>
6#include <arch/io.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +02007#include <device/pci_ops.h>
Dave Frodin2093c4f2014-06-13 08:12:48 -06008#include "amd_pci_util.h"
9#include <pc80/i8259.h>
10#include "amd_pci_int_defs.h"
11#include "amd_pci_int_types.h"
12
Dave Frodin2093c4f2014-06-13 08:12:48 -060013const struct pirq_struct * pirq_data_ptr = NULL;
14u32 pirq_data_size = 0;
Elyes HAOUAS251279c2018-07-08 12:41:56 +020015const u8 *intr_data_ptr = NULL;
16const u8 *picr_data_ptr = NULL;
Dave Frodin2093c4f2014-06-13 08:12:48 -060017
18/*
19 * Read the FCH PCI_INTR registers 0xC00/0xC01 at a
20 * given index and a given PIC (0) or IOAPIC (1) mode
21 */
22u8 read_pci_int_idx(u8 index, int mode)
23{
24 outb((mode << 7) | index, PCI_INTR_INDEX);
25 return inb(PCI_INTR_DATA);
26}
27
28/*
29 * Write a value to the FCH PCI_INTR registers 0xC00/0xC01
30 * at a given index and PIC (0) or IOAPIC (1) mode
31 */
32void write_pci_int_idx(u8 index, int mode, u8 data)
33{
34 outb((mode << 7) | index, PCI_INTR_INDEX);
35 outb(data, PCI_INTR_DATA);
36}
37
38/*
39 * Write the FCH PCI_INTR registers 0xC00/0xC01 with values
40 * given in global variables intr_data and picr_data.
41 * These variables are defined in mainboard.c
42 */
43void write_pci_int_table (void)
44{
45 u8 byte;
46
Elyes HAOUASba28e8d2016-08-31 19:22:16 +020047 if (picr_data_ptr == NULL || intr_data_ptr == NULL){
Dave Frodin2093c4f2014-06-13 08:12:48 -060048 printk(BIOS_ERR, "Warning: Can't write PCI_INTR 0xC00/0xC01 registers because\n"
49 "'mainboard_picr_data' or 'mainboard_intr_data' tables are NULL\n");
50 return;
51 }
52
53 /* PIC IRQ routine */
54 printk(BIOS_DEBUG, "PCI_INTR tables: Writing registers C00/C01 for PIC mode PCI IRQ routing:\n"
55 "\tPCI_INTR_INDEX\t\tPCI_INTR_DATA\n");
56 for (byte = 0; byte < FCH_INT_TABLE_SIZE; byte++) {
57 if (intr_types[byte]) {
58 write_pci_int_idx(byte, 0, (u8) picr_data_ptr[byte]);
59 printk(BIOS_DEBUG, "\t0x%02X %s\t: 0x%02X\n",
60 byte, intr_types[byte], read_pci_int_idx(byte, 0));
61 }
62 }
63
64 /* APIC IRQ routine */
65 printk(BIOS_DEBUG, "PCI_INTR tables: Writing registers C00/C01 for APIC mode PCI IRQ routing:\n"
66 "\tPCI_INTR_INDEX\t\tPCI_INTR_DATA\n");
67 for (byte = 0; byte < FCH_INT_TABLE_SIZE; byte++) {
68 if (intr_types[byte]) {
69 write_pci_int_idx(byte, 1, (u8) intr_data_ptr[byte]);
70 printk(BIOS_DEBUG, "\t0x%02X %s\t: 0x%02X\n",
71 byte, intr_types[byte], read_pci_int_idx(byte, 1));
72 }
73 }
74}
75
76/*
77 * Function to write the PCI config space Interrupt
78 * registers based on the values given in PCI_INTR
79 * table at I/O port 0xC00/0xC01
80 */
81void write_pci_cfg_irqs(void)
82{
Elyes HAOUASee424e52018-05-19 15:03:17 +020083 struct device *dev = NULL; /* Our current device to route IRQs to */
84 struct device *target_dev = NULL; /* The bridge that a device may be connected to */
Dave Frodin2093c4f2014-06-13 08:12:48 -060085 u16 int_pin = 0; /* Value of the INT_PIN register 0x3D */
86 u16 target_pin = 0; /* Pin we will search our tables for */
87 u16 int_line = 0; /* IRQ number read from PCI_INTR table and programmed to INT_LINE reg 0x3C */
88 u16 pci_intr_idx = 0; /* Index into PCI_INTR table, 0xC00/0xC01 */
Dave Frodin2093c4f2014-06-13 08:12:48 -060089 u16 devfn = 0; /* A PCI Device and Function number */
90 u8 bridged_device = 0; /* This device is on a PCI bridge */
91 u32 i = 0;
92
93 if (pirq_data_ptr == NULL) {
94 printk(BIOS_WARNING, "Warning: Can't write PCI IRQ assignments because"
95 " 'mainboard_pirq_data' structure does not exist\n");
96 return;
97 }
98
99 /* Populate the PCI cfg space header with the IRQ assignment */
100 printk(BIOS_DEBUG, "PCI_CFG IRQ: Write PCI config space IRQ assignments\n");
101
102 for (dev = all_devices; dev; dev = dev->next) {
103 /*
104 * Step 1: Get the INT_PIN and device structure to look for in the
105 * PCI_INTR table pirq_data
106 */
107 target_dev = NULL;
108 target_pin = get_pci_irq_pins(dev, &target_dev);
109 if (target_dev == NULL)
110 continue;
111
112 if (target_pin < 1)
113 continue;
114
115 /* Get the original INT_PIN for record keeping */
116 int_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN);
117 if (int_pin < 1 || int_pin > 4)
118 continue; /* Device has invalid INT_PIN so skip it */
119
Dave Frodin2093c4f2014-06-13 08:12:48 -0600120 devfn = target_dev->path.pci.devfn;
121
122 /*
123 * Step 2: Use the INT_PIN and DevFn number to find the PCI_INTR
124 * register (0xC00) index for this device
125 */
126 pci_intr_idx = 0xBAD; /* Will check to make sure it changed */
127 for (i = 0; i < pirq_data_size; i++) {
128 if (pirq_data_ptr[i].devfn != devfn)
129 continue;
130
131 /* PIN_A is index 0 in pirq_data array but 1 in PCI cfg reg */
132 pci_intr_idx = pirq_data_ptr[i].PIN[target_pin - 1];
133 printk(BIOS_SPEW, "\tFound this device in pirq_data table entry %d\n", i);
134 break;
135 }
136
137 /*
138 * Step 3: Make sure we got a valid index and use it to get
139 * the IRQ number from the PCI_INTR register table
140 */
141 if (pci_intr_idx == 0xBAD) { /* Not on a bridge or in pirq_data table, skip it */
142 printk(BIOS_SPEW, "PCI Devfn (0x%x) not found in pirq_data table\n", devfn);
143 continue;
144 } else if (pci_intr_idx == 0x1F) { /* Index found is not defined */
145 printk(BIOS_SPEW, "Got index 0x1F (Not Connected), perhaps this device was defined wrong?\n");
146 continue;
147 } else if (pci_intr_idx >= FCH_INT_TABLE_SIZE) { /* Index out of bounds */
148 printk(BIOS_ERR, "%s: got 0xC00/0xC01 table index 0x%x, max is 0x%x\n",
149 __func__, pci_intr_idx, FCH_INT_TABLE_SIZE);
150 continue;
151 }
152
153 /* Find the value to program into the INT_LINE register from the PCI_INTR registers */
154 int_line = read_pci_int_idx(pci_intr_idx, 0);
155 if (int_line == PIRQ_NC) { /* The IRQ found is disabled */
156 printk(BIOS_SPEW, "Got IRQ 0x1F (disabled), perhaps this device was defined wrong?\n");
157 continue;
158 }
159
160 /*
161 * Step 4: Program the INT_LINE register in this device's
162 * PCI config space with the IRQ number we found in step 3
163 * and make it Level Triggered
164 */
165 pci_write_config8(dev, PCI_INTERRUPT_LINE, int_line);
166
167 /* Set this IRQ to level triggered since it is used by a PCI device */
168 i8259_configure_irq_trigger(int_line, IRQ_LEVEL_TRIGGERED);
169
170 /*
171 * Step 5: Print out debug info and move on to next device
172 */
173 printk(BIOS_SPEW, "\tOrig INT_PIN\t: %d (%s)\n",
174 int_pin, pin_to_str(int_pin));
175 if (bridged_device)
176 printk(BIOS_SPEW, "\tSwizzled to\t: %d (%s)\n",
177 target_pin, pin_to_str(target_pin));
178 printk(BIOS_SPEW, "\tPCI_INTR idx\t: 0x%02x (%s)\n"
179 "\tINT_LINE\t: 0x%X (IRQ %d)\n",
180 pci_intr_idx, intr_types[pci_intr_idx], int_line, int_line);
181 } /* for (dev = all_devices) */
182 printk(BIOS_DEBUG, "PCI_CFG IRQ: Finished writing PCI config space IRQ assignments\n");
183}