Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com> |
| 5 | * Copyright (C) 2010 Stefan Reinauer <stepan@coreboot.org> |
| 6 | * |
| 7 | * This program is free software: you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation, either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 16 | */ |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 17 | #include <console/console.h> |
| 18 | #include <arch/pirq_routing.h> |
| 19 | #include <string.h> |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 20 | #include <device/pci.h> |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 21 | |
Myles Watson | 80e914ff | 2010-06-01 19:25:31 +0000 | [diff] [blame] | 22 | #if CONFIG_DEBUG_PIRQ |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 23 | static void check_pirq_routing_table(struct irq_routing_table *rt) |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 24 | { |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 25 | uint8_t *addr = (uint8_t *)rt; |
| 26 | uint8_t sum=0; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 27 | int i; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 28 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 29 | printk(BIOS_INFO, "Checking Interrupt Routing Table consistency...\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 30 | |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 31 | if (sizeof(struct irq_routing_table) != rt->size) { |
Jens Rottmann | da71ba5 | 2010-08-17 16:32:42 +0000 | [diff] [blame] | 32 | printk(BIOS_WARNING, "Inconsistent Interrupt Routing Table size (0x%x/0x%x).\n", |
| 33 | (unsigned int) sizeof(struct irq_routing_table), |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 34 | rt->size |
Li-Ta Lo | 8e79fc3 | 2004-04-15 17:33:21 +0000 | [diff] [blame] | 35 | ); |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 36 | rt->size=sizeof(struct irq_routing_table); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 37 | } |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 38 | |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 39 | for (i = 0; i < rt->size; i++) |
| 40 | sum += addr[i]; |
| 41 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 42 | printk(BIOS_DEBUG, "%s(): Interrupt Routing Table located at %p.\n", |
Myles Watson | 552b327 | 2009-02-12 21:30:06 +0000 | [diff] [blame] | 43 | __func__, addr); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 44 | |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 45 | |
Eric Biederman | eb00fa5 | 2003-04-25 02:02:25 +0000 | [diff] [blame] | 46 | sum = rt->checksum - sum; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 47 | |
| 48 | if (sum != rt->checksum) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 49 | printk(BIOS_WARNING, "Interrupt Routing Table checksum is: 0x%02x but should be: 0x%02x.\n", |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 50 | rt->checksum, sum); |
Stefan Reinauer | 40cba39 | 2003-10-28 17:02:10 +0000 | [diff] [blame] | 51 | rt->checksum = sum; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | if (rt->signature != PIRQ_SIGNATURE || rt->version != PIRQ_VERSION || |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 55 | rt->size % 16 ) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 56 | printk(BIOS_WARNING, "Interrupt Routing Table not valid.\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 57 | return; |
| 58 | } |
| 59 | |
| 60 | sum = 0; |
| 61 | for (i=0; i<rt->size; i++) |
| 62 | sum += addr[i]; |
| 63 | |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 64 | /* We're manually fixing the checksum above. This warning can probably |
| 65 | * never happen because if the target location is read-only this |
| 66 | * function would have bailed out earlier. |
| 67 | */ |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 68 | if (sum) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 69 | printk(BIOS_WARNING, "Checksum error in Interrupt Routing Table " |
Myles Watson | 80e914ff | 2010-06-01 19:25:31 +0000 | [diff] [blame] | 70 | "could not be fixed.\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 71 | } |
| 72 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 73 | printk(BIOS_INFO, "done.\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 74 | } |
| 75 | |
Stefan Reinauer | a47bd91 | 2012-11-15 15:15:15 -0800 | [diff] [blame] | 76 | static int verify_copy_pirq_routing_table(unsigned long addr, const struct irq_routing_table *routing_table) |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 77 | { |
| 78 | int i; |
Eric Biederman | 8cd55d7 | 2003-04-24 06:56:37 +0000 | [diff] [blame] | 79 | uint8_t *rt_orig, *rt_curr; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 80 | |
Eric Biederman | 8cd55d7 | 2003-04-24 06:56:37 +0000 | [diff] [blame] | 81 | rt_curr = (uint8_t*)addr; |
Stefan Reinauer | a47bd91 | 2012-11-15 15:15:15 -0800 | [diff] [blame] | 82 | rt_orig = (uint8_t*)routing_table; |
Marc Bertens | e9d4bcc | 2010-06-01 19:28:45 +0000 | [diff] [blame] | 83 | printk(BIOS_INFO, "Verifying copy of Interrupt Routing Table at 0x%08lx... ", addr); |
Stefan Reinauer | a47bd91 | 2012-11-15 15:15:15 -0800 | [diff] [blame] | 84 | for (i = 0; i < routing_table->size; i++) { |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 85 | if (*(rt_curr + i) != *(rt_orig + i)) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 86 | printk(BIOS_INFO, "failed\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 87 | return -1; |
| 88 | } |
| 89 | } |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 90 | printk(BIOS_INFO, "done\n"); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 91 | |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 92 | check_pirq_routing_table((struct irq_routing_table *)addr); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 93 | |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 94 | return 0; |
| 95 | } |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 96 | #endif |
| 97 | |
Stefan Reinauer | de3206a | 2010-02-22 06:09:43 +0000 | [diff] [blame] | 98 | #if CONFIG_PIRQ_ROUTE |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 99 | static u8 pirq_get_next_free_irq(u8* pirq, u16 bitmap) |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 100 | { |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 101 | int i, link; |
| 102 | u8 irq = 0; |
| 103 | for (i = 2; i <= 15; i++) |
| 104 | { |
| 105 | /* Can we assign this IRQ ? */ |
| 106 | if (!((bitmap >> i) & 1)) |
| 107 | continue; |
| 108 | /* We can, Now let's assume we can use this IRQ */ |
| 109 | irq = i; |
| 110 | /* And assume we have not yet routed it */ |
| 111 | int already_routed = 0; |
| 112 | /* Have we already routed it ? */ |
| 113 | for(link = 0; link < CONFIG_MAX_PIRQ_LINKS; link++) { |
| 114 | if (pirq[link] == irq) { |
| 115 | already_routed = 1; |
| 116 | break; |
| 117 | } |
| 118 | } |
| 119 | /* If it's not yet routed, use it */ |
| 120 | if(!already_routed) |
| 121 | break; |
| 122 | /* But if it was already routed, try the next one */ |
| 123 | continue; |
| 124 | } |
| 125 | /* Now we got our IRQ */ |
| 126 | return irq; |
| 127 | } |
| 128 | |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 129 | static void pirq_route_irqs(unsigned long addr) |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 130 | { |
| 131 | int i, intx, num_entries; |
| 132 | unsigned char irq_slot[MAX_INTX_ENTRIES]; |
| 133 | unsigned char pirq[CONFIG_MAX_PIRQ_LINKS]; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 134 | struct irq_routing_table *pirq_tbl; |
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 | memset(pirq, 0, CONFIG_MAX_PIRQ_LINKS); |
| 137 | |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 138 | pirq_tbl = (struct irq_routing_table *)(addr); |
| 139 | num_entries = (pirq_tbl->size - 32) / 16; |
| 140 | |
| 141 | /* Set PCI IRQs. */ |
| 142 | for (i = 0; i < num_entries; i++) { |
| 143 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 144 | printk(BIOS_DEBUG, "PIRQ Entry %d Dev/Fn: %X Slot: %d\n", i, |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 145 | pirq_tbl->slots[i].devfn >> 3, pirq_tbl->slots[i].slot); |
| 146 | |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 147 | for (intx = 0; intx < MAX_INTX_ENTRIES; intx++) { |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 148 | |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 149 | int link = pirq_tbl->slots[i].irq[intx].link; |
| 150 | int bitmap = pirq_tbl->slots[i].irq[intx].bitmap; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 151 | int irq = 0; |
| 152 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 153 | printk(BIOS_DEBUG, "INT: %c link: %x bitmap: %x ", |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 154 | 'A' + intx, link, bitmap); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 155 | |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 156 | if (!bitmap|| !link || link > CONFIG_MAX_PIRQ_LINKS) { |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 157 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 158 | printk(BIOS_DEBUG, "not routed\n"); |
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 | continue; |
| 161 | } |
| 162 | |
| 163 | /* yet not routed */ |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 164 | if (!pirq[link - 1]) |
| 165 | { |
| 166 | irq = pirq_get_next_free_irq(pirq, bitmap); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 167 | if (irq) |
| 168 | pirq[link - 1] = irq; |
| 169 | } |
| 170 | else |
| 171 | irq = pirq[link - 1]; |
| 172 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 173 | printk(BIOS_DEBUG, "IRQ: %d\n", irq); |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 174 | irq_slot[intx] = irq; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 175 | } |
| 176 | |
| 177 | /* Bus, device, slots IRQs for {A,B,C,D}. */ |
| 178 | pci_assign_irqs(pirq_tbl->slots[i].bus, |
| 179 | pirq_tbl->slots[i].devfn >> 3, irq_slot); |
| 180 | } |
| 181 | |
Alexandru Gagniuc | 70c660f | 2012-08-23 02:32:58 -0500 | [diff] [blame] | 182 | for(i = 0; i < CONFIG_MAX_PIRQ_LINKS; i++) |
| 183 | printk(BIOS_DEBUG, "PIRQ%c: %d\n", i + 'A', pirq[i]); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 184 | |
| 185 | pirq_assign_irqs(pirq); |
| 186 | } |
| 187 | #endif |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 188 | |
| 189 | unsigned long copy_pirq_routing_table(unsigned long addr, const struct irq_routing_table *routing_table) |
| 190 | { |
| 191 | /* Align the table to be 16 byte aligned. */ |
| 192 | addr = ALIGN(addr, 16); |
| 193 | |
Kyösti Mälkki | 9533d83 | 2014-06-26 05:30:54 +0300 | [diff] [blame] | 194 | /* This table must be between 0xf0000 & 0x100000 */ |
Patrick Georgi | 95efb56 | 2012-10-08 09:33:38 +0200 | [diff] [blame] | 195 | printk(BIOS_INFO, "Copying Interrupt Routing Table to 0x%08lx... ", addr); |
| 196 | memcpy((void *)addr, routing_table, routing_table->size); |
| 197 | printk(BIOS_INFO, "done.\n"); |
| 198 | #if CONFIG_DEBUG_PIRQ |
| 199 | verify_copy_pirq_routing_table(addr, routing_table); |
| 200 | #endif |
| 201 | #if CONFIG_PIRQ_ROUTE |
| 202 | pirq_route_irqs(addr); |
| 203 | #endif |
| 204 | return addr + routing_table->size; |
| 205 | } |