blob: 8c380e55113bf646967d3c1bf8c0adf0417dd468 [file] [log] [blame]
Eric Biederman8ca8d762003-04-22 19:02:15 +00001#include <console/console.h>
Eric Biederman7003ba42004-10-16 06:20:29 +00002#include <device/path.h>
Yinghai Luf327d9f2008-02-20 17:41:38 +00003#include <device/pci_ids.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +00004#include <cpu/cpu.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +00005#include <arch/smp/mpspec.h>
6#include <string.h>
Eric Biedermanb78c1972004-10-14 20:54:17 +00007#include <arch/cpu.h>
8#include <cpu/x86/lapic.h>
Sven Schnelle0fa50a12012-06-21 22:19:48 +02009#include <drivers/generic/ioapic/chip.h>
Eric Biederman8ca8d762003-04-22 19:02:15 +000010
Uwe Hermann55dc2232010-10-25 15:32:07 +000011/* Initialize the specified "mc" struct with initial values. */
Uwe Hermannc36d5062010-12-16 19:51:38 +000012void mptable_init(struct mp_config_table *mc, u32 lapic_addr)
Uwe Hermann55dc2232010-10-25 15:32:07 +000013{
Uwe Hermannc36d5062010-12-16 19:51:38 +000014 int i;
Uwe Hermann55dc2232010-10-25 15:32:07 +000015
16 memset(mc, 0, sizeof(*mc));
Uwe Hermannc36d5062010-12-16 19:51:38 +000017
Uwe Hermann55dc2232010-10-25 15:32:07 +000018 memcpy(mc->mpc_signature, MPC_SIGNATURE, 4);
Uwe Hermannc36d5062010-12-16 19:51:38 +000019
Uwe Hermann55dc2232010-10-25 15:32:07 +000020 mc->mpc_length = sizeof(*mc); /* Initially just the header size. */
21 mc->mpc_spec = 0x04; /* MultiProcessor specification 1.4 */
22 mc->mpc_checksum = 0; /* Not yet computed. */
Uwe Hermann55dc2232010-10-25 15:32:07 +000023 mc->mpc_oemptr = 0;
24 mc->mpc_oemsize = 0;
25 mc->mpc_entry_count = 0; /* No entries yet... */
26 mc->mpc_lapic = lapic_addr;
27 mc->mpe_length = 0;
28 mc->mpe_checksum = 0;
29 mc->reserved = 0;
Uwe Hermannc36d5062010-12-16 19:51:38 +000030
31 strncpy(mc->mpc_oem, CONFIG_MAINBOARD_VENDOR, 8);
32 strncpy(mc->mpc_productid, CONFIG_MAINBOARD_PART_NUMBER, 12);
33
34 /*
35 * The oem/productid fields are exactly 8/12 bytes long. If the resp.
36 * entry is shorter, the remaining bytes are filled with spaces.
37 */
38 for (i = MIN(strlen(CONFIG_MAINBOARD_VENDOR), 8); i < 8; i++)
39 mc->mpc_oem[i] = ' ';
40 for (i = MIN(strlen(CONFIG_MAINBOARD_PART_NUMBER), 12); i < 12; i++)
41 mc->mpc_productid[i] = ' ';
Uwe Hermann55dc2232010-10-25 15:32:07 +000042}
43
Patrick Georgib0a9c5c2011-10-07 23:01:55 +020044static unsigned char smp_compute_checksum(void *v, int len)
Eric Biederman8ca8d762003-04-22 19:02:15 +000045{
46 unsigned char *bytes;
47 unsigned char checksum;
48 int i;
49 bytes = v;
50 checksum = 0;
51 for(i = 0; i < len; i++) {
52 checksum -= bytes[i];
53 }
54 return checksum;
55}
56
Patrick Georgic75c79b2011-10-07 22:41:07 +020057static void *smp_write_floating_table_physaddr(unsigned long addr, unsigned long mpf_physptr, unsigned int virtualwire)
Stefan Reinauer4f1cb232006-07-19 15:32:49 +000058{
59 struct intel_mp_floating *mf;
60 void *v;
Stefan Reinauer14e22772010-04-27 06:56:47 +000061
Yinghai Luf327d9f2008-02-20 17:41:38 +000062 v = (void *)addr;
Stefan Reinauer4f1cb232006-07-19 15:32:49 +000063 mf = v;
64 mf->mpf_signature[0] = '_';
65 mf->mpf_signature[1] = 'M';
66 mf->mpf_signature[2] = 'P';
67 mf->mpf_signature[3] = '_';
68 mf->mpf_physptr = mpf_physptr;
69 mf->mpf_length = 1;
70 mf->mpf_specification = 4;
71 mf->mpf_checksum = 0;
72 mf->mpf_feature1 = 0;
Patrick Georgic75c79b2011-10-07 22:41:07 +020073 mf->mpf_feature2 = virtualwire?MP_FEATURE_VIRTUALWIRE:0;
Stefan Reinauer4f1cb232006-07-19 15:32:49 +000074 mf->mpf_feature3 = 0;
75 mf->mpf_feature4 = 0;
76 mf->mpf_feature5 = 0;
77 mf->mpf_checksum = smp_compute_checksum(mf, mf->mpf_length*16);
78 return v;
79}
80
Patrick Georgic75c79b2011-10-07 22:41:07 +020081void *smp_write_floating_table(unsigned long addr, unsigned int virtualwire)
82{
83 /* 16 byte align the table address */
84 addr = (addr + 0xf) & (~0xf);
85 return smp_write_floating_table_physaddr(addr, addr + SMP_FLOATING_TABLE_LEN, virtualwire);
86}
87
Eric Biederman8ca8d762003-04-22 19:02:15 +000088void *smp_next_mpc_entry(struct mp_config_table *mc)
89{
90 void *v;
91 v = (void *)(((char *)mc) + mc->mpc_length);
92 return v;
93}
94static void smp_add_mpc_entry(struct mp_config_table *mc, unsigned length)
95{
96 mc->mpc_length += length;
97 mc->mpc_entry_count++;
98}
99
100void *smp_next_mpe_entry(struct mp_config_table *mc)
101{
102 void *v;
103 v = (void *)(((char *)mc) + mc->mpc_length + mc->mpe_length);
104 return v;
105}
106static void smp_add_mpe_entry(struct mp_config_table *mc, mpe_t mpe)
107{
108 mc->mpe_length += mpe->mpe_length;
109}
110
111void smp_write_processor(struct mp_config_table *mc,
112 unsigned char apicid, unsigned char apicver,
113 unsigned char cpuflag, unsigned int cpufeature,
114 unsigned int featureflag)
115{
116 struct mpc_config_processor *mpc;
117 mpc = smp_next_mpc_entry(mc);
118 memset(mpc, '\0', sizeof(*mpc));
119 mpc->mpc_type = MP_PROCESSOR;
120 mpc->mpc_apicid = apicid;
121 mpc->mpc_apicver = apicver;
122 mpc->mpc_cpuflag = cpuflag;
123 mpc->mpc_cpufeature = cpufeature;
124 mpc->mpc_featureflag = featureflag;
125 smp_add_mpc_entry(mc, sizeof(*mpc));
126}
127
128/* If we assume a symmetric processor configuration we can
129 * get all of the information we need to write the processor
130 * entry from the bootstrap processor.
131 * Plus I don't think linux really even cares.
132 * Having the proper apicid's in the table so the non-bootstrap
133 * processors can be woken up should be enough.
134 */
Eric Biedermanb78c1972004-10-14 20:54:17 +0000135void smp_write_processors(struct mp_config_table *mc)
Eric Biederman8ca8d762003-04-22 19:02:15 +0000136{
Eric Biedermanb78c1972004-10-14 20:54:17 +0000137 int boot_apic_id;
Patrick Georgi07b42152011-10-14 23:02:57 +0200138 int order_id;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000139 unsigned apic_version;
140 unsigned cpu_features;
141 unsigned cpu_feature_flags;
Eric Biedermanb78c1972004-10-14 20:54:17 +0000142 struct cpuid_result result;
143 device_t cpu;
Stefan Reinauer14e22772010-04-27 06:56:47 +0000144
Eric Biedermanb78c1972004-10-14 20:54:17 +0000145 boot_apic_id = lapicid();
146 apic_version = lapic_read(LAPIC_LVR) & 0xff;
147 result = cpuid(1);
148 cpu_features = result.eax;
149 cpu_feature_flags = result.edx;
Patrick Georgi07b42152011-10-14 23:02:57 +0200150 /* order the output of the cpus to fix a bug in kernel 2.6.11 */
151 for(order_id = 0;order_id <256; order_id++) {
Eric Biederman7003ba42004-10-16 06:20:29 +0000152 for(cpu = all_devices; cpu; cpu = cpu->next) {
Eric Biederman8ca8d762003-04-22 19:02:15 +0000153 unsigned long cpu_flag;
Stefan Reinauer14e22772010-04-27 06:56:47 +0000154 if ((cpu->path.type != DEVICE_PATH_APIC) ||
Stefan Reinauer0aa37c42013-02-12 15:20:54 -0800155 (cpu->bus->dev->path.type != DEVICE_PATH_CPU_CLUSTER))
Eric Biederman7003ba42004-10-16 06:20:29 +0000156 {
Eric Biederman8ca8d762003-04-22 19:02:15 +0000157 continue;
Eric Biederman8ca8d762003-04-22 19:02:15 +0000158 }
Eric Biedermanb78c1972004-10-14 20:54:17 +0000159 if (!cpu->enabled) {
160 continue;
161 }
162 cpu_flag = MPC_CPU_ENABLED;
Stefan Reinauer2b34db82009-02-28 20:10:20 +0000163 if (boot_apic_id == cpu->path.apic.apic_id) {
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000164 cpu_flag = MPC_CPU_ENABLED | MPC_CPU_BOOTPROCESSOR;
Eric Biedermanb78c1972004-10-14 20:54:17 +0000165 }
Patrick Georgi07b42152011-10-14 23:02:57 +0200166 if(cpu->path.apic.apic_id == order_id) {
167 smp_write_processor(mc,
168 cpu->path.apic.apic_id, apic_version,
169 cpu_flag, cpu_features, cpu_feature_flags
170 );
171 break;
172 }
173 }
Eric Biederman8ca8d762003-04-22 19:02:15 +0000174 }
175}
176
Patrick Georgi patrick612fcc32010-11-23 07:19:54 +0000177static void smp_write_bus(struct mp_config_table *mc,
Myles Watson6e235762009-09-29 14:56:15 +0000178 unsigned char id, const char *bustype)
Eric Biederman8ca8d762003-04-22 19:02:15 +0000179{
180 struct mpc_config_bus *mpc;
181 mpc = smp_next_mpc_entry(mc);
182 memset(mpc, '\0', sizeof(*mpc));
183 mpc->mpc_type = MP_BUS;
184 mpc->mpc_busid = id;
185 memcpy(mpc->mpc_bustype, bustype, sizeof(mpc->mpc_bustype));
186 smp_add_mpc_entry(mc, sizeof(*mpc));
187}
188
189void smp_write_ioapic(struct mp_config_table *mc,
Stefan Reinauer14e22772010-04-27 06:56:47 +0000190 unsigned char id, unsigned char ver,
Eric Biedermanb78c1972004-10-14 20:54:17 +0000191 unsigned long apicaddr)
Eric Biederman8ca8d762003-04-22 19:02:15 +0000192{
193 struct mpc_config_ioapic *mpc;
194 mpc = smp_next_mpc_entry(mc);
195 memset(mpc, '\0', sizeof(*mpc));
196 mpc->mpc_type = MP_IOAPIC;
197 mpc->mpc_apicid = id;
198 mpc->mpc_apicver = ver;
199 mpc->mpc_flags = MPC_APIC_USABLE;
200 mpc->mpc_apicaddr = apicaddr;
201 smp_add_mpc_entry(mc, sizeof(*mpc));
202}
203
204void smp_write_intsrc(struct mp_config_table *mc,
205 unsigned char irqtype, unsigned short irqflag,
206 unsigned char srcbus, unsigned char srcbusirq,
207 unsigned char dstapic, unsigned char dstirq)
208{
209 struct mpc_config_intsrc *mpc;
210 mpc = smp_next_mpc_entry(mc);
211 memset(mpc, '\0', sizeof(*mpc));
212 mpc->mpc_type = MP_INTSRC;
213 mpc->mpc_irqtype = irqtype;
214 mpc->mpc_irqflag = irqflag;
215 mpc->mpc_srcbus = srcbus;
216 mpc->mpc_srcbusirq = srcbusirq;
217 mpc->mpc_dstapic = dstapic;
218 mpc->mpc_dstirq = dstirq;
219 smp_add_mpc_entry(mc, sizeof(*mpc));
Stefan Reinauer5bba7522009-04-21 23:00:14 +0000220#ifdef DEBUG_MPTABLE
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000221 printk(BIOS_DEBUG, "add intsrc srcbus 0x%x srcbusirq 0x%x, dstapic 0x%x, dstirq 0x%x\n",
Eric Biederman8ca8d762003-04-22 19:02:15 +0000222 srcbus, srcbusirq, dstapic, dstirq);
Myles Watson552b3272009-02-12 21:30:06 +0000223 hexdump(__func__, mpc, sizeof(*mpc));
Eric Biederman8ca8d762003-04-22 19:02:15 +0000224#endif
225}
226
Yinghai Luf327d9f2008-02-20 17:41:38 +0000227void smp_write_intsrc_pci_bridge(struct mp_config_table *mc,
228 unsigned char irqtype, unsigned short irqflag,
229 struct device *dev,
230 unsigned char dstapic, unsigned char *dstirq)
231{
232 struct device *child;
233
Yinghai Luf327d9f2008-02-20 17:41:38 +0000234 int i;
235 int srcbus;
236 int slot;
237
238 struct bus *link;
239 unsigned char dstirq_x[4];
240
Myles Watson894a3472010-06-09 22:41:35 +0000241 for (link = dev->link_list; link; link = link->next) {
Yinghai Luf327d9f2008-02-20 17:41:38 +0000242
Yinghai Luf327d9f2008-02-20 17:41:38 +0000243 child = link->children;
244 srcbus = link->secondary;
245
246 while (child) {
247 if (child->path.type != DEVICE_PATH_PCI)
248 goto next;
249
Stefan Reinauer2b34db82009-02-28 20:10:20 +0000250 slot = (child->path.pci.devfn >> 3);
Yinghai Luf327d9f2008-02-20 17:41:38 +0000251 /* round pins */
252 for (i = 0; i < 4; i++)
253 dstirq_x[i] = dstirq[(i + slot) % 4];
254
255 if ((child->class >> 16) != PCI_BASE_CLASS_BRIDGE) {
256 /* pci device */
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000257 printk(BIOS_DEBUG, "route irq: %s\n", dev_path(child));
Yinghai Luf327d9f2008-02-20 17:41:38 +0000258 for (i = 0; i < 4; i++)
259 smp_write_intsrc(mc, irqtype, irqflag, srcbus, (slot<<2)|i, dstapic, dstirq_x[i]);
260 goto next;
261 }
262
263 switch (child->class>>8) {
264 case PCI_CLASS_BRIDGE_PCI:
265 case PCI_CLASS_BRIDGE_PCMCIA:
266 case PCI_CLASS_BRIDGE_CARDBUS:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000267 printk(BIOS_DEBUG, "route irq bridge: %s\n", dev_path(child));
Yinghai Luf327d9f2008-02-20 17:41:38 +0000268 smp_write_intsrc_pci_bridge(mc, irqtype, irqflag, child, dstapic, dstirq_x);
269 }
270
Patrick Georgic5fc7db2012-03-07 15:55:47 +0100271next:
Yinghai Luf327d9f2008-02-20 17:41:38 +0000272 child = child->sibling;
273 }
274
275 }
276}
Eric Biederman8ca8d762003-04-22 19:02:15 +0000277
278void smp_write_lintsrc(struct mp_config_table *mc,
279 unsigned char irqtype, unsigned short irqflag,
280 unsigned char srcbusid, unsigned char srcbusirq,
281 unsigned char destapic, unsigned char destapiclint)
282{
283 struct mpc_config_lintsrc *mpc;
284 mpc = smp_next_mpc_entry(mc);
285 memset(mpc, '\0', sizeof(*mpc));
286 mpc->mpc_type = MP_LINTSRC;
287 mpc->mpc_irqtype = irqtype;
288 mpc->mpc_irqflag = irqflag;
289 mpc->mpc_srcbusid = srcbusid;
290 mpc->mpc_srcbusirq = srcbusirq;
291 mpc->mpc_destapic = destapic;
292 mpc->mpc_destapiclint = destapiclint;
293 smp_add_mpc_entry(mc, sizeof(*mpc));
294}
295
296void smp_write_address_space(struct mp_config_table *mc,
297 unsigned char busid, unsigned char address_type,
298 unsigned int address_base_low, unsigned int address_base_high,
299 unsigned int address_length_low, unsigned int address_length_high)
300{
301 struct mp_exten_system_address_space *mpe;
302 mpe = smp_next_mpe_entry(mc);
303 memset(mpe, '\0', sizeof(*mpe));
304 mpe->mpe_type = MPE_SYSTEM_ADDRESS_SPACE;
305 mpe->mpe_length = sizeof(*mpe);
306 mpe->mpe_busid = busid;
307 mpe->mpe_address_type = address_type;
308 mpe->mpe_address_base_low = address_base_low;
309 mpe->mpe_address_base_high = address_base_high;
310 mpe->mpe_address_length_low = address_length_low;
311 mpe->mpe_address_length_high = address_length_high;
312 smp_add_mpe_entry(mc, (mpe_t)mpe);
313}
314
315
316void smp_write_bus_hierarchy(struct mp_config_table *mc,
317 unsigned char busid, unsigned char bus_info,
318 unsigned char parent_busid)
319{
320 struct mp_exten_bus_hierarchy *mpe;
321 mpe = smp_next_mpe_entry(mc);
322 memset(mpe, '\0', sizeof(*mpe));
323 mpe->mpe_type = MPE_BUS_HIERARCHY;
324 mpe->mpe_length = sizeof(*mpe);
325 mpe->mpe_busid = busid;
326 mpe->mpe_bus_info = bus_info;
327 mpe->mpe_parent_busid = parent_busid;
328 smp_add_mpe_entry(mc, (mpe_t)mpe);
329}
330
331void smp_write_compatibility_address_space(struct mp_config_table *mc,
332 unsigned char busid, unsigned char address_modifier,
333 unsigned int range_list)
334{
335 struct mp_exten_compatibility_address_space *mpe;
336 mpe = smp_next_mpe_entry(mc);
337 memset(mpe, '\0', sizeof(*mpe));
338 mpe->mpe_type = MPE_COMPATIBILITY_ADDRESS_SPACE;
339 mpe->mpe_length = sizeof(*mpe);
340 mpe->mpe_busid = busid;
341 mpe->mpe_address_modifier = address_modifier;
342 mpe->mpe_range_list = range_list;
343 smp_add_mpe_entry(mc, (mpe_t)mpe);
344}
345
Patrick Georgi6eb7a532011-10-07 21:42:52 +0200346void mptable_lintsrc(struct mp_config_table *mc, unsigned long bus_isa)
347{
348 smp_write_lintsrc(mc, mp_ExtINT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x0, MP_APIC_ALL, 0x0);
349 smp_write_lintsrc(mc, mp_NMI, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x0, MP_APIC_ALL, 0x1);
350}
351
Patrick Georgic5b87c82010-05-20 15:28:19 +0000352void mptable_add_isa_interrupts(struct mp_config_table *mc, unsigned long bus_isa, unsigned long apicid, int external_int2)
353{
354/*I/O Ints: Type Trigger Polarity Bus ID IRQ APIC ID PIN# */
355 smp_write_intsrc(mc, external_int2?mp_INT:mp_ExtINT,
356 MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x0, apicid, 0x0);
357 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x1, apicid, 0x1);
358 smp_write_intsrc(mc, external_int2?mp_ExtINT:mp_INT,
359 MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x0, apicid, 0x2);
360 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x3, apicid, 0x3);
361 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x4, apicid, 0x4);
362 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x6, apicid, 0x6);
363 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x7, apicid, 0x7);
364 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x8, apicid, 0x8);
365 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0x9, apicid, 0x9);
366 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xa, apicid, 0xa);
367 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xb, apicid, 0xb);
368 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xc, apicid, 0xc);
369 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xd, apicid, 0xd);
370 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xe, apicid, 0xe);
371 smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, bus_isa, 0xf, apicid, 0xf);
372}
Patrick Georgiab272662010-06-25 13:43:22 +0000373
374void mptable_write_buses(struct mp_config_table *mc, int *max_pci_bus, int *isa_bus) {
375 int dummy, i, highest;
376 char buses[256];
377 struct device *dev;
378
379 if (!max_pci_bus) max_pci_bus = &dummy;
380 if (!isa_bus) isa_bus = &dummy;
381
382 *max_pci_bus = 0;
383 highest = 0;
384 memset(buses, 0, sizeof(buses));
385
386 for (dev = all_devices; dev; dev = dev->next) {
387 struct bus *bus;
388 for (bus = dev->link_list; bus; bus = bus->next) {
389 if (bus->secondary > 255) {
390 printk(BIOS_ERR, "A bus claims to have a bus ID > 255?!? Aborting");
391 return;
392 }
393 buses[bus->secondary] = 1;
394 if (highest < bus->secondary) highest = bus->secondary;
395 }
396 }
397 for (i=0; i <= highest; i++) {
398 if (buses[i]) {
399 smp_write_bus(mc, i, "PCI ");
400 *max_pci_bus = i;
401 }
402 }
403 *isa_bus = *max_pci_bus + 1;
404 smp_write_bus(mc, *isa_bus, "ISA ");
405}
406
Patrick Georgib0a9c5c2011-10-07 23:01:55 +0200407void *mptable_finalize(struct mp_config_table *mc)
408{
409 mc->mpe_checksum = smp_compute_checksum(smp_next_mpc_entry(mc), mc->mpe_length);
410 mc->mpc_checksum = smp_compute_checksum(mc, mc->mpc_length);
411 printk(BIOS_DEBUG, "Wrote the mp table end at: %p - %p\n", mc, smp_next_mpe_entry(mc));
412 return smp_next_mpe_entry(mc);
413}
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200414
415unsigned long __attribute__((weak)) write_smp_table(unsigned long addr)
416{
417 struct drivers_generic_ioapic_config *ioapic_config;
418 struct mp_config_table *mc;
419 int isa_bus, pin, parentpin;
420 device_t dev, parent, oldparent;
421 void *tmp, *v;
Sven Schnelle8c027902012-08-20 11:19:00 +0200422 int isaioapic = -1, have_fixed_entries;
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200423
424 v = smp_write_floating_table(addr, 0);
425 mc = (void *)(((char *)v) + SMP_FLOATING_TABLE_LEN);
426
427 mptable_init(mc, LOCAL_APIC_ADDR);
428
Alexandru Gagniuc0d5d70b2012-08-21 17:08:03 -0500429 smp_write_processors(mc);
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200430
431 mptable_write_buses(mc, NULL, &isa_bus);
432
433 for(dev = all_devices; dev; dev = dev->next) {
434 if (dev->path.type != DEVICE_PATH_IOAPIC)
435 continue;
436
437 if (!(ioapic_config = dev->chip_info)) {
438 printk(BIOS_ERR, "%s has no config, ignoring\n", dev_path(dev));
439 continue;
440 }
441 smp_write_ioapic(mc, dev->path.ioapic.ioapic_id,
442 ioapic_config->version,
443 ioapic_config->base);
444
445 if (ioapic_config->have_isa_interrupts) {
Alexandru Gagniuc0d5d70b2012-08-21 17:08:03 -0500446 if (isaioapic >= 0)
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200447 printk(BIOS_ERR, "More than one IOAPIC with ISA interrupts?\n");
448 else
Alexandru Gagniuc0d5d70b2012-08-21 17:08:03 -0500449 isaioapic = dev->path.ioapic.ioapic_id;
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200450 }
451 }
452
453 if (isaioapic >= 0) {
454 /* Legacy Interrupts */
Alexandru Gagniuc0d5d70b2012-08-21 17:08:03 -0500455 printk(BIOS_DEBUG, "Writing ISA IRQs\n");
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200456 mptable_add_isa_interrupts(mc, isa_bus, isaioapic, 0);
457 }
458
459 for(dev = all_devices; dev; dev = dev->next) {
460
Sven Schnelle64c40dd2012-08-20 11:17:52 +0200461 if (dev->path.type != DEVICE_PATH_PCI || !dev->enabled)
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200462 continue;
463
Sven Schnelle8c027902012-08-20 11:19:00 +0200464 have_fixed_entries = 0;
465 for (pin = 0; pin < 4; pin++) {
466 if (dev->pci_irq_info[pin].ioapic_dst_id) {
467 printk(BIOS_DEBUG, "fixed IRQ entry for: %s: INT%c# -> IOAPIC %d PIN %d\n", dev_path(dev),
468 pin + 'A',
469 dev->pci_irq_info[pin].ioapic_dst_id,
470 dev->pci_irq_info[pin].ioapic_irq_pin);
471 smp_write_intsrc(mc, mp_INT,
472 dev->pci_irq_info[pin].ioapic_flags,
473 dev->bus->secondary,
474 ((dev->path.pci.devfn & 0xf8) >> 1) | pin,
475 dev->pci_irq_info[pin].ioapic_dst_id,
476 dev->pci_irq_info[pin].ioapic_irq_pin);
477 have_fixed_entries = 1;
478 }
479 }
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200480
Sven Schnelle8c027902012-08-20 11:19:00 +0200481 if (!have_fixed_entries) {
482 pin = (dev->path.pci.devfn & 7) % 4;
483 oldparent = parent = dev;
484 while((parent = parent->bus->dev)) {
485 parentpin = (oldparent->path.pci.devfn >> 3) + (oldparent->path.pci.devfn & 7);
486 parentpin += dev->path.pci.devfn & 7;
487 parentpin += dev->path.pci.devfn >> 3;
488 parentpin %= 4;
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200489
Sven Schnelle8c027902012-08-20 11:19:00 +0200490 if (parent->pci_irq_info[parentpin].ioapic_dst_id) {
491 printk(BIOS_DEBUG, "automatic IRQ entry for %s: INT%c# -> IOAPIC %d PIN %d\n",
492 dev_path(dev), pin + 'A',
493 parent->pci_irq_info[parentpin].ioapic_dst_id,
494 parent->pci_irq_info[parentpin].ioapic_irq_pin);
495 smp_write_intsrc(mc, mp_INT,
496 parent->pci_irq_info[parentpin].ioapic_flags,
497 dev->bus->secondary,
498 ((dev->path.pci.devfn & 0xf8) >> 1) | pin,
499 parent->pci_irq_info[parentpin].ioapic_dst_id,
500 parent->pci_irq_info[parentpin].ioapic_irq_pin);
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200501
Sven Schnelle8c027902012-08-20 11:19:00 +0200502 break;
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200503 }
Sven Schnelle8c027902012-08-20 11:19:00 +0200504
Stefan Reinauer4aff4452013-02-12 14:17:15 -0800505 if (parent->path.type == DEVICE_PATH_DOMAIN) {
Sven Schnelle8c027902012-08-20 11:19:00 +0200506 printk(BIOS_WARNING, "no IRQ found for %s\n", dev_path(dev));
507 break;
508 }
509 oldparent = parent;
510 }
Sven Schnelle0fa50a12012-06-21 22:19:48 +0200511 }
512 }
513
514 mptable_lintsrc(mc, isa_bus);
515 tmp = mptable_finalize(mc);
516 printk(BIOS_INFO, "MPTABLE len: %d\n", (unsigned int)tmp - (unsigned int)v);
517 return (unsigned long)tmp;
518}