17f5df8d4SBin Meng /*
27f5df8d4SBin Meng * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
37f5df8d4SBin Meng *
47f5df8d4SBin Meng * Adapted from coreboot src/arch/x86/boot/mpspec.c
57f5df8d4SBin Meng *
67f5df8d4SBin Meng * SPDX-License-Identifier: GPL-2.0+
77f5df8d4SBin Meng */
87f5df8d4SBin Meng
97f5df8d4SBin Meng #include <common.h>
107f5df8d4SBin Meng #include <cpu.h>
117f5df8d4SBin Meng #include <dm.h>
1207545d86SBin Meng #include <errno.h>
1307545d86SBin Meng #include <fdtdec.h>
147f5df8d4SBin Meng #include <asm/cpu.h>
1507545d86SBin Meng #include <asm/irq.h>
167f5df8d4SBin Meng #include <asm/ioapic.h>
177f5df8d4SBin Meng #include <asm/lapic.h>
187f5df8d4SBin Meng #include <asm/mpspec.h>
197f5df8d4SBin Meng #include <asm/tables.h>
207f5df8d4SBin Meng #include <dm/uclass-internal.h>
217f5df8d4SBin Meng
2207545d86SBin Meng DECLARE_GLOBAL_DATA_PTR;
2307545d86SBin Meng
2453832bb8SBin Meng static bool isa_irq_occupied[16];
2553832bb8SBin Meng
mp_write_floating_table(struct mp_floating_table * mf)267f5df8d4SBin Meng struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
277f5df8d4SBin Meng {
28e71ffd09SSimon Glass ulong mc;
297f5df8d4SBin Meng
307f5df8d4SBin Meng memcpy(mf->mpf_signature, MPF_SIGNATURE, 4);
31e71ffd09SSimon Glass mf->mpf_physptr = (ulong)mf + sizeof(struct mp_floating_table);
327f5df8d4SBin Meng mf->mpf_length = 1;
337f5df8d4SBin Meng mf->mpf_spec = MPSPEC_V14;
347f5df8d4SBin Meng mf->mpf_checksum = 0;
357f5df8d4SBin Meng /* We don't use the default configuration table */
367f5df8d4SBin Meng mf->mpf_feature1 = 0;
377f5df8d4SBin Meng /* Indicate that virtual wire mode is always implemented */
387f5df8d4SBin Meng mf->mpf_feature2 = 0;
397f5df8d4SBin Meng mf->mpf_feature3 = 0;
407f5df8d4SBin Meng mf->mpf_feature4 = 0;
417f5df8d4SBin Meng mf->mpf_feature5 = 0;
427f5df8d4SBin Meng mf->mpf_checksum = table_compute_checksum(mf, mf->mpf_length * 16);
437f5df8d4SBin Meng
44e71ffd09SSimon Glass mc = (ulong)mf + sizeof(struct mp_floating_table);
457f5df8d4SBin Meng return (struct mp_config_table *)mc;
467f5df8d4SBin Meng }
477f5df8d4SBin Meng
mp_config_table_init(struct mp_config_table * mc)487f5df8d4SBin Meng void mp_config_table_init(struct mp_config_table *mc)
497f5df8d4SBin Meng {
507f5df8d4SBin Meng memcpy(mc->mpc_signature, MPC_SIGNATURE, 4);
517f5df8d4SBin Meng mc->mpc_length = sizeof(struct mp_config_table);
527f5df8d4SBin Meng mc->mpc_spec = MPSPEC_V14;
537f5df8d4SBin Meng mc->mpc_checksum = 0;
547f5df8d4SBin Meng mc->mpc_oemptr = 0;
557f5df8d4SBin Meng mc->mpc_oemsize = 0;
567f5df8d4SBin Meng mc->mpc_entry_count = 0;
577f5df8d4SBin Meng mc->mpc_lapic = LAPIC_DEFAULT_BASE;
587f5df8d4SBin Meng mc->mpe_length = 0;
597f5df8d4SBin Meng mc->mpe_checksum = 0;
607f5df8d4SBin Meng mc->reserved = 0;
617f5df8d4SBin Meng
627f5df8d4SBin Meng /* The oem/product id fields are exactly 8/12 bytes long */
637f5df8d4SBin Meng table_fill_string(mc->mpc_oem, CONFIG_SYS_VENDOR, 8, ' ');
647f5df8d4SBin Meng table_fill_string(mc->mpc_product, CONFIG_SYS_BOARD, 12, ' ');
657f5df8d4SBin Meng }
667f5df8d4SBin Meng
mp_write_processor(struct mp_config_table * mc)677f5df8d4SBin Meng void mp_write_processor(struct mp_config_table *mc)
687f5df8d4SBin Meng {
697f5df8d4SBin Meng struct mpc_config_processor *mpc;
707f5df8d4SBin Meng struct udevice *dev;
717f5df8d4SBin Meng u8 boot_apicid, apicver;
727f5df8d4SBin Meng u32 cpusignature, cpufeature;
737f5df8d4SBin Meng struct cpuid_result result;
747f5df8d4SBin Meng
757f5df8d4SBin Meng boot_apicid = lapicid();
767f5df8d4SBin Meng apicver = lapic_read(LAPIC_LVR) & 0xff;
777f5df8d4SBin Meng result = cpuid(1);
787f5df8d4SBin Meng cpusignature = result.eax;
797f5df8d4SBin Meng cpufeature = result.edx;
807f5df8d4SBin Meng
817f5df8d4SBin Meng for (uclass_find_first_device(UCLASS_CPU, &dev);
827f5df8d4SBin Meng dev;
837f5df8d4SBin Meng uclass_find_next_device(&dev)) {
847f5df8d4SBin Meng struct cpu_platdata *plat = dev_get_parent_platdata(dev);
857f5df8d4SBin Meng u8 cpuflag = MPC_CPU_EN;
867f5df8d4SBin Meng
877f5df8d4SBin Meng if (!device_active(dev))
887f5df8d4SBin Meng continue;
897f5df8d4SBin Meng
907f5df8d4SBin Meng mpc = (struct mpc_config_processor *)mp_next_mpc_entry(mc);
917f5df8d4SBin Meng mpc->mpc_type = MP_PROCESSOR;
927f5df8d4SBin Meng mpc->mpc_apicid = plat->cpu_id;
937f5df8d4SBin Meng mpc->mpc_apicver = apicver;
947f5df8d4SBin Meng if (boot_apicid == plat->cpu_id)
957f5df8d4SBin Meng cpuflag |= MPC_CPU_BP;
967f5df8d4SBin Meng mpc->mpc_cpuflag = cpuflag;
977f5df8d4SBin Meng mpc->mpc_cpusignature = cpusignature;
987f5df8d4SBin Meng mpc->mpc_cpufeature = cpufeature;
997f5df8d4SBin Meng mpc->mpc_reserved[0] = 0;
1007f5df8d4SBin Meng mpc->mpc_reserved[1] = 0;
1017f5df8d4SBin Meng mp_add_mpc_entry(mc, sizeof(*mpc));
1027f5df8d4SBin Meng }
1037f5df8d4SBin Meng }
1047f5df8d4SBin Meng
mp_write_bus(struct mp_config_table * mc,int id,const char * bustype)1057f5df8d4SBin Meng void mp_write_bus(struct mp_config_table *mc, int id, const char *bustype)
1067f5df8d4SBin Meng {
1077f5df8d4SBin Meng struct mpc_config_bus *mpc;
1087f5df8d4SBin Meng
1097f5df8d4SBin Meng mpc = (struct mpc_config_bus *)mp_next_mpc_entry(mc);
1107f5df8d4SBin Meng mpc->mpc_type = MP_BUS;
1117f5df8d4SBin Meng mpc->mpc_busid = id;
1127f5df8d4SBin Meng memcpy(mpc->mpc_bustype, bustype, 6);
1137f5df8d4SBin Meng mp_add_mpc_entry(mc, sizeof(*mpc));
1147f5df8d4SBin Meng }
1157f5df8d4SBin Meng
mp_write_ioapic(struct mp_config_table * mc,int id,int ver,u32 apicaddr)1167f5df8d4SBin Meng void mp_write_ioapic(struct mp_config_table *mc, int id, int ver, u32 apicaddr)
1177f5df8d4SBin Meng {
1187f5df8d4SBin Meng struct mpc_config_ioapic *mpc;
1197f5df8d4SBin Meng
1207f5df8d4SBin Meng mpc = (struct mpc_config_ioapic *)mp_next_mpc_entry(mc);
1217f5df8d4SBin Meng mpc->mpc_type = MP_IOAPIC;
1227f5df8d4SBin Meng mpc->mpc_apicid = id;
1237f5df8d4SBin Meng mpc->mpc_apicver = ver;
1247f5df8d4SBin Meng mpc->mpc_flags = MPC_APIC_USABLE;
1257f5df8d4SBin Meng mpc->mpc_apicaddr = apicaddr;
1267f5df8d4SBin Meng mp_add_mpc_entry(mc, sizeof(*mpc));
1277f5df8d4SBin Meng }
1287f5df8d4SBin Meng
mp_write_intsrc(struct mp_config_table * mc,int irqtype,int irqflag,int srcbus,int srcbusirq,int dstapic,int dstirq)1297f5df8d4SBin Meng void mp_write_intsrc(struct mp_config_table *mc, int irqtype, int irqflag,
1307f5df8d4SBin Meng int srcbus, int srcbusirq, int dstapic, int dstirq)
1317f5df8d4SBin Meng {
1327f5df8d4SBin Meng struct mpc_config_intsrc *mpc;
1337f5df8d4SBin Meng
1347f5df8d4SBin Meng mpc = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
1357f5df8d4SBin Meng mpc->mpc_type = MP_INTSRC;
1367f5df8d4SBin Meng mpc->mpc_irqtype = irqtype;
1377f5df8d4SBin Meng mpc->mpc_irqflag = irqflag;
1387f5df8d4SBin Meng mpc->mpc_srcbus = srcbus;
1397f5df8d4SBin Meng mpc->mpc_srcbusirq = srcbusirq;
1407f5df8d4SBin Meng mpc->mpc_dstapic = dstapic;
1417f5df8d4SBin Meng mpc->mpc_dstirq = dstirq;
1427f5df8d4SBin Meng mp_add_mpc_entry(mc, sizeof(*mpc));
1437f5df8d4SBin Meng }
1447f5df8d4SBin Meng
mp_write_pci_intsrc(struct mp_config_table * mc,int irqtype,int srcbus,int dev,int pin,int dstapic,int dstirq)1457f5df8d4SBin Meng void mp_write_pci_intsrc(struct mp_config_table *mc, int irqtype,
1467f5df8d4SBin Meng int srcbus, int dev, int pin, int dstapic, int dstirq)
1477f5df8d4SBin Meng {
1487f5df8d4SBin Meng u8 srcbusirq = (dev << 2) | (pin - 1);
1497f5df8d4SBin Meng
1507f5df8d4SBin Meng mp_write_intsrc(mc, irqtype, MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_LOW,
1517f5df8d4SBin Meng srcbus, srcbusirq, dstapic, dstirq);
1527f5df8d4SBin Meng }
1537f5df8d4SBin Meng
mp_write_lintsrc(struct mp_config_table * mc,int irqtype,int irqflag,int srcbus,int srcbusirq,int destapic,int destlint)1547f5df8d4SBin Meng void mp_write_lintsrc(struct mp_config_table *mc, int irqtype, int irqflag,
1557f5df8d4SBin Meng int srcbus, int srcbusirq, int destapic, int destlint)
1567f5df8d4SBin Meng {
1577f5df8d4SBin Meng struct mpc_config_lintsrc *mpc;
1587f5df8d4SBin Meng
1597f5df8d4SBin Meng mpc = (struct mpc_config_lintsrc *)mp_next_mpc_entry(mc);
1607f5df8d4SBin Meng mpc->mpc_type = MP_LINTSRC;
1617f5df8d4SBin Meng mpc->mpc_irqtype = irqtype;
1627f5df8d4SBin Meng mpc->mpc_irqflag = irqflag;
1637f5df8d4SBin Meng mpc->mpc_srcbusid = srcbus;
1647f5df8d4SBin Meng mpc->mpc_srcbusirq = srcbusirq;
1657f5df8d4SBin Meng mpc->mpc_destapic = destapic;
1667f5df8d4SBin Meng mpc->mpc_destlint = destlint;
1677f5df8d4SBin Meng mp_add_mpc_entry(mc, sizeof(*mpc));
1687f5df8d4SBin Meng }
1697f5df8d4SBin Meng
mp_write_address_space(struct mp_config_table * mc,int busid,int addr_type,u32 addr_base_low,u32 addr_base_high,u32 addr_length_low,u32 addr_length_high)1707f5df8d4SBin Meng void mp_write_address_space(struct mp_config_table *mc,
1717f5df8d4SBin Meng int busid, int addr_type,
1727f5df8d4SBin Meng u32 addr_base_low, u32 addr_base_high,
1737f5df8d4SBin Meng u32 addr_length_low, u32 addr_length_high)
1747f5df8d4SBin Meng {
1757f5df8d4SBin Meng struct mp_ext_system_address_space *mpe;
1767f5df8d4SBin Meng
1777f5df8d4SBin Meng mpe = (struct mp_ext_system_address_space *)mp_next_mpe_entry(mc);
1787f5df8d4SBin Meng mpe->mpe_type = MPE_SYSTEM_ADDRESS_SPACE;
1797f5df8d4SBin Meng mpe->mpe_length = sizeof(*mpe);
1807f5df8d4SBin Meng mpe->mpe_busid = busid;
1817f5df8d4SBin Meng mpe->mpe_addr_type = addr_type;
1827f5df8d4SBin Meng mpe->mpe_addr_base_low = addr_base_low;
1837f5df8d4SBin Meng mpe->mpe_addr_base_high = addr_base_high;
1847f5df8d4SBin Meng mpe->mpe_addr_length_low = addr_length_low;
1857f5df8d4SBin Meng mpe->mpe_addr_length_high = addr_length_high;
1867f5df8d4SBin Meng mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
1877f5df8d4SBin Meng }
1887f5df8d4SBin Meng
mp_write_bus_hierarchy(struct mp_config_table * mc,int busid,int bus_info,int parent_busid)1897f5df8d4SBin Meng void mp_write_bus_hierarchy(struct mp_config_table *mc,
1907f5df8d4SBin Meng int busid, int bus_info, int parent_busid)
1917f5df8d4SBin Meng {
1927f5df8d4SBin Meng struct mp_ext_bus_hierarchy *mpe;
1937f5df8d4SBin Meng
1947f5df8d4SBin Meng mpe = (struct mp_ext_bus_hierarchy *)mp_next_mpe_entry(mc);
1957f5df8d4SBin Meng mpe->mpe_type = MPE_BUS_HIERARCHY;
1967f5df8d4SBin Meng mpe->mpe_length = sizeof(*mpe);
1977f5df8d4SBin Meng mpe->mpe_busid = busid;
1987f5df8d4SBin Meng mpe->mpe_bus_info = bus_info;
1997f5df8d4SBin Meng mpe->mpe_parent_busid = parent_busid;
2007f5df8d4SBin Meng mpe->reserved[0] = 0;
2017f5df8d4SBin Meng mpe->reserved[1] = 0;
2027f5df8d4SBin Meng mpe->reserved[2] = 0;
2037f5df8d4SBin Meng mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
2047f5df8d4SBin Meng }
2057f5df8d4SBin Meng
mp_write_compat_address_space(struct mp_config_table * mc,int busid,int addr_modifier,u32 range_list)2067f5df8d4SBin Meng void mp_write_compat_address_space(struct mp_config_table *mc, int busid,
2077f5df8d4SBin Meng int addr_modifier, u32 range_list)
2087f5df8d4SBin Meng {
2097f5df8d4SBin Meng struct mp_ext_compat_address_space *mpe;
2107f5df8d4SBin Meng
2117f5df8d4SBin Meng mpe = (struct mp_ext_compat_address_space *)mp_next_mpe_entry(mc);
2127f5df8d4SBin Meng mpe->mpe_type = MPE_COMPAT_ADDRESS_SPACE;
2137f5df8d4SBin Meng mpe->mpe_length = sizeof(*mpe);
2147f5df8d4SBin Meng mpe->mpe_busid = busid;
2157f5df8d4SBin Meng mpe->mpe_addr_modifier = addr_modifier;
2167f5df8d4SBin Meng mpe->mpe_range_list = range_list;
2177f5df8d4SBin Meng mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
2187f5df8d4SBin Meng }
2197f5df8d4SBin Meng
mptable_finalize(struct mp_config_table * mc)2207f5df8d4SBin Meng u32 mptable_finalize(struct mp_config_table *mc)
2217f5df8d4SBin Meng {
222e71ffd09SSimon Glass ulong end;
2237f5df8d4SBin Meng
2247f5df8d4SBin Meng mc->mpe_checksum = table_compute_checksum((void *)mp_next_mpc_entry(mc),
2257f5df8d4SBin Meng mc->mpe_length);
2267f5df8d4SBin Meng mc->mpc_checksum = table_compute_checksum(mc, mc->mpc_length);
2277f5df8d4SBin Meng end = mp_next_mpe_entry(mc);
2287f5df8d4SBin Meng
229e71ffd09SSimon Glass debug("Write the MP table at: %lx - %lx\n", (ulong)mc, end);
2307f5df8d4SBin Meng
2317f5df8d4SBin Meng return end;
2327f5df8d4SBin Meng }
23307545d86SBin Meng
mptable_add_isa_interrupts(struct mp_config_table * mc,int bus_isa,int apicid,int external_int2)23407545d86SBin Meng static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
23507545d86SBin Meng int apicid, int external_int2)
23607545d86SBin Meng {
23707545d86SBin Meng int i;
23807545d86SBin Meng
23907545d86SBin Meng mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
24007545d86SBin Meng MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
24107545d86SBin Meng bus_isa, 0, apicid, 0);
24207545d86SBin Meng mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
24307545d86SBin Meng bus_isa, 1, apicid, 1);
24407545d86SBin Meng mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
24507545d86SBin Meng MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
24607545d86SBin Meng bus_isa, 0, apicid, 2);
24707545d86SBin Meng
24853832bb8SBin Meng for (i = 3; i < 16; i++) {
24953832bb8SBin Meng /*
25053832bb8SBin Meng * Do not write ISA interrupt entry if it is already occupied
25153832bb8SBin Meng * by the platform devices.
25253832bb8SBin Meng */
25353832bb8SBin Meng if (isa_irq_occupied[i])
25453832bb8SBin Meng continue;
25553832bb8SBin Meng
25607545d86SBin Meng mp_write_intsrc(mc, MP_INT,
25707545d86SBin Meng MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
25807545d86SBin Meng bus_isa, i, apicid, i);
25907545d86SBin Meng }
26053832bb8SBin Meng }
26107545d86SBin Meng
26207545d86SBin Meng /*
26307545d86SBin Meng * Check duplicated I/O interrupt assignment table entry, to make sure
26407545d86SBin Meng * there is only one entry with the given bus, device and interrupt pin.
26507545d86SBin Meng */
check_dup_entry(struct mpc_config_intsrc * intsrc_base,int entry_num,int bus,int device,int pin)26607545d86SBin Meng static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
26707545d86SBin Meng int entry_num, int bus, int device, int pin)
26807545d86SBin Meng {
26907545d86SBin Meng struct mpc_config_intsrc *intsrc = intsrc_base;
27007545d86SBin Meng int i;
27107545d86SBin Meng
27207545d86SBin Meng for (i = 0; i < entry_num; i++) {
27307545d86SBin Meng if (intsrc->mpc_srcbus == bus &&
27407545d86SBin Meng intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
27507545d86SBin Meng break;
27607545d86SBin Meng intsrc++;
27707545d86SBin Meng }
27807545d86SBin Meng
27907545d86SBin Meng return (i == entry_num) ? false : true;
28007545d86SBin Meng }
28107545d86SBin Meng
282abab9128SBin Meng /* TODO: move this to driver model */
mp_determine_pci_dstirq(int bus,int dev,int func,int pirq)283abab9128SBin Meng __weak int mp_determine_pci_dstirq(int bus, int dev, int func, int pirq)
284abab9128SBin Meng {
285abab9128SBin Meng /* PIRQ[A-H] are connected to I/O APIC INTPIN#16-23 */
286abab9128SBin Meng return pirq + 16;
287abab9128SBin Meng }
288abab9128SBin Meng
mptable_add_intsrc(struct mp_config_table * mc,int bus_isa,int apicid)28907545d86SBin Meng static int mptable_add_intsrc(struct mp_config_table *mc,
29007545d86SBin Meng int bus_isa, int apicid)
29107545d86SBin Meng {
29207545d86SBin Meng struct mpc_config_intsrc *intsrc_base;
29307545d86SBin Meng int intsrc_entries = 0;
29407545d86SBin Meng const void *blob = gd->fdt_blob;
295b565d66dSSimon Glass struct udevice *dev;
29607545d86SBin Meng int len, count;
29707545d86SBin Meng const u32 *cell;
298b565d66dSSimon Glass int i, ret;
29907545d86SBin Meng
3003f603cbbSSimon Glass ret = uclass_first_device_err(UCLASS_IRQ, &dev);
301b565d66dSSimon Glass if (ret && ret != -ENODEV) {
30207545d86SBin Meng debug("%s: Cannot find irq router node\n", __func__);
303b565d66dSSimon Glass return ret;
30407545d86SBin Meng }
30507545d86SBin Meng
306b565d66dSSimon Glass /* Get I/O interrupt information from device tree */
307*e160f7d4SSimon Glass cell = fdt_getprop(blob, dev_of_offset(dev), "intel,pirq-routing",
308*e160f7d4SSimon Glass &len);
30907545d86SBin Meng if (!cell)
31007545d86SBin Meng return -ENOENT;
31107545d86SBin Meng
31207545d86SBin Meng if ((len % sizeof(struct pirq_routing)) == 0)
31307545d86SBin Meng count = len / sizeof(struct pirq_routing);
31407545d86SBin Meng else
31507545d86SBin Meng return -EINVAL;
31607545d86SBin Meng
31707545d86SBin Meng intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
31807545d86SBin Meng
31907545d86SBin Meng for (i = 0; i < count; i++) {
32007545d86SBin Meng struct pirq_routing pr;
321abab9128SBin Meng int bus, dev, func;
322abab9128SBin Meng int dstirq;
32307545d86SBin Meng
32407545d86SBin Meng pr.bdf = fdt_addr_to_cpu(cell[0]);
32507545d86SBin Meng pr.pin = fdt_addr_to_cpu(cell[1]);
32607545d86SBin Meng pr.pirq = fdt_addr_to_cpu(cell[2]);
327abab9128SBin Meng bus = PCI_BUS(pr.bdf);
328abab9128SBin Meng dev = PCI_DEV(pr.bdf);
329abab9128SBin Meng func = PCI_FUNC(pr.bdf);
33007545d86SBin Meng
33107545d86SBin Meng if (check_dup_entry(intsrc_base, intsrc_entries,
332abab9128SBin Meng bus, dev, pr.pin)) {
33307545d86SBin Meng debug("found entry for bus %d device %d INT%c, skipping\n",
334abab9128SBin Meng bus, dev, 'A' + pr.pin - 1);
33507545d86SBin Meng cell += sizeof(struct pirq_routing) / sizeof(u32);
33607545d86SBin Meng continue;
33707545d86SBin Meng }
33807545d86SBin Meng
339abab9128SBin Meng dstirq = mp_determine_pci_dstirq(bus, dev, func, pr.pirq);
34053832bb8SBin Meng /*
34153832bb8SBin Meng * For PIRQ which is connected to I/O APIC interrupt pin#0-15,
34253832bb8SBin Meng * mark it as occupied so that we can skip it later.
34353832bb8SBin Meng */
34453832bb8SBin Meng if (dstirq < 16)
34553832bb8SBin Meng isa_irq_occupied[dstirq] = true;
346abab9128SBin Meng mp_write_pci_intsrc(mc, MP_INT, bus, dev, pr.pin,
347abab9128SBin Meng apicid, dstirq);
34807545d86SBin Meng intsrc_entries++;
34907545d86SBin Meng cell += sizeof(struct pirq_routing) / sizeof(u32);
35007545d86SBin Meng }
35107545d86SBin Meng
35253832bb8SBin Meng /* Legacy Interrupts */
35353832bb8SBin Meng debug("Writing ISA IRQs\n");
35453832bb8SBin Meng mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
35553832bb8SBin Meng
35607545d86SBin Meng return 0;
35707545d86SBin Meng }
35807545d86SBin Meng
mptable_add_lintsrc(struct mp_config_table * mc,int bus_isa)35907545d86SBin Meng static void mptable_add_lintsrc(struct mp_config_table *mc, int bus_isa)
36007545d86SBin Meng {
36107545d86SBin Meng mp_write_lintsrc(mc, MP_EXTINT,
36207545d86SBin Meng MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
36307545d86SBin Meng bus_isa, 0, MP_APIC_ALL, 0);
36407545d86SBin Meng mp_write_lintsrc(mc, MP_NMI,
36507545d86SBin Meng MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
36607545d86SBin Meng bus_isa, 0, MP_APIC_ALL, 1);
36707545d86SBin Meng }
36807545d86SBin Meng
write_mp_table(ulong addr)36942fd8c19SSimon Glass ulong write_mp_table(ulong addr)
37007545d86SBin Meng {
37107545d86SBin Meng struct mp_config_table *mc;
37207545d86SBin Meng int ioapic_id, ioapic_ver;
37307545d86SBin Meng int bus_isa = 0xff;
37407545d86SBin Meng int ret;
375e71ffd09SSimon Glass ulong end;
37607545d86SBin Meng
37707545d86SBin Meng /* 16 byte align the table address */
37807545d86SBin Meng addr = ALIGN(addr, 16);
37907545d86SBin Meng
38007545d86SBin Meng /* Write floating table */
38107545d86SBin Meng mc = mp_write_floating_table((struct mp_floating_table *)addr);
38207545d86SBin Meng
38307545d86SBin Meng /* Write configuration table header */
38407545d86SBin Meng mp_config_table_init(mc);
38507545d86SBin Meng
38607545d86SBin Meng /* Write processor entry */
38707545d86SBin Meng mp_write_processor(mc);
38807545d86SBin Meng
38907545d86SBin Meng /* Write bus entry */
39007545d86SBin Meng mp_write_bus(mc, bus_isa, BUSTYPE_ISA);
39107545d86SBin Meng
39207545d86SBin Meng /* Write I/O APIC entry */
39307545d86SBin Meng ioapic_id = io_apic_read(IO_APIC_ID) >> 24;
39407545d86SBin Meng ioapic_ver = io_apic_read(IO_APIC_VER) & 0xff;
39507545d86SBin Meng mp_write_ioapic(mc, ioapic_id, ioapic_ver, IO_APIC_ADDR);
39607545d86SBin Meng
39707545d86SBin Meng /* Write I/O interrupt assignment entry */
39807545d86SBin Meng ret = mptable_add_intsrc(mc, bus_isa, ioapic_id);
39907545d86SBin Meng if (ret)
40007545d86SBin Meng debug("Failed to write I/O interrupt assignment table\n");
40107545d86SBin Meng
40207545d86SBin Meng /* Write local interrupt assignment entry */
40307545d86SBin Meng mptable_add_lintsrc(mc, bus_isa);
40407545d86SBin Meng
40507545d86SBin Meng /* Finalize the MP table */
40607545d86SBin Meng end = mptable_finalize(mc);
40707545d86SBin Meng
40807545d86SBin Meng return end;
40907545d86SBin Meng }
410