blob: 4d9311427c99007987c9d56da920fda41d14696c [file] [log] [blame]
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -05001/*
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 Gagniuc70c660f2012-08-23 02:32:58 -050016 */
Eric Biederman8ca8d762003-04-22 19:02:15 +000017#include <console/console.h>
18#include <arch/pirq_routing.h>
19#include <string.h>
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +000020#include <device/pci.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +000021
Myles Watson80e914ff2010-06-01 19:25:31 +000022#if CONFIG_DEBUG_PIRQ
Stefan Reinauer0b607b32004-06-07 10:25:42 +000023static void check_pirq_routing_table(struct irq_routing_table *rt)
Eric Biederman8ca8d762003-04-22 19:02:15 +000024{
Stefan Reinauer0b607b32004-06-07 10:25:42 +000025 uint8_t *addr = (uint8_t *)rt;
26 uint8_t sum=0;
Eric Biederman8ca8d762003-04-22 19:02:15 +000027 int i;
Eric Biederman8ca8d762003-04-22 19:02:15 +000028
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000029 printk(BIOS_INFO, "Checking Interrupt Routing Table consistency...\n");
Eric Biederman8ca8d762003-04-22 19:02:15 +000030
Stefan Reinauer0b607b32004-06-07 10:25:42 +000031 if (sizeof(struct irq_routing_table) != rt->size) {
Jens Rottmannda71ba52010-08-17 16:32:42 +000032 printk(BIOS_WARNING, "Inconsistent Interrupt Routing Table size (0x%x/0x%x).\n",
33 (unsigned int) sizeof(struct irq_routing_table),
Stefan Reinauer0b607b32004-06-07 10:25:42 +000034 rt->size
Li-Ta Lo8e79fc32004-04-15 17:33:21 +000035 );
Stefan Reinauer0b607b32004-06-07 10:25:42 +000036 rt->size=sizeof(struct irq_routing_table);
Eric Biederman8ca8d762003-04-22 19:02:15 +000037 }
Eric Biederman8ca8d762003-04-22 19:02:15 +000038
Eric Biederman8ca8d762003-04-22 19:02:15 +000039 for (i = 0; i < rt->size; i++)
40 sum += addr[i];
41
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000042 printk(BIOS_DEBUG, "%s(): Interrupt Routing Table located at %p.\n",
Myles Watson552b3272009-02-12 21:30:06 +000043 __func__, addr);
Eric Biederman8ca8d762003-04-22 19:02:15 +000044
Stefan Reinauer14e22772010-04-27 06:56:47 +000045
Eric Biedermaneb00fa52003-04-25 02:02:25 +000046 sum = rt->checksum - sum;
Eric Biederman8ca8d762003-04-22 19:02:15 +000047
48 if (sum != rt->checksum) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000049 printk(BIOS_WARNING, "Interrupt Routing Table checksum is: 0x%02x but should be: 0x%02x.\n",
Stefan Reinauer94f17772009-01-20 19:21:47 +000050 rt->checksum, sum);
Stefan Reinauer40cba392003-10-28 17:02:10 +000051 rt->checksum = sum;
Eric Biederman8ca8d762003-04-22 19:02:15 +000052 }
53
54 if (rt->signature != PIRQ_SIGNATURE || rt->version != PIRQ_VERSION ||
Stefan Reinauer0b607b32004-06-07 10:25:42 +000055 rt->size % 16 ) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000056 printk(BIOS_WARNING, "Interrupt Routing Table not valid.\n");
Eric Biederman8ca8d762003-04-22 19:02:15 +000057 return;
58 }
59
60 sum = 0;
61 for (i=0; i<rt->size; i++)
62 sum += addr[i];
63
Stefan Reinauer94f17772009-01-20 19:21:47 +000064 /* 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 Biederman8ca8d762003-04-22 19:02:15 +000068 if (sum) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000069 printk(BIOS_WARNING, "Checksum error in Interrupt Routing Table "
Myles Watson80e914ff2010-06-01 19:25:31 +000070 "could not be fixed.\n");
Eric Biederman8ca8d762003-04-22 19:02:15 +000071 }
72
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000073 printk(BIOS_INFO, "done.\n");
Eric Biederman8ca8d762003-04-22 19:02:15 +000074}
75
Stefan Reinauera47bd912012-11-15 15:15:15 -080076static int verify_copy_pirq_routing_table(unsigned long addr, const struct irq_routing_table *routing_table)
Eric Biederman8ca8d762003-04-22 19:02:15 +000077{
78 int i;
Eric Biederman8cd55d72003-04-24 06:56:37 +000079 uint8_t *rt_orig, *rt_curr;
Eric Biederman8ca8d762003-04-22 19:02:15 +000080
Eric Biederman8cd55d72003-04-24 06:56:37 +000081 rt_curr = (uint8_t*)addr;
Stefan Reinauera47bd912012-11-15 15:15:15 -080082 rt_orig = (uint8_t*)routing_table;
Marc Bertense9d4bcc2010-06-01 19:28:45 +000083 printk(BIOS_INFO, "Verifying copy of Interrupt Routing Table at 0x%08lx... ", addr);
Stefan Reinauera47bd912012-11-15 15:15:15 -080084 for (i = 0; i < routing_table->size; i++) {
Eric Biederman8ca8d762003-04-22 19:02:15 +000085 if (*(rt_curr + i) != *(rt_orig + i)) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000086 printk(BIOS_INFO, "failed\n");
Eric Biederman8ca8d762003-04-22 19:02:15 +000087 return -1;
88 }
89 }
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000090 printk(BIOS_INFO, "done\n");
Stefan Reinauer14e22772010-04-27 06:56:47 +000091
Stefan Reinauer0b607b32004-06-07 10:25:42 +000092 check_pirq_routing_table((struct irq_routing_table *)addr);
Stefan Reinauer14e22772010-04-27 06:56:47 +000093
Eric Biederman8ca8d762003-04-22 19:02:15 +000094 return 0;
95}
Eric Biederman8ca8d762003-04-22 19:02:15 +000096#endif
97
Stefan Reinauerde3206a2010-02-22 06:09:43 +000098#if CONFIG_PIRQ_ROUTE
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -050099static u8 pirq_get_next_free_irq(u8* pirq, u16 bitmap)
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000100{
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500101 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 Georgi95efb562012-10-08 09:33:38 +0200129static void pirq_route_irqs(unsigned long addr)
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500130{
131 int i, intx, num_entries;
132 unsigned char irq_slot[MAX_INTX_ENTRIES];
133 unsigned char pirq[CONFIG_MAX_PIRQ_LINKS];
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000134 struct irq_routing_table *pirq_tbl;
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000135
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500136 memset(pirq, 0, CONFIG_MAX_PIRQ_LINKS);
137
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000138 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 Reinauerc02b4fc2010-03-22 11:42:32 +0000144 printk(BIOS_DEBUG, "PIRQ Entry %d Dev/Fn: %X Slot: %d\n", i,
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000145 pirq_tbl->slots[i].devfn >> 3, pirq_tbl->slots[i].slot);
146
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500147 for (intx = 0; intx < MAX_INTX_ENTRIES; intx++) {
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000148
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500149 int link = pirq_tbl->slots[i].irq[intx].link;
150 int bitmap = pirq_tbl->slots[i].irq[intx].bitmap;
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000151 int irq = 0;
152
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000153 printk(BIOS_DEBUG, "INT: %c link: %x bitmap: %x ",
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500154 'A' + intx, link, bitmap);
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000155
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500156 if (!bitmap|| !link || link > CONFIG_MAX_PIRQ_LINKS) {
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000157
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000158 printk(BIOS_DEBUG, "not routed\n");
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500159 irq_slot[intx] = irq;
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000160 continue;
161 }
162
163 /* yet not routed */
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500164 if (!pirq[link - 1])
165 {
166 irq = pirq_get_next_free_irq(pirq, bitmap);
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000167 if (irq)
168 pirq[link - 1] = irq;
169 }
170 else
171 irq = pirq[link - 1];
172
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000173 printk(BIOS_DEBUG, "IRQ: %d\n", irq);
Alexandru Gagniuc70c660f2012-08-23 02:32:58 -0500174 irq_slot[intx] = irq;
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000175 }
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 Gagniuc70c660f2012-08-23 02:32:58 -0500182 for(i = 0; i < CONFIG_MAX_PIRQ_LINKS; i++)
183 printk(BIOS_DEBUG, "PIRQ%c: %d\n", i + 'A', pirq[i]);
Nikolay Petukhov9c2255c2008-03-29 16:59:27 +0000184
185 pirq_assign_irqs(pirq);
186}
187#endif
Patrick Georgi95efb562012-10-08 09:33:38 +0200188
189unsigned 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älkki9533d832014-06-26 05:30:54 +0300194 /* This table must be between 0xf0000 & 0x100000 */
Patrick Georgi95efb562012-10-08 09:33:38 +0200195 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}