blob: 9f2cc5c1ee66445c7a679158f35b29a3b2c8e6d0 [file] [log] [blame]
Dave Frodin2093c4f2014-06-13 08:12:48 -06001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2014 Sage Electronic Engineering, LLC.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <console/console.h>
21#include <device/pci.h>
22#include <arch/io.h>
23#include <string.h>
24#include "amd_pci_util.h"
25#include <pc80/i8259.h>
26#include "amd_pci_int_defs.h"
27#include "amd_pci_int_types.h"
28
29#ifndef __PRE_RAM__
30
31const struct pirq_struct * pirq_data_ptr = NULL;
32u32 pirq_data_size = 0;
33const u8 * intr_data_ptr = NULL;
34const u8 * picr_data_ptr = NULL;
35
36/*
37 * Read the FCH PCI_INTR registers 0xC00/0xC01 at a
38 * given index and a given PIC (0) or IOAPIC (1) mode
39 */
40u8 read_pci_int_idx(u8 index, int mode)
41{
42 outb((mode << 7) | index, PCI_INTR_INDEX);
43 return inb(PCI_INTR_DATA);
44}
45
46/*
47 * Write a value to the FCH PCI_INTR registers 0xC00/0xC01
48 * at a given index and PIC (0) or IOAPIC (1) mode
49 */
50void write_pci_int_idx(u8 index, int mode, u8 data)
51{
52 outb((mode << 7) | index, PCI_INTR_INDEX);
53 outb(data, PCI_INTR_DATA);
54}
55
56/*
57 * Write the FCH PCI_INTR registers 0xC00/0xC01 with values
58 * given in global variables intr_data and picr_data.
59 * These variables are defined in mainboard.c
60 */
61void write_pci_int_table (void)
62{
63 u8 byte;
64
65 if(picr_data_ptr == NULL || intr_data_ptr == NULL){
66 printk(BIOS_ERR, "Warning: Can't write PCI_INTR 0xC00/0xC01 registers because\n"
67 "'mainboard_picr_data' or 'mainboard_intr_data' tables are NULL\n");
68 return;
69 }
70
71 /* PIC IRQ routine */
72 printk(BIOS_DEBUG, "PCI_INTR tables: Writing registers C00/C01 for PIC mode PCI IRQ routing:\n"
73 "\tPCI_INTR_INDEX\t\tPCI_INTR_DATA\n");
74 for (byte = 0; byte < FCH_INT_TABLE_SIZE; byte++) {
75 if (intr_types[byte]) {
76 write_pci_int_idx(byte, 0, (u8) picr_data_ptr[byte]);
77 printk(BIOS_DEBUG, "\t0x%02X %s\t: 0x%02X\n",
78 byte, intr_types[byte], read_pci_int_idx(byte, 0));
79 }
80 }
81
82 /* APIC IRQ routine */
83 printk(BIOS_DEBUG, "PCI_INTR tables: Writing registers C00/C01 for APIC mode PCI IRQ routing:\n"
84 "\tPCI_INTR_INDEX\t\tPCI_INTR_DATA\n");
85 for (byte = 0; byte < FCH_INT_TABLE_SIZE; byte++) {
86 if (intr_types[byte]) {
87 write_pci_int_idx(byte, 1, (u8) intr_data_ptr[byte]);
88 printk(BIOS_DEBUG, "\t0x%02X %s\t: 0x%02X\n",
89 byte, intr_types[byte], read_pci_int_idx(byte, 1));
90 }
91 }
92}
93
94/*
95 * Function to write the PCI config space Interrupt
96 * registers based on the values given in PCI_INTR
97 * table at I/O port 0xC00/0xC01
98 */
99void write_pci_cfg_irqs(void)
100{
101 device_t dev = NULL; /* Our current device to route IRQs to */
102 device_t target_dev = NULL; /* The bridge that a device may be connected to */
103 u16 int_pin = 0; /* Value of the INT_PIN register 0x3D */
104 u16 target_pin = 0; /* Pin we will search our tables for */
105 u16 int_line = 0; /* IRQ number read from PCI_INTR table and programmed to INT_LINE reg 0x3C */
106 u16 pci_intr_idx = 0; /* Index into PCI_INTR table, 0xC00/0xC01 */
107 u8 bus = 0; /* A PCI Device Bus number */
108 u16 devfn = 0; /* A PCI Device and Function number */
109 u8 bridged_device = 0; /* This device is on a PCI bridge */
110 u32 i = 0;
111
112 if (pirq_data_ptr == NULL) {
113 printk(BIOS_WARNING, "Warning: Can't write PCI IRQ assignments because"
114 " 'mainboard_pirq_data' structure does not exist\n");
115 return;
116 }
117
118 /* Populate the PCI cfg space header with the IRQ assignment */
119 printk(BIOS_DEBUG, "PCI_CFG IRQ: Write PCI config space IRQ assignments\n");
120
121 for (dev = all_devices; dev; dev = dev->next) {
122 /*
123 * Step 1: Get the INT_PIN and device structure to look for in the
124 * PCI_INTR table pirq_data
125 */
126 target_dev = NULL;
127 target_pin = get_pci_irq_pins(dev, &target_dev);
128 if (target_dev == NULL)
129 continue;
130
131 if (target_pin < 1)
132 continue;
133
134 /* Get the original INT_PIN for record keeping */
135 int_pin = pci_read_config8(dev, PCI_INTERRUPT_PIN);
136 if (int_pin < 1 || int_pin > 4)
137 continue; /* Device has invalid INT_PIN so skip it */
138
139 bus = target_dev->bus->secondary;
140 devfn = target_dev->path.pci.devfn;
141
142 /*
143 * Step 2: Use the INT_PIN and DevFn number to find the PCI_INTR
144 * register (0xC00) index for this device
145 */
146 pci_intr_idx = 0xBAD; /* Will check to make sure it changed */
147 for (i = 0; i < pirq_data_size; i++) {
148 if (pirq_data_ptr[i].devfn != devfn)
149 continue;
150
151 /* PIN_A is index 0 in pirq_data array but 1 in PCI cfg reg */
152 pci_intr_idx = pirq_data_ptr[i].PIN[target_pin - 1];
153 printk(BIOS_SPEW, "\tFound this device in pirq_data table entry %d\n", i);
154 break;
155 }
156
157 /*
158 * Step 3: Make sure we got a valid index and use it to get
159 * the IRQ number from the PCI_INTR register table
160 */
161 if (pci_intr_idx == 0xBAD) { /* Not on a bridge or in pirq_data table, skip it */
162 printk(BIOS_SPEW, "PCI Devfn (0x%x) not found in pirq_data table\n", devfn);
163 continue;
164 } else if (pci_intr_idx == 0x1F) { /* Index found is not defined */
165 printk(BIOS_SPEW, "Got index 0x1F (Not Connected), perhaps this device was defined wrong?\n");
166 continue;
167 } else if (pci_intr_idx >= FCH_INT_TABLE_SIZE) { /* Index out of bounds */
168 printk(BIOS_ERR, "%s: got 0xC00/0xC01 table index 0x%x, max is 0x%x\n",
169 __func__, pci_intr_idx, FCH_INT_TABLE_SIZE);
170 continue;
171 }
172
173 /* Find the value to program into the INT_LINE register from the PCI_INTR registers */
174 int_line = read_pci_int_idx(pci_intr_idx, 0);
175 if (int_line == PIRQ_NC) { /* The IRQ found is disabled */
176 printk(BIOS_SPEW, "Got IRQ 0x1F (disabled), perhaps this device was defined wrong?\n");
177 continue;
178 }
179
180 /*
181 * Step 4: Program the INT_LINE register in this device's
182 * PCI config space with the IRQ number we found in step 3
183 * and make it Level Triggered
184 */
185 pci_write_config8(dev, PCI_INTERRUPT_LINE, int_line);
186
187 /* Set this IRQ to level triggered since it is used by a PCI device */
188 i8259_configure_irq_trigger(int_line, IRQ_LEVEL_TRIGGERED);
189
190 /*
191 * Step 5: Print out debug info and move on to next device
192 */
193 printk(BIOS_SPEW, "\tOrig INT_PIN\t: %d (%s)\n",
194 int_pin, pin_to_str(int_pin));
195 if (bridged_device)
196 printk(BIOS_SPEW, "\tSwizzled to\t: %d (%s)\n",
197 target_pin, pin_to_str(target_pin));
198 printk(BIOS_SPEW, "\tPCI_INTR idx\t: 0x%02x (%s)\n"
199 "\tINT_LINE\t: 0x%X (IRQ %d)\n",
200 pci_intr_idx, intr_types[pci_intr_idx], int_line, int_line);
201 } /* for (dev = all_devices) */
202 printk(BIOS_DEBUG, "PCI_CFG IRQ: Finished writing PCI config space IRQ assignments\n");
203}
204#endif /* __PRE_RAM__ */