blob: f2ea73ff1a518f139d2881ab66b842026ae5def8 [file] [log] [blame]
Angel Ponsae593872020-04-04 18:50:57 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
Marc Jones24484842017-05-04 21:17:45 -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>
Richard Spiegel2bbc3dc2017-12-06 16:14:58 -07008#include <amdblocks/amd_pci_util.h>
Marc Jones24484842017-05-04 21:17:45 -06009#include <pc80/i8259.h>
Richard Spiegela800bdb2017-11-13 16:59:38 -070010#include <soc/amd_pci_int_defs.h>
Marc Jones24484842017-05-04 21:17:45 -060011
Aaron Durbin8dd40062017-11-03 11:50:14 -060012const struct pirq_struct *pirq_data_ptr;
13u32 pirq_data_size;
14const u8 *intr_data_ptr;
15const u8 *picr_data_ptr;
Marc Jones24484842017-05-04 21:17:45 -060016
17/*
18 * Read the FCH PCI_INTR registers 0xC00/0xC01 at a
19 * given index and a given PIC (0) or IOAPIC (1) mode
20 */
21u8 read_pci_int_idx(u8 index, int mode)
22{
23 outb((mode << 7) | index, PCI_INTR_INDEX);
24 return inb(PCI_INTR_DATA);
25}
26
27/*
28 * Write a value to the FCH PCI_INTR registers 0xC00/0xC01
29 * at a given index and PIC (0) or IOAPIC (1) mode
30 */
31void write_pci_int_idx(u8 index, int mode, u8 data)
32{
33 outb((mode << 7) | index, PCI_INTR_INDEX);
34 outb(data, PCI_INTR_DATA);
35}
36
37/*
38 * Write the FCH PCI_INTR registers 0xC00/0xC01 with values
39 * given in global variables intr_data and picr_data.
40 * These variables are defined in mainboard.c
41 */
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -060042void write_pci_int_table(void)
Marc Jones24484842017-05-04 21:17:45 -060043{
Richard Spiegel376dc822017-12-01 08:24:26 -070044 uint8_t byte;
45 size_t i, limit;
46 const struct irq_idx_name *idx_name;
Marc Jones24484842017-05-04 21:17:45 -060047
Richard Spiegel376dc822017-12-01 08:24:26 -070048 idx_name = sb_get_apic_reg_association(&limit);
Richard Spiegel6c2ab062017-12-18 09:52:42 -070049 if (picr_data_ptr == NULL || intr_data_ptr == NULL ||
50 idx_name == NULL) {
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -060051 printk(BIOS_ERR, "Warning: Can't write PCI_INTR 0xC00/0xC01"
52 " registers because\n"
Richard Spiegel6c2ab062017-12-18 09:52:42 -070053 "'mainboard_picr_data' or 'mainboard_intr_data'"
54 " or 'irq_association'\ntables are NULL\n");
Marc Jones24484842017-05-04 21:17:45 -060055 return;
56 }
57
58 /* PIC IRQ routine */
Richard Spiegel376dc822017-12-01 08:24:26 -070059 printk(BIOS_DEBUG, "PCI_INTR tables: Writing registers C00/C01 for"
60 " PCI IRQ routing:\n"
Richard Spiegele89d4442017-12-08 07:52:42 -070061 "PCI_INTR_INDEX\tname\t\t PIC mode"
Richard Spiegel376dc822017-12-01 08:24:26 -070062 "\tAPIC mode\n");
63 /*
64 * Iterate table idx_name, indexes outside the table are ignored
65 * (assumed not connected within the chip). For each iteration,
66 * get the register index "byte" and the name of the associated
67 * IRQ source for printing.
68 */
69 for (i = 0 ; i < limit; i++) {
70 byte = idx_name[i].index;
71 write_pci_int_idx(byte, 0, (u8) picr_data_ptr[byte]);
Richard Spiegele89d4442017-12-08 07:52:42 -070072 printk(BIOS_DEBUG, "0x%02X\t\t%-20s 0x%02X\t",
Richard Spiegel376dc822017-12-01 08:24:26 -070073 byte, idx_name[i].name,
74 read_pci_int_idx(byte, 0));
75 write_pci_int_idx(byte, 1, (u8) intr_data_ptr[byte]);
76 printk(BIOS_DEBUG, "0x%02X\n", read_pci_int_idx(byte, 1));
Marc Jones24484842017-05-04 21:17:45 -060077 }
78}
79
80/*
81 * Function to write the PCI config space Interrupt
82 * registers based on the values given in PCI_INTR
83 * table at I/O port 0xC00/0xC01
84 */
85void write_pci_cfg_irqs(void)
86{
Elyes HAOUAS0f5957a2018-05-22 10:58:50 +020087 struct device *dev = NULL; /* Our current device to route IRQs */
Richard Spiegel2b7cd1d2018-10-22 14:39:37 -070088 struct device *target_dev = NULL; /* the bridge a device may be
89 * connected to */
90 u16 int_pin = 0;
91 u16 target_pin = 0;
92 u16 int_line = 0;
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -060093 u16 pci_intr_idx = 0; /* Index into PCI_INTR table, 0xC00/0xC01 */
Richard Spiegel2b7cd1d2018-10-22 14:39:37 -070094 u16 devfn = 0;
Marc Jones24484842017-05-04 21:17:45 -060095 u32 i = 0;
Richard Spiegel376dc822017-12-01 08:24:26 -070096 size_t limit;
97 const struct irq_idx_name *idx_name;
Marc Jones24484842017-05-04 21:17:45 -060098
Richard Spiegel376dc822017-12-01 08:24:26 -070099 idx_name = sb_get_apic_reg_association(&limit);
Marc Jones24484842017-05-04 21:17:45 -0600100 if (pirq_data_ptr == NULL) {
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600101 printk(BIOS_WARNING, "Warning: Can't write PCI IRQ assignments"
102 " because 'mainboard_pirq_data' structure does"
103 " not exist\n");
Marc Jones24484842017-05-04 21:17:45 -0600104 return;
105 }
106
107 /* Populate the PCI cfg space header with the IRQ assignment */
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600108 printk(BIOS_DEBUG, "PCI_CFG IRQ: Write PCI config space IRQ"
109 " assignments\n");
Marc Jones24484842017-05-04 21:17:45 -0600110
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600111 for (dev = all_devices ; dev ; dev = dev->next) {
Marc Jones24484842017-05-04 21:17:45 -0600112 /*
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600113 * Step 1: Get INT_PIN and device structure to look for in the
Marc Jones24484842017-05-04 21:17:45 -0600114 * PCI_INTR table pirq_data
115 */
116 target_dev = NULL;
117 target_pin = get_pci_irq_pins(dev, &target_dev);
118 if (target_dev == NULL)
119 continue;
120
121 if (target_pin < 1)
122 continue;
123
124 /* Get the original INT_PIN for record keeping */
125 int_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN);
126 if (int_pin < 1 || int_pin > 4)
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600127 continue; /* Device has invalid INT_PIN - skip */
Marc Jones24484842017-05-04 21:17:45 -0600128
Marc Jones24484842017-05-04 21:17:45 -0600129 devfn = target_dev->path.pci.devfn;
130
131 /*
132 * Step 2: Use the INT_PIN and DevFn number to find the PCI_INTR
133 * register (0xC00) index for this device
134 */
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600135 pci_intr_idx = 0xbad; /* Will check to make sure it changed */
136 for (i = 0 ; i < pirq_data_size ; i++) {
Marc Jones24484842017-05-04 21:17:45 -0600137 if (pirq_data_ptr[i].devfn != devfn)
138 continue;
139
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600140 /* PIN_A is idx0 in pirq_data array but 1 in PCI reg */
Marc Jones24484842017-05-04 21:17:45 -0600141 pci_intr_idx = pirq_data_ptr[i].PIN[target_pin - 1];
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600142 printk(BIOS_SPEW, "\tFound this device in pirq_data"
143 " table entry %d\n", i);
Marc Jones24484842017-05-04 21:17:45 -0600144 break;
145 }
146
147 /*
148 * Step 3: Make sure we got a valid index and use it to get
149 * the IRQ number from the PCI_INTR register table
150 */
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600151 if (pci_intr_idx == 0xbad) {
152 /* Not on a bridge or in pirq_data table, skip it */
153 printk(BIOS_SPEW, "PCI Devfn (0x%x) not found in"
154 " pirq_data table\n", devfn);
Marc Jones24484842017-05-04 21:17:45 -0600155 continue;
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600156 } else if (pci_intr_idx == 0x1f) {
157 /* Index found is not defined */
158 printk(BIOS_SPEW, "Got index 0x1F (Not Connected),"
159 " perhaps this device was"
160 " defined wrong?\n");
Marc Jones24484842017-05-04 21:17:45 -0600161 continue;
Richard Spiegel376dc822017-12-01 08:24:26 -0700162 }
163 /*
164 * Find the name associated with register [pci_intr_idx]
165 * and print information.
166 */
167 for (i = 0; i < limit; i++) {
168 if (idx_name[i].index == pci_intr_idx)
169 break;
170 }
171 if (i == limit) {
172 printk(BIOS_SPEW, "Got register index 0x%02x"
173 " undefined in table irq_idx_name,\n"
174 " perhaps this device was"
175 " defined wrong?\n", pci_intr_idx);
Marc Jones24484842017-05-04 21:17:45 -0600176 continue;
177 }
178
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600179 /* Find the value to program into the INT_LINE register from
180 * the PCI_INTR registers
181 */
Marc Jones24484842017-05-04 21:17:45 -0600182 int_line = read_pci_int_idx(pci_intr_idx, 0);
183 if (int_line == PIRQ_NC) { /* The IRQ found is disabled */
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600184 printk(BIOS_SPEW, "Got IRQ 0x1F (disabled), perhaps"
185 " this device was defined wrong?\n");
Marc Jones24484842017-05-04 21:17:45 -0600186 continue;
187 }
188
189 /*
190 * Step 4: Program the INT_LINE register in this device's
191 * PCI config space with the IRQ number we found in step 3
192 * and make it Level Triggered
193 */
194 pci_write_config8(dev, PCI_INTERRUPT_LINE, int_line);
195
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600196 /* Set IRQ to level triggered since used by a PCI device */
Marc Jones24484842017-05-04 21:17:45 -0600197 i8259_configure_irq_trigger(int_line, IRQ_LEVEL_TRIGGERED);
198
199 /*
200 * Step 5: Print out debug info and move on to next device
201 */
202 printk(BIOS_SPEW, "\tOrig INT_PIN\t: %d (%s)\n",
203 int_pin, pin_to_str(int_pin));
Marc Jones24484842017-05-04 21:17:45 -0600204 printk(BIOS_SPEW, "\tPCI_INTR idx\t: 0x%02x (%s)\n"
Richard Spiegel376dc822017-12-01 08:24:26 -0700205 "\tINT_LINE\t: 0x%X (IRQ %d)\n",
206 pci_intr_idx, idx_name[i].name,
207 int_line, int_line);
Marc Jones24484842017-05-04 21:17:45 -0600208 } /* for (dev = all_devices) */
Marshall Dawsonf3dc71e2017-06-14 16:22:07 -0600209 printk(BIOS_DEBUG, "PCI_CFG IRQ: Finished writing PCI config space"
210 " IRQ assignments\n");
Marc Jones24484842017-05-04 21:17:45 -0600211}