]> git.sur5r.net Git - u-boot/commitdiff
x86: Support platform PIRQ routing
authorBin Meng <bmeng.cn@gmail.com>
Fri, 24 Apr 2015 10:10:05 +0000 (18:10 +0800)
committerSimon Glass <sjg@chromium.org>
Thu, 30 Apr 2015 00:51:49 +0000 (18:51 -0600)
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>
arch/x86/Kconfig
arch/x86/include/asm/pirq_routing.h [new file with mode: 0644]
arch/x86/lib/Makefile
arch/x86/lib/pirq_routing.c [new file with mode: 0644]

index 3f1401ae4d35427ce704bab845d718153e72d891..aaceaef8b5e9f438370d32718c8e22f982167e47 100644 (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"
diff --git a/arch/x86/include/asm/pirq_routing.h b/arch/x86/include/asm/pirq_routing.h
new file mode 100644 (file)
index 0000000..ddc08e1
--- /dev/null
@@ -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_ */
index 0de0d898d9bc8e1eefa5a266192b627b498c8409..0178fe1b888fc373d2c847c434023b0077491bf4 100644 (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
diff --git a/arch/x86/lib/pirq_routing.c b/arch/x86/lib/pirq_routing.c
new file mode 100644 (file)
index 0000000..5a2591a
--- /dev/null
@@ -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;
+}