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>
This commit is contained in:
Bin Meng 2015-04-24 18:10:05 +08:00 committed by Simon Glass
parent 5e2400e8f8
commit b5b6b01960
4 changed files with 300 additions and 0 deletions

View File

@ -442,6 +442,37 @@ config TSC_FREQ_IN_MHZ
help
The running frequency in MHz of Time-Stamp Counter (TSC).
menu "System tables"
config GENERATE_PIRQ_TABLE
bool "Generate a PIRQ table"
default n
help
Generate a PIRQ routing table for this board. The PIRQ routing table
is generated by U-Boot in the system memory from 0xf0000 to 0xfffff
at every 16-byte boundary with a PCI IRQ routing signature ("$PIR").
It specifies the interrupt router information as well how all the PCI
devices' interrupt pins are wired to PIRQs.
endmenu
config MAX_PIRQ_LINKS
int
default 8
help
This variable specifies the number of PIRQ interrupt links which are
routable. On most older chipsets, this is 4, PIRQA through PIRQD.
Some newer chipsets offer more than four links, commonly up to PIRQH.
config IRQ_SLOT_COUNT
int
default 128
help
U-Boot can support up to 254 IRQ slot info in the PIRQ routing table
which in turns forms a table of exact 4KiB. The default value 128
should be enough for most boards. If this does not fit your board,
change it according to your needs.
source "board/coreboot/coreboot/Kconfig"
source "board/google/chromebook_link/Kconfig"

View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* Ported from coreboot src/arch/x86/include/arch/pirq_routing.h
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _PIRQ_ROUTING_H_
#define _PIRQ_ROUTING_H_
/*
* This is the maximum number on interrupt entries that a PCI device may have.
* This is NOT the number of slots or devices in the system
* This is NOT the number of entries in the PIRQ table
*
* This tells us that in the PIRQ table, we are going to have 4 link-bitmap
* entries per PCI device which is fixed at 4: INTA, INTB, INTC, and INTD.
*
* CAUTION: If you change this, PIRQ routing will not work correctly.
*/
#define MAX_INTX_ENTRIES 4
#define PIRQ_SIGNATURE \
(('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
#define PIRQ_VERSION 0x0100
struct __packed irq_info {
u8 bus; /* Bus number */
u8 devfn; /* Device and function number */
struct __packed {
u8 link; /* IRQ line ID, 0=not routed */
u16 bitmap; /* Available IRQs */
} irq[MAX_INTX_ENTRIES];
u8 slot; /* Slot number, 0=onboard */
u8 rfu;
};
struct __packed irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus; /* busno of the interrupt router */
u8 rtr_devfn; /* devfn of the interrupt router */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor; /* Vendor ID of the interrupt router */
u16 rtr_device; /* Device ID of the interrupt router */
u32 miniport_data;
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[CONFIG_IRQ_SLOT_COUNT];
};
/**
* get_irq_slot_count() - Get the number of entries in the irq_info table
*
* This calculates the number of entries for the irq_info table.
*
* @rt: pointer to the base address of the struct irq_info
* @return: number of entries
*/
static inline int get_irq_slot_count(struct irq_routing_table *rt)
{
return (rt->size - 32) / sizeof(struct irq_info);
}
/**
* pirq_check_irq_routed() - Check whether an IRQ is routed to 8259 PIC
*
* This function checks whether an IRQ is routed to 8259 PIC for a given link.
*
* Note: this function should be provided by the platform codes, as the
* implementation of interrupt router may be different.
*
* @link: link number which represents a PIRQ
* @irq: the 8259 IRQ number
* @return: true if the irq is already routed to 8259 for a given link,
* false elsewise
*/
bool pirq_check_irq_routed(int link, u8 irq);
/**
* pirq_translate_link() - Translate a link value
*
* This function translates a platform-specific link value to a link number.
* On Intel platforms, the link value is normally a offset into the PCI
* configuration space into the legacy bridge.
*
* Note: this function should be provided by the platform codes, as the
* implementation of interrupt router may be different.
*
* @link: platform-specific link value
* @return: link number which represents a PIRQ
*/
int pirq_translate_link(int link);
/**
* pirq_assign_irq() - Assign an IRQ to a PIRQ link
*
* This function assigns the IRQ to a PIRQ link so that the PIRQ is routed to
* the 8259 PIC.
*
* Note: this function should be provided by the platform codes, as the
* implementation of interrupt router may be different.
*
* @link: link number which represents a PIRQ
* @irq: IRQ to which the PIRQ is routed
*/
void pirq_assign_irq(int link, u8 irq);
/**
* pirq_route_irqs() - Route PIRQs to 8259 PIC
*
* This function configures all PCI devices' interrupt pins and maps them to
* PIRQs and finally 8259 PIC. The routed irq number is written to interrupt
* line register in the configuration space of the PCI device for OS to use.
* The configuration source is taken from a struct irq_info table, the format
* of which is defined in PIRQ routing table spec and PCI BIOS spec.
*
* @irq: pointer to the base address of the struct irq_info
* @num: number of entries in the struct irq_info
*/
void pirq_route_irqs(struct irq_info *irq, int num);
/**
* copy_pirq_routing_table() - Copy a PIRQ routing table
*
* This helper function copies the given PIRQ routing table to a given address.
* Before copying, it does several sanity tests against the PIRQ routing table.
* It also fixes up the table checksum and align the given address to a 16 byte
* boundary to meet the PIRQ routing table spec requirements.
*
* @addr: address to store the copied PIRQ routing table
* @rt: pointer to the PIRQ routing table to copy from
* @return: end address of the copied PIRQ routing table
*/
u32 copy_pirq_routing_table(u32 addr, struct irq_routing_table *rt);
#endif /* _PIRQ_ROUTING_H_ */

View File

@ -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

129
arch/x86/lib/pirq_routing.c Normal file
View File

@ -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;
}