Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 1 | #include <console/console.h> |
| 2 | #include <arch/pirq_routing.h> |
| 3 | #include <string.h> |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 4 | #include <device/pci.h> |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 5 | |
Stefan Reinauer | de3206a | 2010-02-22 06:09:43 +0000 | [diff] [blame^] | 6 | #ifndef CONFIG_IRQ_SLOT_COUNT |
| 7 | #warning "CONFIG_IRQ_SLOT_COUNT is not defined. PIRQ won't work correctly." |
| 8 | #endif |
| 9 | |
| 10 | #if CONFIG_DEBUG |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 11 | static void check_pirq_routing_table(struct irq_routing_table *rt) |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 12 | { |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 13 | uint8_t *addr = (uint8_t *)rt; |
| 14 | uint8_t sum=0; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 15 | int i; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 16 | |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 17 | printk_info("Checking Interrupt Routing Table consistency...\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 18 | |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 19 | if (sizeof(struct irq_routing_table) != rt->size) { |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 20 | printk_warning("Inconsistent Interrupt Routing Table size (0x%x/0x%x).\n", |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 21 | sizeof(struct irq_routing_table), |
| 22 | rt->size |
Li-Ta Lo | 8e79fc3 | 2004-04-15 17:33:21 +0000 | [diff] [blame] | 23 | ); |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 24 | rt->size=sizeof(struct irq_routing_table); |
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 | |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 27 | for (i = 0; i < rt->size; i++) |
| 28 | sum += addr[i]; |
| 29 | |
Myles Watson | 94e340b | 2009-02-10 03:02:05 +0000 | [diff] [blame] | 30 | printk_debug("%s(): Interrupt Routing Table located at %p.\n", |
Myles Watson | 552b327 | 2009-02-12 21:30:06 +0000 | [diff] [blame] | 31 | __func__, addr); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 32 | |
Eric Biederman | eb00fa5 | 2003-04-25 02:02:25 +0000 | [diff] [blame] | 33 | |
| 34 | sum = rt->checksum - sum; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 35 | |
| 36 | if (sum != rt->checksum) { |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 37 | printk_warning("Interrupt Routing Table checksum is: 0x%02x but should be: 0x%02x.\n", |
| 38 | rt->checksum, sum); |
Stefan Reinauer | 40cba39 | 2003-10-28 17:02:10 +0000 | [diff] [blame] | 39 | rt->checksum = sum; |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 40 | } |
| 41 | |
| 42 | if (rt->signature != PIRQ_SIGNATURE || rt->version != PIRQ_VERSION || |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 43 | rt->size % 16 ) { |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 44 | printk_warning("Interrupt Routing Table not valid.\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 45 | return; |
| 46 | } |
| 47 | |
| 48 | sum = 0; |
| 49 | for (i=0; i<rt->size; i++) |
| 50 | sum += addr[i]; |
| 51 | |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 52 | /* We're manually fixing the checksum above. This warning can probably |
| 53 | * never happen because if the target location is read-only this |
| 54 | * function would have bailed out earlier. |
| 55 | */ |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 56 | if (sum) { |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 57 | printk_warning("Checksum error in Interrupt Routing Table " |
| 58 | "could not be fixed.\n"); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 59 | } |
| 60 | |
| 61 | printk_info("done.\n"); |
| 62 | } |
| 63 | |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 64 | static int verify_copy_pirq_routing_table(unsigned long addr) |
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 | |
Eric Biederman | 8cd55d7 | 2003-04-24 06:56:37 +0000 | [diff] [blame] | 69 | rt_curr = (uint8_t*)addr; |
| 70 | rt_orig = (uint8_t*)&intel_irq_routing_table; |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 71 | printk_info("Verifing copy of Interrupt Routing Table at 0x%08x... ", addr); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 72 | for (i = 0; i < intel_irq_routing_table.size; i++) { |
| 73 | if (*(rt_curr + i) != *(rt_orig + i)) { |
| 74 | printk_info("failed\n"); |
| 75 | return -1; |
| 76 | } |
| 77 | } |
Stefan Reinauer | abf9fea | 2004-01-26 10:54:44 +0000 | [diff] [blame] | 78 | printk_info("done\n"); |
Stefan Reinauer | 0b607b3 | 2004-06-07 10:25:42 +0000 | [diff] [blame] | 79 | |
| 80 | check_pirq_routing_table((struct irq_routing_table *)addr); |
| 81 | |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 82 | return 0; |
| 83 | } |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 84 | #endif |
| 85 | |
| 86 | unsigned long copy_pirq_routing_table(unsigned long addr) |
| 87 | { |
| 88 | /* Align the table to be 16 byte aligned. */ |
| 89 | addr += 15; |
| 90 | addr &= ~15; |
| 91 | |
| 92 | /* This table must be betweeen 0xf0000 & 0x100000 */ |
Myles Watson | c4ddbff | 2009-02-09 17:52:54 +0000 | [diff] [blame] | 93 | printk_info("Copying Interrupt Routing Table to 0x%08lx... ", addr); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 94 | memcpy((void *)addr, &intel_irq_routing_table, intel_irq_routing_table.size); |
| 95 | printk_info("done.\n"); |
| 96 | verify_copy_pirq_routing_table(addr); |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 97 | pirq_routing_irqs(addr); |
Eric Biederman | 8ca8d76 | 2003-04-22 19:02:15 +0000 | [diff] [blame] | 98 | return addr + intel_irq_routing_table.size; |
| 99 | } |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 100 | |
Stefan Reinauer | de3206a | 2010-02-22 06:09:43 +0000 | [diff] [blame^] | 101 | #if CONFIG_PIRQ_ROUTE |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 102 | void pirq_routing_irqs(unsigned long addr) |
| 103 | { |
| 104 | int i, j, k, num_entries; |
| 105 | unsigned char irq_slot[4]; |
| 106 | unsigned char pirq[4] = {0, 0, 0, 0}; |
| 107 | struct irq_routing_table *pirq_tbl; |
| 108 | device_t pdev; |
| 109 | |
| 110 | pirq_tbl = (struct irq_routing_table *)(addr); |
| 111 | num_entries = (pirq_tbl->size - 32) / 16; |
| 112 | |
| 113 | /* Set PCI IRQs. */ |
| 114 | for (i = 0; i < num_entries; i++) { |
| 115 | |
Stefan Reinauer | 94f1777 | 2009-01-20 19:21:47 +0000 | [diff] [blame] | 116 | printk_debug("PIRQ Entry %d Dev/Fn: %X Slot: %d\n", i, |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 117 | pirq_tbl->slots[i].devfn >> 3, pirq_tbl->slots[i].slot); |
| 118 | |
| 119 | for (j = 0; j < 4; j++) { |
| 120 | |
| 121 | int link = pirq_tbl->slots[i].irq[j].link; |
Marc Jones(marc.jones | df22f78 | 2008-04-07 17:49:57 +0000 | [diff] [blame] | 122 | int bitmap = pirq_tbl->slots[i].irq[j].bitmap; |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 123 | int irq = 0; |
| 124 | |
| 125 | printk_debug("INT: %c link: %x bitmap: %x ", |
| 126 | 'A' + j, link, bitmap); |
| 127 | |
| 128 | if (!bitmap|| !link || link > 4) { |
| 129 | |
| 130 | printk_debug("not routed\n"); |
| 131 | irq_slot[j] = irq; |
| 132 | continue; |
| 133 | } |
| 134 | |
| 135 | /* yet not routed */ |
| 136 | if (!pirq[link - 1]) { |
| 137 | |
Jens Rottmann | 9a9e61b | 2008-10-22 22:20:48 +0000 | [diff] [blame] | 138 | for (k = 2; k <= 15; k++) { |
Nikolay Petukhov | 9c2255c | 2008-03-29 16:59:27 +0000 | [diff] [blame] | 139 | |
| 140 | if (!((bitmap >> k) & 1)) |
| 141 | continue; |
| 142 | |
| 143 | irq = k; |
| 144 | |
| 145 | /* yet not routed */ |
| 146 | if (pirq[0] != irq && pirq[1] != irq && pirq[2] != irq && pirq[3] != irq) |
| 147 | break; |
| 148 | } |
| 149 | |
| 150 | if (irq) |
| 151 | pirq[link - 1] = irq; |
| 152 | } |
| 153 | else |
| 154 | irq = pirq[link - 1]; |
| 155 | |
| 156 | printk_debug("IRQ: %d\n", irq); |
| 157 | irq_slot[j] = irq; |
| 158 | } |
| 159 | |
| 160 | /* Bus, device, slots IRQs for {A,B,C,D}. */ |
| 161 | pci_assign_irqs(pirq_tbl->slots[i].bus, |
| 162 | pirq_tbl->slots[i].devfn >> 3, irq_slot); |
| 163 | } |
| 164 | |
| 165 | printk_debug("PIRQ1: %d\n", pirq[0]); |
| 166 | printk_debug("PIRQ2: %d\n", pirq[1]); |
| 167 | printk_debug("PIRQ3: %d\n", pirq[2]); |
| 168 | printk_debug("PIRQ4: %d\n", pirq[3]); |
| 169 | |
| 170 | pirq_assign_irqs(pirq); |
| 171 | } |
| 172 | #endif |