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