Patrick Georgi | 11f0079 | 2020-03-04 15:10:45 +0100 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
Patrick Georgi | 11f0079 | 2020-03-04 15:10:45 +0100 | [diff] [blame] | 2 | |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 3 | #include <console/console.h> |
| 4 | #include <arch/pirq_routing.h> |
Elyes HAOUAS | e2d152c | 2019-06-21 07:06:50 +0200 | [diff] [blame] | 5 | #include <commonlib/helpers.h> |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 6 | #include <string.h> |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 7 | #include <device/pci.h> |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 8 | |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 9 | static void check_pirq_routing_table(struct irq_routing_table *rt) |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 10 | { |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 11 | uint8_t *addr = (uint8_t *)rt; |
Lee Leahy | 024b13d | 2017-03-16 13:41:11 -0700 | [diff] [blame] | 12 | uint8_t sum = 0; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 13 | int i; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 14 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 15 | printk(BIOS_INFO, "Checking Interrupt Routing Table consistency...\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 16 | |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 17 | if (sizeof(struct irq_routing_table) != rt->size) { |
Lee Leahy | 6f80ccc | 2017-03-16 15:18:22 -0700 | [diff] [blame] | 18 | printk(BIOS_WARNING, |
| 19 | "Inconsistent Interrupt Routing Table size (0x%x/0x%x).\n", |
| 20 | (unsigned int) sizeof(struct irq_routing_table), |
| 21 | rt->size); |
Lee Leahy | 024b13d | 2017-03-16 13:41:11 -0700 | [diff] [blame] | 22 | rt->size = sizeof(struct irq_routing_table); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 23 | } |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 24 | |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 25 | for (i = 0; i < rt->size; i++) |
| 26 | sum += addr[i]; |
| 27 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 28 | printk(BIOS_DEBUG, "%s(): Interrupt Routing Table located at %p.\n", |
Myles Watson | 552b327 | 2009-02-12 21:30:06 +0000 | [diff] [blame] | 29 | __func__, addr); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 30 | |
Eric Biederman | eb00fa5 | 2003-04-25 02:02:25 +0000 | [diff] [blame] | 31 | sum = rt->checksum - sum; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 32 | |
| 33 | if (sum != rt->checksum) { |
Lee Leahy | 6f80ccc | 2017-03-16 15:18:22 -0700 | [diff] [blame] | 34 | printk(BIOS_WARNING, |
| 35 | "Interrupt Routing Table checksum is: 0x%02x but should be: 0x%02x.\n", |
| 36 | rt->checksum, sum); |
Stefan Reinauer | 40cba39 | 2003-10-28 17:02:10 +0000 | [diff] [blame] | 37 | rt->checksum = sum; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 38 | } |
| 39 | |
| 40 | if (rt->signature != PIRQ_SIGNATURE || rt->version != PIRQ_VERSION || |
Lee Leahy | 024b13d | 2017-03-16 13:41:11 -0700 | [diff] [blame] | 41 | rt->size % 16) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 42 | printk(BIOS_WARNING, "Interrupt Routing Table not valid.\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 43 | return; |
| 44 | } |
| 45 | |
| 46 | sum = 0; |
Lee Leahy | 024b13d | 2017-03-16 13:41:11 -0700 | [diff] [blame] | 47 | for (i = 0; i < rt->size; i++) |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 48 | sum += addr[i]; |
| 49 | |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 50 | /* We're manually fixing the checksum above. This warning can probably |
| 51 | * never happen because if the target location is read-only this |
| 52 | * function would have bailed out earlier. |
| 53 | */ |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 54 | if (sum) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 55 | printk(BIOS_WARNING, "Checksum error in Interrupt Routing Table " |
Myles Watson | 80e914ff | 2010-06-01 19:25:31 +0000 | [diff] [blame] | 56 | "could not be fixed.\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 57 | } |
| 58 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 59 | printk(BIOS_INFO, "done.\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 60 | } |
| 61 | |
Lee Leahy | 6f80ccc | 2017-03-16 15:18:22 -0700 | [diff] [blame] | 62 | static int verify_copy_pirq_routing_table(unsigned long addr, |
| 63 | const struct irq_routing_table *routing_table) |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 64 | { |
| 65 | int i; |
Eric Biederman | 8cd55d7 | 2003-04-24 06:56:37 +0000 | [diff] [blame] | 66 | uint8_t *rt_orig, *rt_curr; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 67 | |
Lee Leahy | 024b13d | 2017-03-16 13:41:11 -0700 | [diff] [blame] | 68 | rt_curr = (uint8_t *)addr; |
| 69 | rt_orig = (uint8_t *)routing_table; |
Lee Leahy | 6f80ccc | 2017-03-16 15:18:22 -0700 | [diff] [blame] | 70 | printk(BIOS_INFO, |
| 71 | "Verifying copy of Interrupt Routing Table at 0x%08lx... ", |
| 72 | addr); |
Stefan Reinauer | a47bd91 | 2012-11-15 15:15:15 -0800 | [diff] [blame] | 73 | for (i = 0; i < routing_table->size; i++) { |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 74 | if (*(rt_curr + i) != *(rt_orig + i)) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 75 | printk(BIOS_INFO, "failed\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 76 | return -1; |
| 77 | } |
| 78 | } |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 79 | printk(BIOS_INFO, "done\n"); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 80 | |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 81 | check_pirq_routing_table((struct irq_routing_table *)addr); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 82 | |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 83 | return 0; |
| 84 | } |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 85 | |
Lee Leahy | 024b13d | 2017-03-16 13:41:11 -0700 | [diff] [blame] | 86 | static u8 pirq_get_next_free_irq(u8 *pirq, u16 bitmap) |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 87 | { |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 88 | int i, link; |
| 89 | u8 irq = 0; |
Lee Leahy | 9c7c6f7 | 2017-03-16 11:24:09 -0700 | [diff] [blame] | 90 | for (i = 2; i <= 15; i++) { |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 91 | /* Can we assign this IRQ ? */ |
| 92 | if (!((bitmap >> i) & 1)) |
| 93 | continue; |
| 94 | /* We can, Now let's assume we can use this IRQ */ |
| 95 | irq = i; |
| 96 | /* And assume we have not yet routed it */ |
| 97 | int already_routed = 0; |
| 98 | /* Have we already routed it ? */ |
Elyes HAOUAS | dbf3067 | 2016-08-21 17:37:15 +0200 | [diff] [blame] | 99 | for (link = 0; link < CONFIG_MAX_PIRQ_LINKS; link++) { |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 100 | if (pirq[link] == irq) { |
| 101 | already_routed = 1; |
| 102 | break; |
| 103 | } |
| 104 | } |
| 105 | /* If it's not yet routed, use it */ |
Elyes HAOUAS | dbf3067 | 2016-08-21 17:37:15 +0200 | [diff] [blame] | 106 | if (!already_routed) |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 107 | break; |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 108 | } |
| 109 | /* Now we got our IRQ */ |
| 110 | return irq; |
| 111 | } |
| 112 | |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 113 | static void pirq_route_irqs(unsigned long addr) |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 114 | { |
| 115 | int i, intx, num_entries; |
| 116 | unsigned char irq_slot[MAX_INTX_ENTRIES]; |
| 117 | unsigned char pirq[CONFIG_MAX_PIRQ_LINKS]; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 118 | struct irq_routing_table *pirq_tbl; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 119 | |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 120 | memset(pirq, 0, CONFIG_MAX_PIRQ_LINKS); |
| 121 | |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 122 | pirq_tbl = (struct irq_routing_table *)(addr); |
| 123 | num_entries = (pirq_tbl->size - 32) / 16; |
| 124 | |
| 125 | /* Set PCI IRQs. */ |
| 126 | for (i = 0; i < num_entries; i++) { |
| 127 | |
Kyösti Mälkki | b72b5d9 | 2019-07-04 21:08:17 +0300 | [diff] [blame] | 128 | u8 bus = pirq_tbl->slots[i].bus; |
| 129 | u8 devfn = pirq_tbl->slots[i].devfn; |
| 130 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 131 | printk(BIOS_DEBUG, "PIRQ Entry %d Dev/Fn: %X Slot: %d\n", i, |
Kyösti Mälkki | b72b5d9 | 2019-07-04 21:08:17 +0300 | [diff] [blame] | 132 | devfn >> 3, pirq_tbl->slots[i].slot); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 133 | |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 134 | for (intx = 0; intx < MAX_INTX_ENTRIES; intx++) { |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 135 | |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 136 | int link = pirq_tbl->slots[i].irq[intx].link; |
| 137 | int bitmap = pirq_tbl->slots[i].irq[intx].bitmap; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 138 | int irq = 0; |
| 139 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 140 | printk(BIOS_DEBUG, "INT: %c link: %x bitmap: %x ", |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 141 | 'A' + intx, link, bitmap); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 142 | |
Lee Leahy | 024b13d | 2017-03-16 13:41:11 -0700 | [diff] [blame] | 143 | if (!bitmap || !link || link > CONFIG_MAX_PIRQ_LINKS) { |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 144 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 145 | printk(BIOS_DEBUG, "not routed\n"); |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 146 | irq_slot[intx] = irq; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 147 | continue; |
| 148 | } |
| 149 | |
| 150 | /* yet not routed */ |
Lee Leahy | 9c7c6f7 | 2017-03-16 11:24:09 -0700 | [diff] [blame] | 151 | if (!pirq[link - 1]) { |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 152 | irq = pirq_get_next_free_irq(pirq, bitmap); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 153 | if (irq) |
| 154 | pirq[link - 1] = irq; |
Lee Leahy | 9c7c6f7 | 2017-03-16 11:24:09 -0700 | [diff] [blame] | 155 | } else |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 156 | irq = pirq[link - 1]; |
| 157 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 158 | printk(BIOS_DEBUG, "IRQ: %d\n", irq); |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 159 | irq_slot[intx] = irq; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | /* Bus, device, slots IRQs for {A,B,C,D}. */ |
Kyösti Mälkki | c19d6a6 | 2019-07-04 21:39:28 +0300 | [diff] [blame] | 163 | pci_assign_irqs(pcidev_path_on_bus(bus, devfn), irq_slot); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 164 | } |
| 165 | |
Elyes HAOUAS | dbf3067 | 2016-08-21 17:37:15 +0200 | [diff] [blame] | 166 | for (i = 0; i < CONFIG_MAX_PIRQ_LINKS; i++) |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 167 | printk(BIOS_DEBUG, "PIRQ%c: %d\n", i + 'A', pirq[i]); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 168 | |
| 169 | pirq_assign_irqs(pirq); |
| 170 | } |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 171 | |
Lee Leahy | 6f80ccc | 2017-03-16 15:18:22 -0700 | [diff] [blame] | 172 | unsigned long copy_pirq_routing_table(unsigned long addr, |
| 173 | const struct irq_routing_table *routing_table) |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 174 | { |
| 175 | /* Align the table to be 16 byte aligned. */ |
Felix Held | 84b5aa3 | 2019-06-20 14:14:51 +0200 | [diff] [blame] | 176 | addr = ALIGN_UP(addr, 16); |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 177 | |
Kyösti Mälkki | 9533d83 | 2014-06-26 05:30:54 +0300 | [diff] [blame] | 178 | /* This table must be between 0xf0000 & 0x100000 */ |
Lee Leahy | 6f80ccc | 2017-03-16 15:18:22 -0700 | [diff] [blame] | 179 | printk(BIOS_INFO, "Copying Interrupt Routing Table to 0x%08lx... ", |
| 180 | addr); |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 181 | memcpy((void *)addr, routing_table, routing_table->size); |
| 182 | printk(BIOS_INFO, "done.\n"); |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 183 | if (CONFIG(DEBUG_PIRQ)) |
Martin Roth | 898a775 | 2017-06-01 11:39:59 -0600 | [diff] [blame] | 184 | verify_copy_pirq_routing_table(addr, routing_table); |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 185 | if (CONFIG(PIRQ_ROUTE)) |
Martin Roth | 898a775 | 2017-06-01 11:39:59 -0600 | [diff] [blame] | 186 | pirq_route_irqs(addr); |
| 187 | |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 188 | return addr + routing_table->size; |
| 189 | } |