diff options
author | Bin Meng | 2015-04-24 18:10:05 +0800 |
---|---|---|
committer | Simon Glass | 2015-04-29 18:51:49 -0600 |
commit | b5b6b0196017da0c2b4d483a2cd59be7810c1d7a (patch) | |
tree | 99b3f12b2f5f5f207fbec089a184562847cc990e /arch/x86/lib | |
parent | 5e2400e8f873a1b7ee840554e4157c5f7900a863 (diff) |
x86: Support platform PIRQ routing
On x86 boards, platform chipset receives up to four different
interrupt signals from PCI devices (INTA/B/C/D), which in turn
will be routed to chipset internal PIRQ lines then routed to
8259 PIC finally if configuring the whole system to work under
the so-called PIC mode (in contrast to symmetric IO mode which
uses IOAPIC).
We add two major APIs to aid this, one for routing PIRQ and the
other one for generating a PIRQ routing table.
Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Acked-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'arch/x86/lib')
-rw-r--r-- | arch/x86/lib/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/lib/pirq_routing.c | 129 |
2 files changed, 130 insertions, 0 deletions
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 0de0d898d9b..0178fe1b888 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -22,6 +22,7 @@ ifndef CONFIG_DM_PCI obj-$(CONFIG_PCI) += pci_type1.o endif obj-y += pch-uclass.o +obj-y += pirq_routing.o obj-y += relocate.o obj-y += physmem.o obj-$(CONFIG_X86_RAMTEST) += ramtest.o diff --git a/arch/x86/lib/pirq_routing.c b/arch/x86/lib/pirq_routing.c new file mode 100644 index 00000000000..5a2591a26cd --- /dev/null +++ b/arch/x86/lib/pirq_routing.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Part of this file is ported from coreboot src/arch/x86/boot/pirq_routing.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <pci.h> +#include <asm/pci.h> +#include <asm/pirq_routing.h> +#include <asm/tables.h> + +static bool irq_already_routed[16]; + +static u8 pirq_get_next_free_irq(u8 *pirq, u16 bitmap) +{ + int i, link; + u8 irq = 0; + + /* IRQ sharing starts from IRQ#3 */ + for (i = 3; i < 16; i++) { + /* Can we assign this IRQ? */ + if (!((bitmap >> i) & 1)) + continue; + + /* We can, now let's assume we can use this IRQ */ + irq = i; + + /* Have we already routed it? */ + if (irq_already_routed[irq]) + continue; + + for (link = 0; link < CONFIG_MAX_PIRQ_LINKS; link++) { + if (pirq_check_irq_routed(link, irq)) { + irq_already_routed[irq] = true; + break; + } + } + + /* If it's not yet routed, use it */ + if (!irq_already_routed[irq]) { + irq_already_routed[irq] = true; + break; + } + + /* But if it was already routed, try the next one */ + } + + /* Now we get our IRQ */ + return irq; +} + +void pirq_route_irqs(struct irq_info *irq, int num) +{ + unsigned char irq_slot[MAX_INTX_ENTRIES]; + unsigned char pirq[CONFIG_MAX_PIRQ_LINKS]; + int i, intx; + + memset(pirq, 0, CONFIG_MAX_PIRQ_LINKS); + + /* Set PCI IRQs */ + for (i = 0; i < num; i++) { + debug("PIRQ Entry %d Dev: %d.%x.%d\n", i, + irq->bus, irq->devfn >> 3, irq->devfn & 7); + + for (intx = 0; intx < MAX_INTX_ENTRIES; intx++) { + int link = irq->irq[intx].link; + int bitmap = irq->irq[intx].bitmap; + int irq = 0; + + debug("INT%c link: %x bitmap: %x ", + 'A' + intx, link, bitmap); + + if (!bitmap || !link) { + debug("not routed\n"); + irq_slot[intx] = irq; + continue; + } + + /* translate link value to link number */ + link = pirq_translate_link(link); + + /* yet not routed */ + if (!pirq[link]) { + irq = pirq_get_next_free_irq(pirq, bitmap); + pirq[link] = irq; + } else { + irq = pirq[link]; + } + + debug("IRQ: %d\n", irq); + irq_slot[intx] = irq; + + /* Assign IRQ in the interrupt router */ + pirq_assign_irq(link, irq); + } + + /* Bus, device, slots IRQs for {A,B,C,D} */ + pci_assign_irqs(irq->bus, irq->devfn >> 3, irq->devfn & 7, + irq_slot); + + irq++; + } + + for (i = 0; i < CONFIG_MAX_PIRQ_LINKS; i++) + debug("PIRQ%c: %d\n", 'A' + i, pirq[i]); +} + +u32 copy_pirq_routing_table(u32 addr, struct irq_routing_table *rt) +{ + if (rt->signature != PIRQ_SIGNATURE || rt->version != PIRQ_VERSION || + rt->size % 16) { + debug("Interrupt Routing Table not valid\n"); + return addr; + } + + /* Fix up the table checksum */ + rt->checksum = table_compute_checksum(rt, rt->size); + + /* Align the table to be 16 byte aligned */ + addr = ALIGN(addr, 16); + + debug("Copying Interrupt Routing Table to 0x%x\n", addr); + memcpy((void *)addr, rt, rt->size); + + return addr + rt->size; +} |