All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 02/10] x86: queensbay: Correct Topcliff device irqs
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
@ 2015-06-15  7:59 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  2015-06-15  7:59 ` [U-Boot] [PATCH 03/10] x86: Write correct bus number for the irq router Bin Meng
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  7:59 UTC (permalink / raw)
  To: u-boot

There are 4 usb ports on the Intel Crown Bay board, 2 of which are
connected to Topcliff usb host 0 and the other 2 connected to usb
host 1. USB devices inserted in the ports connected to usb host 1
cannot get detected due to wrong IRQ assigned to the controller.
Actually we need apply the PCI interrupt pin swizzling logic to all
devices on the Topcliff chipset when configuring the PIRQ routing.

This was observed on usb ports, but device 6 and 10 irqs are also
wrong. Correct them all together.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/dts/crownbay.dts | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/arch/x86/dts/crownbay.dts b/arch/x86/dts/crownbay.dts
index 1ec90cd..e20eb24 100644
--- a/arch/x86/dts/crownbay.dts
+++ b/arch/x86/dts/crownbay.dts
@@ -179,29 +179,29 @@
 				 * Note on the Crown Bay board, Topcliff chipset
 				 * is connected to TunnelCreek PCIe port 0, so
 				 * its bus number is 1 for its PCIe port and 2
-				 * for its PCI devices per U-Boot currnet PCI
+				 * for its PCI devices per U-Boot current PCI
 				 * bus enumeration algorithm.
 				 */
 				PCI_BDF(1, 0, 0) INTA PIRQA
 				PCI_BDF(2, 0, 1) INTA PIRQA
 				PCI_BDF(2, 0, 2) INTA PIRQA
-				PCI_BDF(2, 2, 0) INTB PIRQB
-				PCI_BDF(2, 2, 1) INTB PIRQB
-				PCI_BDF(2, 2, 2) INTB PIRQB
-				PCI_BDF(2, 2, 3) INTB PIRQB
-				PCI_BDF(2, 2, 4) INTB PIRQB
+				PCI_BDF(2, 2, 0) INTB PIRQD
+				PCI_BDF(2, 2, 1) INTB PIRQD
+				PCI_BDF(2, 2, 2) INTB PIRQD
+				PCI_BDF(2, 2, 3) INTB PIRQD
+				PCI_BDF(2, 2, 4) INTB PIRQD
 				PCI_BDF(2, 4, 0) INTC PIRQC
 				PCI_BDF(2, 4, 1) INTC PIRQC
-				PCI_BDF(2, 6, 0) INTD PIRQD
+				PCI_BDF(2, 6, 0) INTD PIRQB
 				PCI_BDF(2, 8, 0) INTA PIRQA
 				PCI_BDF(2, 8, 1) INTA PIRQA
 				PCI_BDF(2, 8, 2) INTA PIRQA
 				PCI_BDF(2, 8, 3) INTA PIRQA
-				PCI_BDF(2, 10, 0) INTB PIRQB
-				PCI_BDF(2, 10, 1) INTB PIRQB
-				PCI_BDF(2, 10, 2) INTB PIRQB
-				PCI_BDF(2, 10, 3) INTB PIRQB
-				PCI_BDF(2, 10, 4) INTB PIRQB
+				PCI_BDF(2, 10, 0) INTB PIRQD
+				PCI_BDF(2, 10, 1) INTB PIRQD
+				PCI_BDF(2, 10, 2) INTB PIRQD
+				PCI_BDF(2, 10, 3) INTB PIRQD
+				PCI_BDF(2, 10, 4) INTB PIRQD
 				PCI_BDF(2, 12, 0) INTC PIRQC
 				PCI_BDF(2, 12, 1) INTC PIRQC
 				PCI_BDF(2, 12, 2) INTC PIRQC
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 03/10] x86: Write correct bus number for the irq router
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
  2015-06-15  7:59 ` [U-Boot] [PATCH 02/10] x86: queensbay: Correct Topcliff device irqs Bin Meng
@ 2015-06-15  7:59 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  2015-06-15  7:59 ` [U-Boot] [PATCH 04/10] x86: Ignore function number when writing PIRQ routing table Bin Meng
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  7:59 UTC (permalink / raw)
  To: u-boot

We should write correct bus number to the PIRQ routing table for the
irq router from device tree, instead of hard-coded zero.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/cpu/irq.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/cpu/irq.c b/arch/x86/cpu/irq.c
index 74b89ad..7d5ccc1 100644
--- a/arch/x86/cpu/irq.c
+++ b/arch/x86/cpu/irq.c
@@ -161,7 +161,7 @@ static int create_pirq_routing_table(void)
 	/* Populate the PIRQ table fields */
 	rt->signature = PIRQ_SIGNATURE;
 	rt->version = PIRQ_VERSION;
-	rt->rtr_bus = 0;
+	rt->rtr_bus = PCI_BUS(irq_router.bdf);
 	rt->rtr_devfn = (PCI_DEV(irq_router.bdf) << 3) |
 			PCI_FUNC(irq_router.bdf);
 	rt->rtr_vendor = PCI_VENDOR_ID_INTEL;
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 04/10] x86: Ignore function number when writing PIRQ routing table
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
  2015-06-15  7:59 ` [U-Boot] [PATCH 02/10] x86: queensbay: Correct Topcliff device irqs Bin Meng
  2015-06-15  7:59 ` [U-Boot] [PATCH 03/10] x86: Write correct bus number for the irq router Bin Meng
@ 2015-06-15  7:59 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  2015-06-15  7:59 ` [U-Boot] [PATCH 05/10] x86: Reduce PIRQ routing table size Bin Meng
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  7:59 UTC (permalink / raw)
  To: u-boot

In fill_irq_info() pci device's function number is written into
the table, however this is not really necessary. The function
number can be anything as OS doesn't care about this field,
neither does the PIRQ routing specification. Change to always
writing 0 as the function number.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/cpu/irq.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/x86/cpu/irq.c b/arch/x86/cpu/irq.c
index 7d5ccc1..df4300c 100644
--- a/arch/x86/cpu/irq.c
+++ b/arch/x86/cpu/irq.c
@@ -59,12 +59,12 @@ void pirq_assign_irq(int link, u8 irq)
 }
 
 static inline void fill_irq_info(struct irq_info **slotp, int *entries, u8 bus,
-				 u8 device, u8 func, u8 pin, u8 pirq)
+				 u8 device, u8 pin, u8 pirq)
 {
 	struct irq_info *slot = *slotp;
 
 	slot->bus = bus;
-	slot->devfn = (device << 3) | func;
+	slot->devfn = (device << 3) | 0;
 	slot->irq[pin - 1].link = LINK_N2V(pirq, irq_router.link_base);
 	slot->irq[pin - 1].bitmap = irq_router.irq_mask;
 	(*entries)++;
@@ -182,8 +182,7 @@ static int create_pirq_routing_table(void)
 		      PCI_FUNC(pr.bdf), 'A' + pr.pin - 1,
 		      'A' + pr.pirq);
 		fill_irq_info(&slot, &irq_entries, PCI_BUS(pr.bdf),
-			      PCI_DEV(pr.bdf), PCI_FUNC(pr.bdf),
-			      pr.pin, pr.pirq);
+			      PCI_DEV(pr.bdf), pr.pin, pr.pirq);
 		cell += sizeof(struct pirq_routing) / sizeof(u32);
 	}
 
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 05/10] x86: Reduce PIRQ routing table size
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
                   ` (2 preceding siblings ...)
  2015-06-15  7:59 ` [U-Boot] [PATCH 04/10] x86: Ignore function number when writing PIRQ routing table Bin Meng
@ 2015-06-15  7:59 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  2015-06-15  7:59 ` [U-Boot] [PATCH 06/10] x86: Clean up ioapic header file Bin Meng
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  7:59 UTC (permalink / raw)
  To: u-boot

There is no need to populate multiple irq info entries with the same
bus number and device number, but with different interrupt pin. We
can use the same entry to store all the 4 interrupt pin (INT A/B/C/D)
routing information to reduce the whole PIRQ routing table size.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/cpu/irq.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 56 insertions(+), 9 deletions(-)

diff --git a/arch/x86/cpu/irq.c b/arch/x86/cpu/irq.c
index df4300c..676033c 100644
--- a/arch/x86/cpu/irq.c
+++ b/arch/x86/cpu/irq.c
@@ -58,17 +58,28 @@ void pirq_assign_irq(int link, u8 irq)
 		writeb(irq, irq_router.ibase + LINK_N2V(link, base));
 }
 
-static inline void fill_irq_info(struct irq_info **slotp, int *entries, u8 bus,
-				 u8 device, u8 pin, u8 pirq)
+static struct irq_info *check_dup_entry(struct irq_info *slot_base,
+					int entry_num, u8 bus, u8 device)
 {
-	struct irq_info *slot = *slotp;
+	struct irq_info *slot = slot_base;
+	int i;
+
+	for (i = 0; i < entry_num; i++) {
+		if (slot->bus == bus && slot->devfn == (device << 3))
+			break;
+		slot++;
+	}
 
+	return (i == entry_num) ? NULL : slot;
+}
+
+static inline void fill_irq_info(struct irq_info *slot, u8 bus, u8 device,
+				 u8 pin, u8 pirq)
+{
 	slot->bus = bus;
 	slot->devfn = (device << 3) | 0;
 	slot->irq[pin - 1].link = LINK_N2V(pirq, irq_router.link_base);
 	slot->irq[pin - 1].bitmap = irq_router.irq_mask;
-	(*entries)++;
-	(*slotp)++;
 }
 
 __weak void cpu_irq_init(void)
@@ -84,7 +95,7 @@ static int create_pirq_routing_table(void)
 	int len, count;
 	const u32 *cell;
 	struct irq_routing_table *rt;
-	struct irq_info *slot;
+	struct irq_info *slot, *slot_base;
 	int irq_entries = 0;
 	int i;
 	int ret;
@@ -167,7 +178,7 @@ static int create_pirq_routing_table(void)
 	rt->rtr_vendor = PCI_VENDOR_ID_INTEL;
 	rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31;
 
-	slot = rt->slots;
+	slot_base = rt->slots;
 
 	/* Now fill in the irq_info entries in the PIRQ table */
 	for (i = 0; i < count; i++) {
@@ -181,8 +192,44 @@ static int create_pirq_routing_table(void)
 		      i, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
 		      PCI_FUNC(pr.bdf), 'A' + pr.pin - 1,
 		      'A' + pr.pirq);
-		fill_irq_info(&slot, &irq_entries, PCI_BUS(pr.bdf),
-			      PCI_DEV(pr.bdf), pr.pin, pr.pirq);
+
+		slot = check_dup_entry(slot_base, irq_entries,
+				       PCI_BUS(pr.bdf), PCI_DEV(pr.bdf));
+		if (slot) {
+			debug("found entry for bus %d device %d, ",
+			      PCI_BUS(pr.bdf), PCI_DEV(pr.bdf));
+
+			if (slot->irq[pr.pin - 1].link) {
+				debug("skipping\n");
+
+				/*
+				 * Sanity test on the routed PIRQ pin
+				 *
+				 * If they don't match, show a warning to tell
+				 * there might be something wrong with the PIRQ
+				 * routing information in the device tree.
+				 */
+				if (slot->irq[pr.pin - 1].link !=
+					LINK_N2V(pr.pirq, irq_router.link_base))
+					debug("WARNING: Inconsistent PIRQ routing information\n");
+
+				cell += sizeof(struct pirq_routing) /
+					sizeof(u32);
+				continue;
+			} else {
+				debug("writing INT%c\n", 'A' + pr.pin - 1);
+				fill_irq_info(slot, PCI_BUS(pr.bdf),
+					      PCI_DEV(pr.bdf), pr.pin, pr.pirq);
+				cell += sizeof(struct pirq_routing) /
+					sizeof(u32);
+				continue;
+			}
+		}
+
+		slot = slot_base + irq_entries;
+		fill_irq_info(slot, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
+			      pr.pin, pr.pirq);
+		irq_entries++;
 		cell += sizeof(struct pirq_routing) / sizeof(u32);
 	}
 
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 06/10] x86: Clean up ioapic header file
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
                   ` (3 preceding siblings ...)
  2015-06-15  7:59 ` [U-Boot] [PATCH 05/10] x86: Reduce PIRQ routing table size Bin Meng
@ 2015-06-15  7:59 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  2015-06-15  7:59 ` [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines Bin Meng
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  7:59 UTC (permalink / raw)
  To: u-boot

Remove all the dead/unused macros from asm/ioapic.h.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/include/asm/ioapic.h | 26 +++-----------------------
 1 file changed, 3 insertions(+), 23 deletions(-)

diff --git a/arch/x86/include/asm/ioapic.h b/arch/x86/include/asm/ioapic.h
index 699160f..f5d69db 100644
--- a/arch/x86/include/asm/ioapic.h
+++ b/arch/x86/include/asm/ioapic.h
@@ -10,29 +10,9 @@
 #define __ASM_IOAPIC_H
 
 #define IO_APIC_ADDR		0xfec00000
-#define IO_APIC_INDEX		IO_APIC_ADDR
-#define IO_APIC_DATA		(IO_APIC_ADDR + 0x10)
-#define IO_APIC_INTERRUPTS	24
-
-#define ALL		(0xff << 24)
-#define NONE		0
-#define DISABLED	(1 << 16)
-#define ENABLED		(0 << 16)
-#define TRIGGER_EDGE	(0 << 15)
-#define TRIGGER_LEVEL	(1 << 15)
-#define POLARITY_HIGH	(0 << 13)
-#define POLARITY_LOW	(1 << 13)
-#define PHYSICAL_DEST	(0 << 11)
-#define LOGICAL_DEST	(1 << 11)
-#define ExtINT		(7 << 8)
-#define NMI		(4 << 8)
-#define SMI		(2 << 8)
-#define INT		(1 << 8)
 
-u32 io_apic_read(u32 ioapic_base, u32 reg);
-void io_apic_write(u32 ioapic_base, u32 reg, u32 value);
-void set_ioapic_id(u32 ioapic_base, u8 ioapic_id);
-void setup_ioapic(u32 ioapic_base, u8 ioapic_id);
-void clear_ioapic(u32 ioapic_base);
+/* Direct addressed register */
+#define IO_APIC_INDEX		(IO_APIC_ADDR + 0x00)
+#define IO_APIC_DATA		(IO_APIC_ADDR + 0x10)
 
 #endif
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
                   ` (4 preceding siblings ...)
  2015-06-15  7:59 ` [U-Boot] [PATCH 06/10] x86: Clean up ioapic header file Bin Meng
@ 2015-06-15  7:59 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  2015-06-15  8:00 ` [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs Bin Meng
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  7:59 UTC (permalink / raw)
  To: u-boot

I/O APIC registers are addressed indirectly. Add io_apic_read() and
io_apic_write() inline routines. Two macros for I/O APIC ID and
version register offset are also added.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/include/asm/ioapic.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/arch/x86/include/asm/ioapic.h b/arch/x86/include/asm/ioapic.h
index f5d69db..697eabb 100644
--- a/arch/x86/include/asm/ioapic.h
+++ b/arch/x86/include/asm/ioapic.h
@@ -9,10 +9,44 @@
 #ifndef __ASM_IOAPIC_H
 #define __ASM_IOAPIC_H
 
+#include <asm/io.h>
+
 #define IO_APIC_ADDR		0xfec00000
 
 /* Direct addressed register */
 #define IO_APIC_INDEX		(IO_APIC_ADDR + 0x00)
 #define IO_APIC_DATA		(IO_APIC_ADDR + 0x10)
 
+/* Indirect addressed register offset */
+#define IO_APIC_ID		0x00
+#define IO_APIC_VER		0x01
+
+/**
+ * io_apic_read() - Read I/O APIC register
+ *
+ * This routine reads I/O APIC indirect addressed register.
+ *
+ * @reg:	address of indirect addressed register
+ * @return:	register value to read
+ */
+static inline u32 io_apic_read(u32 reg)
+{
+	writel(reg, IO_APIC_INDEX);
+	return readl(IO_APIC_DATA);
+}
+
+/**
+ * io_apic_write() - Write I/O APIC register
+ *
+ * This routine writes I/O APIC indirect addressed register.
+ *
+ * @reg:	address of indirect addressed register
+ * @val:	register value to write
+ */
+static inline void io_apic_write(u32 reg, u32 val)
+{
+	writel(reg, IO_APIC_INDEX);
+	writel(val, IO_APIC_DATA);
+}
+
 #endif
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
                   ` (5 preceding siblings ...)
  2015-06-15  7:59 ` [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines Bin Meng
@ 2015-06-15  8:00 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  2015-06-15  8:00 ` [U-Boot] [PATCH 09/10] x86: Generate a valid MultiProcessor (MP) table Bin Meng
  2015-06-15  8:00 ` [U-Boot] [PATCH 10/10] x86: crownbay: Enable writing MP table Bin Meng
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  8:00 UTC (permalink / raw)
  To: u-boot

The MP table provides a way for the operating system to support
for symmetric multiprocessing as well as symmetric I/O interrupt
handling with the local APIC and I/O APIC. We provide a bunch of
APIs for U-Boot to write the floating table, configuration table
header as well as base and extended table entries.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/include/asm/mpspec.h | 434 ++++++++++++++++++++++++++++++++++++++++++
 arch/x86/lib/Makefile         |   1 +
 arch/x86/lib/mpspec.c         | 238 +++++++++++++++++++++++
 3 files changed, 673 insertions(+)
 create mode 100644 arch/x86/include/asm/mpspec.h
 create mode 100644 arch/x86/lib/mpspec.c

diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
new file mode 100644
index 0000000..c489a58
--- /dev/null
+++ b/arch/x86/include/asm/mpspec.h
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * Adapted from coreboot src/arch/x86/include/arch/smp/mpspec.h
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __ASM_MPSPEC_H
+#define __ASM_MPSPEC_H
+
+/*
+ * Structure definitions for SMP machines following the
+ * Intel MultiProcessor Specification 1.4
+ */
+
+#define MPSPEC_V14	4
+
+#define MPF_SIGNATURE	"_MP_"
+
+struct mp_floating_table {
+	char mpf_signature[4];	/* "_MP_" */
+	u32 mpf_physptr;	/* Configuration table address */
+	u8 mpf_length;		/* Our length (paragraphs) */
+	u8 mpf_spec;		/* Specification version */
+	u8 mpf_checksum;	/* Checksum (makes sum 0) */
+	u8 mpf_feature1;	/* Predefined or Unique configuration? */
+	u8 mpf_feature2;	/* Bit7 set for IMCR/PIC */
+	u8 mpf_feature3;	/* Unused (0) */
+	u8 mpf_feature4;	/* Unused (0) */
+	u8 mpf_feature5;	/* Unused (0) */
+};
+
+#define MPC_SIGNATURE	"PCMP"
+
+struct mp_config_table {
+	char mpc_signature[4];	/* "PCMP" */
+	u16 mpc_length;		/* Size of table */
+	u8 mpc_spec;		/* Specification version */
+	u8 mpc_checksum;	/* Checksum (makes sum 0) */
+	char mpc_oem[8];	/* OEM ID */
+	char mpc_product[12];	/* Product ID */
+	u32 mpc_oemptr;		/* OEM table address */
+	u16 mpc_oemsize;	/* OEM table size */
+	u16 mpc_entry_count;	/* Number of entries in the table */
+	u32 mpc_lapic;		/* Local APIC address */
+	u16 mpe_length;		/* Extended table size */
+	u8 mpe_checksum;	/* Extended table checksum */
+	u8 reserved;
+};
+
+/* Base MP configuration table entry types */
+
+enum mp_base_config_entry_type {
+	MP_PROCESSOR,
+	MP_BUS,
+	MP_IOAPIC,
+	MP_INTSRC,
+	MP_LINTSRC
+};
+
+#define MPC_CPU_EN	(1 << 0)
+#define MPC_CPU_BP	(1 << 1)
+
+struct mpc_config_processor {
+	u8 mpc_type;
+	u8 mpc_apicid;
+	u8 mpc_apicver;
+	u8 mpc_cpuflag;
+	u32 mpc_cpusignature;
+	u32 mpc_cpufeature;
+	u32 mpc_reserved[2];
+};
+
+#define BUSTYPE_CBUS	"CBUS  "
+#define BUSTYPE_CBUSII	"CBUSII"
+#define BUSTYPE_EISA	"EISA  "
+#define BUSTYPE_FUTURE	"FUTURE"
+#define BUSTYPE_INTERN	"INTERN"
+#define BUSTYPE_ISA	"ISA   "
+#define BUSTYPE_MBI	"MBI   "
+#define BUSTYPE_MBII	"MBII  "
+#define BUSTYPE_MCA	"MCA   "
+#define BUSTYPE_MPI	"MPI   "
+#define BUSTYPE_MPSA	"MPSA  "
+#define BUSTYPE_NUBUS	"NUBUS "
+#define BUSTYPE_PCI	"PCI   "
+#define BUSTYPE_PCMCIA	"PCMCIA"
+#define BUSTYPE_TC	"TC    "
+#define BUSTYPE_VL	"VL    "
+#define BUSTYPE_VME	"VME   "
+#define BUSTYPE_XPRESS	"XPRESS"
+
+struct mpc_config_bus {
+	u8 mpc_type;
+	u8 mpc_busid;
+	u8 mpc_bustype[6];
+};
+
+#define MPC_APIC_USABLE	(1 << 0)
+
+struct mpc_config_ioapic {
+	u8 mpc_type;
+	u8 mpc_apicid;
+	u8 mpc_apicver;
+	u8 mpc_flags;
+	u32 mpc_apicaddr;
+};
+
+enum mp_irq_source_types {
+	MP_INT,
+	MP_NMI,
+	MP_SMI,
+	MP_EXTINT
+};
+
+#define MP_IRQ_POLARITY_DEFAULT	0x0
+#define MP_IRQ_POLARITY_HIGH	0x1
+#define MP_IRQ_POLARITY_LOW	0x3
+#define MP_IRQ_POLARITY_MASK	0x3
+#define MP_IRQ_TRIGGER_DEFAULT	0x0
+#define MP_IRQ_TRIGGER_EDGE	0x4
+#define MP_IRQ_TRIGGER_LEVEL	0xc
+#define MP_IRQ_TRIGGER_MASK	0xc
+
+#define MP_APIC_ALL		0xff
+
+struct mpc_config_intsrc {
+	u8 mpc_type;
+	u8 mpc_irqtype;
+	u16 mpc_irqflag;
+	u8 mpc_srcbus;
+	u8 mpc_srcbusirq;
+	u8 mpc_dstapic;
+	u8 mpc_dstirq;
+};
+
+struct mpc_config_lintsrc {
+	u8 mpc_type;
+	u8 mpc_irqtype;
+	u16 mpc_irqflag;
+	u8 mpc_srcbusid;
+	u8 mpc_srcbusirq;
+	u8 mpc_destapic;
+	u8 mpc_destlint;
+};
+
+/* Extended MP configuration table entry types */
+
+enum mp_ext_config_entry_type {
+	MPE_SYSTEM_ADDRESS_SPACE = 128,
+	MPE_BUS_HIERARCHY,
+	MPE_COMPAT_ADDRESS_SPACE
+};
+
+struct mp_ext_config {
+	u8 mpe_type;
+	u8 mpe_length;
+};
+
+#define ADDRESS_TYPE_IO		0
+#define ADDRESS_TYPE_MEM	1
+#define ADDRESS_TYPE_PREFETCH	2
+
+struct mp_ext_system_address_space {
+	u8 mpe_type;
+	u8 mpe_length;
+	u8 mpe_busid;
+	u8 mpe_addr_type;
+	u32 mpe_addr_base_low;
+	u32 mpe_addr_base_high;
+	u32 mpe_addr_length_low;
+	u32 mpe_addr_length_high;
+};
+
+#define BUS_SUBTRACTIVE_DECODE	(1 << 0)
+
+struct mp_ext_bus_hierarchy {
+	u8 mpe_type;
+	u8 mpe_length;
+	u8 mpe_busid;
+	u8 mpe_bus_info;
+	u8 mpe_parent_busid;
+	u8 reserved[3];
+};
+
+#define ADDRESS_RANGE_ADD	0
+#define ADDRESS_RANGE_SUBTRACT	1
+
+/*
+ * X100 - X3FF
+ * X500 - X7FF
+ * X900 - XBFF
+ * XD00 - XFFF
+ */
+#define RANGE_LIST_IO_ISA	0
+/*
+ * X3B0 - X3BB
+ * X3C0 - X3DF
+ * X7B0 - X7BB
+ * X7C0 - X7DF
+ * XBB0 - XBBB
+ * XBC0 - XBDF
+ * XFB0 - XFBB
+ * XFC0 - XCDF
+ */
+#define RANGE_LIST_IO_VGA	1
+
+struct mp_ext_compat_address_space {
+	u8 mpe_type;
+	u8 mpe_length;
+	u8 mpe_busid;
+	u8 mpe_addr_modifier;
+	u32 mpe_range_list;
+};
+
+/**
+ * mp_next_mpc_entry() - Compute MP configuration table end to be used as
+ *                       next base table entry start address
+ *
+ * This computes the end address of current MP configuration table, without
+ * counting any extended configuration table entry.
+ *
+ * @mc:		configuration table header address
+ * @return:	configuration table end address
+ */
+static inline u32 mp_next_mpc_entry(struct mp_config_table *mc)
+{
+	return (u32)mc + mc->mpc_length;
+}
+
+/**
+ * mp_add_mpc_entry() - Add a base MP configuration table entry
+ *
+ * This adds the base MP configuration table entry size with
+ * added base table entry length and increases entry count by 1.
+ *
+ * @mc:		configuration table header address
+ * @length:	length of the added table entry
+ */
+static inline void mp_add_mpc_entry(struct mp_config_table *mc, u16 length)
+{
+	mc->mpc_length += length;
+	mc->mpc_entry_count++;
+}
+
+/**
+ * mp_next_mpe_entry() - Compute MP configuration table end to be used as
+ *                       next extended table entry start address
+ *
+ * This computes the end address of current MP configuration table,
+ * including any extended configuration table entry.
+ *
+ * @mc:		configuration table header address
+ * @return:	configuration table end address
+ */
+static inline u32 mp_next_mpe_entry(struct mp_config_table *mc)
+{
+	return (u32)mc + mc->mpc_length + mc->mpe_length;
+}
+
+/**
+ * mp_add_mpe_entry() - Add an extended MP configuration table entry
+ *
+ * This adds the extended MP configuration table entry size with
+ * added extended table entry length.
+ *
+ * @mc:		configuration table header address
+ * @mpe:	extended table entry base address
+ */
+static inline void mp_add_mpe_entry(struct mp_config_table *mc,
+				    struct mp_ext_config *mpe)
+{
+	mc->mpe_length += mpe->mpe_length;
+}
+
+/**
+ * mp_write_floating_table() - Write the MP floating table
+ *
+ * This writes the MP floating table, and points MP configuration table
+ * to its end address so that MP configuration table follows immediately
+ * after the floating table.
+ *
+ * @mf:		MP floating table base address
+ * @return:	MP configuration table header address
+ */
+struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf);
+
+/**
+ * mp_config_table_init() - Initialize the MP configuration table header
+ *
+ * This populates the MP configuration table header with valid bits.
+ *
+ * @mc:		MP configuration table header address
+ */
+void mp_config_table_init(struct mp_config_table *mc);
+
+/**
+ * mp_write_processor() - Write a processor entry
+ *
+ * This writes a processor entry to the configuration table.
+ *
+ * @mc:		MP configuration table header address
+ */
+void mp_write_processor(struct mp_config_table *mc);
+
+/**
+ * mp_write_bus() - Write a bus entry
+ *
+ * This writes a bus entry to the configuration table.
+ *
+ * @mc:		MP configuration table header address
+ * @id:		bus id
+ * @bustype:	bus type name
+ */
+void mp_write_bus(struct mp_config_table *mc, u8 id, const char *bustype);
+
+/**
+ * mp_write_ioapic() - Write an I/O APIC entry
+ *
+ * This writes an I/O APIC entry to the configuration table.
+ *
+ * @mc:		MP configuration table header address
+ * @id:		I/O APIC id
+ * @ver:	I/O APIC version
+ * @apicaddr:	I/O APIC address
+ */
+void mp_write_ioapic(struct mp_config_table *mc, u8 id, u8 ver, u32 apicaddr);
+
+/**
+ * mp_write_intsrc() - Write an I/O interrupt assignment entry
+ *
+ * This writes an I/O interrupt assignment entry to the configuration table.
+ *
+ * @mc:		MP configuration table header address
+ * @irqtype:	IRQ type (INT/NMI/SMI/ExtINT)
+ * @irqflag:	IRQ flag (level/trigger)
+ * @srcbus:	source bus id where the interrupt comes from
+ * @srcbusirq:	IRQ number mapped on the source bus
+ * @dstapic:	destination I/O APIC id where the interrupt goes to
+ * @dstirq:	destination I/O APIC pin where the interrupt goes to
+ */
+void mp_write_intsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
+		     u8 srcbus, u8 srcbusirq, u8 dstapic, u8 dstirq);
+
+/**
+ * mp_write_pci_intsrc() - Write a PCI interrupt assignment entry
+ *
+ * This writes a PCI interrupt assignment entry to the configuration table.
+ *
+ * @mc:		MP configuration table header address
+ * @irqtype:	IRQ type (INT/NMI/SMI/ExtINT)
+ * @srcbus:	PCI bus number where the interrupt comes from
+ * @dev:	device number on the PCI bus
+ * @pin:	PCI interrupt pin (INT A/B/C/D)
+ * @dstapic:	destination I/O APIC id where the interrupt goes to
+ * @dstirq:	destination I/O APIC pin where the interrupt goes to
+ */
+void mp_write_pci_intsrc(struct mp_config_table *mc, u8 irqtype,
+			 u8 srcbus, u8 dev, u8 pin, u8 dstapic, u8 dstirq);
+
+/**
+ * mp_write_lintsrc() - Write a local interrupt assignment entry
+ *
+ * This writes a local interrupt assignment entry to the configuration table.
+ *
+ * @mc:		MP configuration table header address
+ * @irqtype:	IRQ type (INT/NMI/SMI/ExtINT)
+ * @irqflag:	IRQ flag (level/trigger)
+ * @srcbus:	PCI bus number where the interrupt comes from
+ * @srcbusirq:	IRQ number mapped on the source bus
+ * @dstapic:	destination local APIC id where the interrupt goes to
+ * @destlint:	destination local APIC pin where the interrupt goes to
+ */
+void mp_write_lintsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
+		      u8 srcbus, u8 srcbusirq, u8 destapic, u8 destlint);
+
+
+/**
+ * mp_write_address_space() - Write a system address space entry
+ *
+ * This writes a system address space entry to the configuration table.
+ *
+ * @mc:			MP configuration table header address
+ * @busid:		bus id for the bus where system address space is mapped
+ * @addr_type:		system address type
+ * @addr_base_low:	starting address low
+ * @addr_base_high:	starting address high
+ * @addr_length_low:	address length low
+ * @addr_length_high:	address length high
+ */
+void mp_write_address_space(struct mp_config_table *mc,
+			    u8 busid, u8 addr_type,
+			    u32 addr_base_low, u32 addr_base_high,
+			    u32 addr_length_low, u32 addr_length_high);
+
+/**
+ * mp_write_bus_hierarchy() - Write a bus hierarchy descriptor entry
+ *
+ * This writes a bus hierarchy descriptor entry to the configuration table.
+ *
+ * @mc:			MP configuration table header address
+ * @busid:		bus id
+ * @bus_info:		bit0 indicates if the bus is a subtractive decode bus
+ * @parent_busid:	parent bus id
+ */
+void mp_write_bus_hierarchy(struct mp_config_table *mc,
+			    u8 busid, u8 bus_info, u8 parent_busid);
+
+/**
+ * mp_write_compat_address_space() - Write a compat bus address space entry
+ *
+ * This writes a compatibility bus address space modifier entry to the
+ * configuration table.
+ *
+ * @mc:			MP configuration table header address
+ * @busid:		bus id
+ * @addr_modifier:	add or subtract to predefined address range list
+ * @range_list:		list of predefined address space ranges
+ */
+void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
+				   u8 addr_modifier, u32 range_list);
+
+/**
+ * mptable_finalize() - Finalize the MP table
+ *
+ * This finalizes the MP table by calculating required checksums.
+ *
+ * @mc:		MP configuration table header address
+ * @return:	MP table end address
+ */
+u32 mptable_finalize(struct mp_config_table *mc);
+
+#endif /* __ASM_MPSPEC_H */
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 70ad19b..43489fd 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -15,6 +15,7 @@ obj-y	+= gcc.o
 obj-y	+= init_helpers.o
 obj-y	+= interrupts.o
 obj-y	+= lpc-uclass.o
+obj-y	+= mpspec.o
 obj-y += cmd_mtrr.o
 obj-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o
 obj-$(CONFIG_SYS_PCAT_TIMER) += pcat_timer.o
diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
new file mode 100644
index 0000000..657df22
--- /dev/null
+++ b/arch/x86/lib/mpspec.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * Adapted from coreboot src/arch/x86/boot/mpspec.c
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <cpu.h>
+#include <dm.h>
+#include <asm/cpu.h>
+#include <asm/ioapic.h>
+#include <asm/lapic.h>
+#include <asm/mpspec.h>
+#include <asm/tables.h>
+#include <dm/uclass-internal.h>
+
+struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
+{
+	u32 mc;
+
+	memcpy(mf->mpf_signature, MPF_SIGNATURE, 4);
+	mf->mpf_physptr = (u32)mf + sizeof(struct mp_floating_table);
+	mf->mpf_length = 1;
+	mf->mpf_spec = MPSPEC_V14;
+	mf->mpf_checksum = 0;
+	/* We don't use the default configuration table */
+	mf->mpf_feature1 = 0;
+	/* Indicate that virtual wire mode is always implemented */
+	mf->mpf_feature2 = 0;
+	mf->mpf_feature3 = 0;
+	mf->mpf_feature4 = 0;
+	mf->mpf_feature5 = 0;
+	mf->mpf_checksum = table_compute_checksum(mf, mf->mpf_length * 16);
+
+	mc = (u32)mf + sizeof(struct mp_floating_table);
+	return (struct mp_config_table *)mc;
+}
+
+void mp_config_table_init(struct mp_config_table *mc)
+{
+	int start, len;
+	int i;
+
+	memcpy(mc->mpc_signature, MPC_SIGNATURE, 4);
+	mc->mpc_length = sizeof(struct mp_config_table);
+	mc->mpc_spec = MPSPEC_V14;
+	mc->mpc_checksum = 0;
+	mc->mpc_oemptr = 0;
+	mc->mpc_oemsize = 0;
+	mc->mpc_entry_count = 0;
+	mc->mpc_lapic = LAPIC_DEFAULT_BASE;
+	mc->mpe_length = 0;
+	mc->mpe_checksum = 0;
+	mc->reserved = 0;
+
+	/* The oem/product id fields are exactly 8/12 bytes long */
+	strncpy(mc->mpc_oem, CONFIG_SYS_VENDOR, 8);
+	strncpy(mc->mpc_product, CONFIG_SYS_BOARD, 12);
+
+	/* Fill the remaining bytes with spaces */
+	len = strlen(CONFIG_SYS_VENDOR);
+	start = len < 8 ? len : 8;
+	for (i = start; i < 8; i++)
+		mc->mpc_oem[i] = ' ';
+	len = strlen(CONFIG_SYS_BOARD);
+	start = len < 12 ? len : 12;
+	for (i = start; i < 12; i++)
+		mc->mpc_product[i] = ' ';
+}
+
+void mp_write_processor(struct mp_config_table *mc)
+{
+	struct mpc_config_processor *mpc;
+	struct udevice *dev;
+	u8 boot_apicid, apicver;
+	u32 cpusignature, cpufeature;
+	struct cpuid_result result;
+
+	boot_apicid = lapicid();
+	apicver = lapic_read(LAPIC_LVR) & 0xff;
+	result = cpuid(1);
+	cpusignature = result.eax;
+	cpufeature = result.edx;
+
+	for (uclass_find_first_device(UCLASS_CPU, &dev);
+	     dev;
+	     uclass_find_next_device(&dev)) {
+		struct cpu_platdata *plat = dev_get_parent_platdata(dev);
+		u8 cpuflag = MPC_CPU_EN;
+
+		if (!device_active(dev))
+			continue;
+
+		mpc = (struct mpc_config_processor *)mp_next_mpc_entry(mc);
+		mpc->mpc_type = MP_PROCESSOR;
+		mpc->mpc_apicid = plat->cpu_id;
+		mpc->mpc_apicver = apicver;
+		if (boot_apicid == plat->cpu_id)
+			cpuflag |= MPC_CPU_BP;
+		mpc->mpc_cpuflag = cpuflag;
+		mpc->mpc_cpusignature = cpusignature;
+		mpc->mpc_cpufeature = cpufeature;
+		mpc->mpc_reserved[0] = 0;
+		mpc->mpc_reserved[1] = 0;
+		mp_add_mpc_entry(mc, sizeof(*mpc));
+	}
+}
+
+void mp_write_bus(struct mp_config_table *mc, u8 id, const char *bustype)
+{
+	struct mpc_config_bus *mpc;
+
+	mpc = (struct mpc_config_bus *)mp_next_mpc_entry(mc);
+	mpc->mpc_type = MP_BUS;
+	mpc->mpc_busid = id;
+	memcpy(mpc->mpc_bustype, bustype, 6);
+	mp_add_mpc_entry(mc, sizeof(*mpc));
+}
+
+void mp_write_ioapic(struct mp_config_table *mc, u8 id, u8 ver, u32 apicaddr)
+{
+	struct mpc_config_ioapic *mpc;
+
+	mpc = (struct mpc_config_ioapic *)mp_next_mpc_entry(mc);
+	mpc->mpc_type = MP_IOAPIC;
+	mpc->mpc_apicid = id;
+	mpc->mpc_apicver = ver;
+	mpc->mpc_flags = MPC_APIC_USABLE;
+	mpc->mpc_apicaddr = apicaddr;
+	mp_add_mpc_entry(mc, sizeof(*mpc));
+}
+
+void mp_write_intsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
+		     u8 srcbus, u8 srcbusirq, u8 dstapic, u8 dstirq)
+{
+	struct mpc_config_intsrc *mpc;
+
+	mpc = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
+	mpc->mpc_type = MP_INTSRC;
+	mpc->mpc_irqtype = irqtype;
+	mpc->mpc_irqflag = irqflag;
+	mpc->mpc_srcbus = srcbus;
+	mpc->mpc_srcbusirq = srcbusirq;
+	mpc->mpc_dstapic = dstapic;
+	mpc->mpc_dstirq = dstirq;
+	mp_add_mpc_entry(mc, sizeof(*mpc));
+}
+
+void mp_write_pci_intsrc(struct mp_config_table *mc, u8 irqtype,
+			 u8 srcbus, u8 dev, u8 pin, u8 dstapic, u8 dstirq)
+{
+	u8 srcbusirq = (dev << 2) | (pin - 1);
+
+	mp_write_intsrc(mc, irqtype, MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_LOW,
+			srcbus, srcbusirq, dstapic, dstirq);
+}
+
+void mp_write_lintsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
+		      u8 srcbus, u8 srcbusirq, u8 destapic, u8 destlint)
+{
+	struct mpc_config_lintsrc *mpc;
+
+	mpc = (struct mpc_config_lintsrc *)mp_next_mpc_entry(mc);
+	mpc->mpc_type = MP_LINTSRC;
+	mpc->mpc_irqtype = irqtype;
+	mpc->mpc_irqflag = irqflag;
+	mpc->mpc_srcbusid = srcbus;
+	mpc->mpc_srcbusirq = srcbusirq;
+	mpc->mpc_destapic = destapic;
+	mpc->mpc_destlint = destlint;
+	mp_add_mpc_entry(mc, sizeof(*mpc));
+}
+
+void mp_write_address_space(struct mp_config_table *mc,
+			    u8 busid, u8 addr_type,
+			    u32 addr_base_low, u32 addr_base_high,
+			    u32 addr_length_low, u32 addr_length_high)
+{
+	struct mp_ext_system_address_space *mpe;
+
+	mpe = (struct mp_ext_system_address_space *)mp_next_mpe_entry(mc);
+	mpe->mpe_type = MPE_SYSTEM_ADDRESS_SPACE;
+	mpe->mpe_length = sizeof(*mpe);
+	mpe->mpe_busid = busid;
+	mpe->mpe_addr_type = addr_type;
+	mpe->mpe_addr_base_low = addr_base_low;
+	mpe->mpe_addr_base_high = addr_base_high;
+	mpe->mpe_addr_length_low = addr_length_low;
+	mpe->mpe_addr_length_high = addr_length_high;
+	mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
+}
+
+void mp_write_bus_hierarchy(struct mp_config_table *mc,
+			    u8 busid, u8 bus_info, u8 parent_busid)
+{
+	struct mp_ext_bus_hierarchy *mpe;
+
+	mpe = (struct mp_ext_bus_hierarchy *)mp_next_mpe_entry(mc);
+	mpe->mpe_type = MPE_BUS_HIERARCHY;
+	mpe->mpe_length = sizeof(*mpe);
+	mpe->mpe_busid = busid;
+	mpe->mpe_bus_info = bus_info;
+	mpe->mpe_parent_busid = parent_busid;
+	mpe->reserved[0] = 0;
+	mpe->reserved[1] = 0;
+	mpe->reserved[2] = 0;
+	mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
+}
+
+void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
+				   u8 addr_modifier, u32 range_list)
+{
+	struct mp_ext_compat_address_space *mpe;
+
+	mpe = (struct mp_ext_compat_address_space *)mp_next_mpe_entry(mc);
+	mpe->mpe_type = MPE_COMPAT_ADDRESS_SPACE;
+	mpe->mpe_length = sizeof(*mpe);
+	mpe->mpe_busid = busid;
+	mpe->mpe_addr_modifier = addr_modifier;
+	mpe->mpe_range_list = range_list;
+	mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
+}
+
+u32 mptable_finalize(struct mp_config_table *mc)
+{
+	u32 end;
+
+	mc->mpe_checksum = table_compute_checksum((void *)mp_next_mpc_entry(mc),
+						  mc->mpe_length);
+	mc->mpc_checksum = table_compute_checksum(mc, mc->mpc_length);
+	end = mp_next_mpe_entry(mc);
+
+	debug("Write the MP table at: %x - %x\n", (u32)mc, end);
+
+	return end;
+}
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 09/10] x86: Generate a valid MultiProcessor (MP) table
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
                   ` (6 preceding siblings ...)
  2015-06-15  8:00 ` [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs Bin Meng
@ 2015-06-15  8:00 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  2015-06-15  8:00 ` [U-Boot] [PATCH 10/10] x86: crownbay: Enable writing MP table Bin Meng
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  8:00 UTC (permalink / raw)
  To: u-boot

Implement a weak write_mp_table() to create a minimal working MP
table. This includes an MP floating table, a configuration table
header and all of the 5 base configuration table entries. The I/O
interrupt assignment table entry is created based on the same
information used in the creation of PIRQ routing table from device
tree. A check duplicated entry logic is applied to prevent writing
multiple I/O interrupt entries with the same information.

Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
need actually write the MP table at the F seg, just like we did for
PIRQ routing and SFI tables. With MP table existence, linux kernel
will switch to I/O APIC and local APIC to process all the peripheral
interrupts instead of 8259 PICs. This takes full advantage of the
multicore hardware and the SMP kernel.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/Kconfig              |   8 +++
 arch/x86/include/asm/mpspec.h |  10 +++
 arch/x86/lib/mpspec.c         | 147 ++++++++++++++++++++++++++++++++++++++++++
 arch/x86/lib/tables.c         |   5 ++
 4 files changed, 170 insertions(+)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 1aeae9d..d4b2772 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -319,6 +319,14 @@ config GENERATE_SFI_TABLE
 
 	  For more information, see http://simplefirmware.org
 
+config GENERATE_MP_TABLE
+	bool "Generate an MP (Multi-Processor) table"
+	help
+	  Generate an MP (Multi-Processor) table for this board. The MP table
+	  provides a way for the operating system to support for symmetric
+	  multiprocessing as well as symmetric I/O interrupt handling with
+	  the local APIC and I/O APIC.
+
 endmenu
 
 config MAX_PIRQ_LINKS
diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
index c489a58..e9e1a2a 100644
--- a/arch/x86/include/asm/mpspec.h
+++ b/arch/x86/include/asm/mpspec.h
@@ -431,4 +431,14 @@ void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
  */
 u32 mptable_finalize(struct mp_config_table *mc);
 
+/**
+ * write_mp_table() - Write MP table
+ *
+ * This writes MP table at a given address.
+ *
+ * @addr:	start address to write MP table
+ * @return:	end address of MP table
+ */
+u32 write_mp_table(u32 addr);
+
 #endif /* __ASM_MPSPEC_H */
diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
index 657df22..1e3523f 100644
--- a/arch/x86/lib/mpspec.c
+++ b/arch/x86/lib/mpspec.c
@@ -9,13 +9,17 @@
 #include <common.h>
 #include <cpu.h>
 #include <dm.h>
+#include <fdtdec.h>
 #include <asm/cpu.h>
+#include <asm/irq.h>
 #include <asm/ioapic.h>
 #include <asm/lapic.h>
 #include <asm/mpspec.h>
 #include <asm/tables.h>
 #include <dm/uclass-internal.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
 {
 	u32 mc;
@@ -236,3 +240,146 @@ u32 mptable_finalize(struct mp_config_table *mc)
 
 	return end;
 }
+
+static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
+				       u32 apicid, int external_int2)
+{
+	int i;
+
+	mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
+			MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			bus_isa, 0, apicid, 0);
+	mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			bus_isa, 1, apicid, 1);
+	mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
+			MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			bus_isa, 0, apicid, 2);
+
+	for (i = 3; i < 16; i++)
+		mp_write_intsrc(mc, MP_INT,
+				MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+				bus_isa, i, apicid, i);
+}
+
+static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
+			    int entry_num, u8 bus, u8 device, u8 pin)
+{
+	struct mpc_config_intsrc *intsrc = intsrc_base;
+	int i;
+
+	for (i = 0; i < entry_num; i++) {
+		if (intsrc->mpc_srcbus == bus &&
+		    intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
+			break;
+		intsrc++;
+	}
+
+	return (i == entry_num) ? false : true;
+}
+
+static void mptable_add_intsrc(struct mp_config_table *mc,
+			       int bus_isa, u32 apicid)
+{
+	struct mpc_config_intsrc *intsrc_base;
+	int intsrc_entries = 0;
+	const void *blob = gd->fdt_blob;
+	int node;
+	int len, count;
+	const u32 *cell;
+	int i;
+
+	/* Legacy Interrupts */
+	debug("Writing ISA IRQs\n");
+	mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
+
+	/* Get I/O interrupt information from device tree */
+	node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
+	if (node < 0) {
+		debug("%s: Cannot find irq router node\n", __func__);
+		return;
+	}
+
+	cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
+	if (!cell)
+		return;
+
+	if ((len % sizeof(struct pirq_routing)) == 0)
+		count = len / sizeof(struct pirq_routing);
+	else
+		return;
+
+	intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
+
+	for (i = 0; i < count; i++) {
+		struct pirq_routing pr;
+
+		pr.bdf = fdt_addr_to_cpu(cell[0]);
+		pr.pin = fdt_addr_to_cpu(cell[1]);
+		pr.pirq = fdt_addr_to_cpu(cell[2]);
+
+		if (check_dup_entry(intsrc_base, intsrc_entries,
+				    PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
+			debug("found entry for bus %d device %d INT%c, skipping\n",
+			      PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
+			      'A' + pr.pin - 1);
+			cell += sizeof(struct pirq_routing) / sizeof(u32);
+			continue;
+		}
+
+		/* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
+		mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
+				    PCI_DEV(pr.bdf), pr.pin, apicid,
+				    pr.pirq + 16);
+		intsrc_entries++;
+		cell += sizeof(struct pirq_routing) / sizeof(u32);
+	}
+}
+
+static void mptable_add_lintsrc(struct mp_config_table *mc, u32 bus_isa)
+{
+	mp_write_lintsrc(mc, MP_EXTINT,
+			 MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			 bus_isa, 0, MP_APIC_ALL, 0);
+	mp_write_lintsrc(mc, MP_NMI,
+			 MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			 bus_isa, 0, MP_APIC_ALL, 1);
+}
+
+__weak u32 write_mp_table(u32 addr)
+{
+	struct mp_config_table *mc;
+	int ioapic_id, ioapic_ver;
+	int bus_isa = 0xff;
+	u32 end;
+
+	/* 16 byte align the table address */
+	addr = ALIGN(addr, 16);
+
+	/* Write floating table */
+	mc = mp_write_floating_table((struct mp_floating_table *)addr);
+
+	/* Write configuration table header */
+	mp_config_table_init(mc);
+
+	/* Write processor entry */
+	mp_write_processor(mc);
+
+	/* Write bus entry */
+	mp_write_bus(mc, bus_isa, BUSTYPE_ISA);
+
+	/* Write I/O APIC entry */
+	ioapic_id = io_apic_read(IO_APIC_ID) >> 24;
+	ioapic_ver = io_apic_read(IO_APIC_VER) & 0xff;
+	mp_write_ioapic(mc, ioapic_id, ioapic_ver, IO_APIC_ADDR);
+
+	/* Write I/O interrupt assignment entry */
+	mptable_add_intsrc(mc, bus_isa, ioapic_id);
+
+	/* Write local interrupt assignment entry */
+	mptable_add_lintsrc(mc, bus_isa);
+
+	/* Finalize the MP table */
+	end = mptable_finalize(mc);
+
+	return end;
+}
diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
index 8031201..d3bef6f 100644
--- a/arch/x86/lib/tables.c
+++ b/arch/x86/lib/tables.c
@@ -6,6 +6,7 @@
 
 #include <common.h>
 #include <asm/sfi.h>
+#include <asm/mpspec.h>
 #include <asm/tables.h>
 
 u8 table_compute_checksum(void *v, int len)
@@ -32,4 +33,8 @@ void write_tables(void)
 	rom_table_end = write_sfi_table(rom_table_end);
 	rom_table_end = ALIGN(rom_table_end, 1024);
 #endif
+#ifdef CONFIG_GENERATE_MP_TABLE
+	rom_table_end = write_mp_table(rom_table_end);
+	rom_table_end = ALIGN(rom_table_end, 1024);
+#endif
 }
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 10/10] x86: crownbay: Enable writing MP table
       [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
                   ` (7 preceding siblings ...)
  2015-06-15  8:00 ` [U-Boot] [PATCH 09/10] x86: Generate a valid MultiProcessor (MP) table Bin Meng
@ 2015-06-15  8:00 ` Bin Meng
  2015-06-16  2:46   ` Simon Glass
  8 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-15  8:00 UTC (permalink / raw)
  To: u-boot

Enable writing MP table for Intel Crown Bay board.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>

---

 configs/crownbay_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/configs/crownbay_defconfig b/configs/crownbay_defconfig
index d21177d..90706c4 100644
--- a/configs/crownbay_defconfig
+++ b/configs/crownbay_defconfig
@@ -5,6 +5,7 @@ CONFIG_TARGET_CROWNBAY=y
 CONFIG_SMP=y
 CONFIG_MAX_CPUS=2
 CONFIG_GENERATE_PIRQ_TABLE=y
+CONFIG_GENERATE_MP_TABLE=y
 CONFIG_CMD_CPU=y
 CONFIG_CMD_NET=y
 CONFIG_OF_CONTROL=y
-- 
1.8.2.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 02/10] x86: queensbay: Correct Topcliff device irqs
  2015-06-15  7:59 ` [U-Boot] [PATCH 02/10] x86: queensbay: Correct Topcliff device irqs Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  0 siblings, 0 replies; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
> There are 4 usb ports on the Intel Crown Bay board, 2 of which are
> connected to Topcliff usb host 0 and the other 2 connected to usb
> host 1. USB devices inserted in the ports connected to usb host 1
> cannot get detected due to wrong IRQ assigned to the controller.
> Actually we need apply the PCI interrupt pin swizzling logic to all
> devices on the Topcliff chipset when configuring the PIRQ routing.
>
> This was observed on usb ports, but device 6 and 10 irqs are also
> wrong. Correct them all together.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/dts/crownbay.dts | 24 ++++++++++++------------
>  1 file changed, 12 insertions(+), 12 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 03/10] x86: Write correct bus number for the irq router
  2015-06-15  7:59 ` [U-Boot] [PATCH 03/10] x86: Write correct bus number for the irq router Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  0 siblings, 0 replies; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
> We should write correct bus number to the PIRQ routing table for the
> irq router from device tree, instead of hard-coded zero.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/cpu/irq.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 04/10] x86: Ignore function number when writing PIRQ routing table
  2015-06-15  7:59 ` [U-Boot] [PATCH 04/10] x86: Ignore function number when writing PIRQ routing table Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  0 siblings, 0 replies; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
> In fill_irq_info() pci device's function number is written into
> the table, however this is not really necessary. The function
> number can be anything as OS doesn't care about this field,
> neither does the PIRQ routing specification. Change to always
> writing 0 as the function number.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/cpu/irq.c | 7 +++----
>  1 file changed, 3 insertions(+), 4 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 05/10] x86: Reduce PIRQ routing table size
  2015-06-15  7:59 ` [U-Boot] [PATCH 05/10] x86: Reduce PIRQ routing table size Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  0 siblings, 0 replies; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
>
> There is no need to populate multiple irq info entries with the same
> bus number and device number, but with different interrupt pin. We
> can use the same entry to store all the 4 interrupt pin (INT A/B/C/D)
> routing information to reduce the whole PIRQ routing table size.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/cpu/irq.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 56 insertions(+), 9 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 06/10] x86: Clean up ioapic header file
  2015-06-15  7:59 ` [U-Boot] [PATCH 06/10] x86: Clean up ioapic header file Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  0 siblings, 0 replies; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
> Remove all the dead/unused macros from asm/ioapic.h.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/include/asm/ioapic.h | 26 +++-----------------------
>  1 file changed, 3 insertions(+), 23 deletions(-)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines
  2015-06-15  7:59 ` [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  2015-06-17  7:35     ` Bin Meng
  0 siblings, 1 reply; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

Hi Bin,

On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
> I/O APIC registers are addressed indirectly. Add io_apic_read() and
> io_apic_write() inline routines. Two macros for I/O APIC ID and
> version register offset are also added.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/include/asm/ioapic.h | 34 ++++++++++++++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
>
> diff --git a/arch/x86/include/asm/ioapic.h b/arch/x86/include/asm/ioapic.h
> index f5d69db..697eabb 100644
> --- a/arch/x86/include/asm/ioapic.h
> +++ b/arch/x86/include/asm/ioapic.h
> @@ -9,10 +9,44 @@
>  #ifndef __ASM_IOAPIC_H
>  #define __ASM_IOAPIC_H
>
> +#include <asm/io.h>
> +
>  #define IO_APIC_ADDR           0xfec00000
>
>  /* Direct addressed register */
>  #define IO_APIC_INDEX          (IO_APIC_ADDR + 0x00)
>  #define IO_APIC_DATA           (IO_APIC_ADDR + 0x10)
>
> +/* Indirect addressed register offset */
> +#define IO_APIC_ID             0x00
> +#define IO_APIC_VER            0x01
> +
> +/**
> + * io_apic_read() - Read I/O APIC register
> + *
> + * This routine reads I/O APIC indirect addressed register.
> + *
> + * @reg:       address of indirect addressed register
> + * @return:    register value to read
> + */
> +static inline u32 io_apic_read(u32 reg)
> +{
> +       writel(reg, IO_APIC_INDEX);
> +       return readl(IO_APIC_DATA);
> +}

Is there any reason these need to be inline?

> +
> +/**
> + * io_apic_write() - Write I/O APIC register
> + *
> + * This routine writes I/O APIC indirect addressed register.
> + *
> + * @reg:       address of indirect addressed register
> + * @val:       register value to write
> + */
> +static inline void io_apic_write(u32 reg, u32 val)
> +{
> +       writel(reg, IO_APIC_INDEX);
> +       writel(val, IO_APIC_DATA);
> +}
> +
>  #endif
> --
> 1.8.2.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs
  2015-06-15  8:00 ` [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  2015-06-17  7:42     ` Bin Meng
  0 siblings, 1 reply; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

Hi Bin,

On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
> The MP table provides a way for the operating system to support
> for symmetric multiprocessing as well as symmetric I/O interrupt
> handling with the local APIC and I/O APIC. We provide a bunch of
> APIs for U-Boot to write the floating table, configuration table
> header as well as base and extended table entries.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/include/asm/mpspec.h | 434 ++++++++++++++++++++++++++++++++++++++++++
>  arch/x86/lib/Makefile         |   1 +
>  arch/x86/lib/mpspec.c         | 238 +++++++++++++++++++++++
>  3 files changed, 673 insertions(+)
>  create mode 100644 arch/x86/include/asm/mpspec.h
>  create mode 100644 arch/x86/lib/mpspec.c

Does this avoid needing SFI?

I have one request below.

>
> diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
> new file mode 100644
> index 0000000..c489a58
> --- /dev/null
> +++ b/arch/x86/include/asm/mpspec.h
> @@ -0,0 +1,434 @@
> +/*
> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
> + *
> + * Adapted from coreboot src/arch/x86/include/arch/smp/mpspec.h
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#ifndef __ASM_MPSPEC_H
> +#define __ASM_MPSPEC_H
> +
> +/*
> + * Structure definitions for SMP machines following the
> + * Intel MultiProcessor Specification 1.4
> + */
> +
> +#define MPSPEC_V14     4
> +
> +#define MPF_SIGNATURE  "_MP_"
> +
> +struct mp_floating_table {
> +       char mpf_signature[4];  /* "_MP_" */
> +       u32 mpf_physptr;        /* Configuration table address */
> +       u8 mpf_length;          /* Our length (paragraphs) */
> +       u8 mpf_spec;            /* Specification version */
> +       u8 mpf_checksum;        /* Checksum (makes sum 0) */
> +       u8 mpf_feature1;        /* Predefined or Unique configuration? */
> +       u8 mpf_feature2;        /* Bit7 set for IMCR/PIC */
> +       u8 mpf_feature3;        /* Unused (0) */
> +       u8 mpf_feature4;        /* Unused (0) */
> +       u8 mpf_feature5;        /* Unused (0) */
> +};
> +
> +#define MPC_SIGNATURE  "PCMP"
> +
> +struct mp_config_table {
> +       char mpc_signature[4];  /* "PCMP" */
> +       u16 mpc_length;         /* Size of table */
> +       u8 mpc_spec;            /* Specification version */
> +       u8 mpc_checksum;        /* Checksum (makes sum 0) */
> +       char mpc_oem[8];        /* OEM ID */
> +       char mpc_product[12];   /* Product ID */
> +       u32 mpc_oemptr;         /* OEM table address */
> +       u16 mpc_oemsize;        /* OEM table size */
> +       u16 mpc_entry_count;    /* Number of entries in the table */
> +       u32 mpc_lapic;          /* Local APIC address */
> +       u16 mpe_length;         /* Extended table size */
> +       u8 mpe_checksum;        /* Extended table checksum */
> +       u8 reserved;
> +};
> +
> +/* Base MP configuration table entry types */
> +
> +enum mp_base_config_entry_type {
> +       MP_PROCESSOR,
> +       MP_BUS,
> +       MP_IOAPIC,
> +       MP_INTSRC,
> +       MP_LINTSRC
> +};
> +
> +#define MPC_CPU_EN     (1 << 0)
> +#define MPC_CPU_BP     (1 << 1)
> +
> +struct mpc_config_processor {
> +       u8 mpc_type;
> +       u8 mpc_apicid;
> +       u8 mpc_apicver;
> +       u8 mpc_cpuflag;
> +       u32 mpc_cpusignature;
> +       u32 mpc_cpufeature;
> +       u32 mpc_reserved[2];
> +};
> +
> +#define BUSTYPE_CBUS   "CBUS  "
> +#define BUSTYPE_CBUSII "CBUSII"
> +#define BUSTYPE_EISA   "EISA  "
> +#define BUSTYPE_FUTURE "FUTURE"
> +#define BUSTYPE_INTERN "INTERN"
> +#define BUSTYPE_ISA    "ISA   "
> +#define BUSTYPE_MBI    "MBI   "
> +#define BUSTYPE_MBII   "MBII  "
> +#define BUSTYPE_MCA    "MCA   "
> +#define BUSTYPE_MPI    "MPI   "
> +#define BUSTYPE_MPSA   "MPSA  "
> +#define BUSTYPE_NUBUS  "NUBUS "
> +#define BUSTYPE_PCI    "PCI   "
> +#define BUSTYPE_PCMCIA "PCMCIA"
> +#define BUSTYPE_TC     "TC    "
> +#define BUSTYPE_VL     "VL    "
> +#define BUSTYPE_VME    "VME   "
> +#define BUSTYPE_XPRESS "XPRESS"
> +
> +struct mpc_config_bus {
> +       u8 mpc_type;
> +       u8 mpc_busid;
> +       u8 mpc_bustype[6];
> +};
> +
> +#define MPC_APIC_USABLE        (1 << 0)
> +
> +struct mpc_config_ioapic {
> +       u8 mpc_type;
> +       u8 mpc_apicid;
> +       u8 mpc_apicver;
> +       u8 mpc_flags;
> +       u32 mpc_apicaddr;
> +};
> +
> +enum mp_irq_source_types {
> +       MP_INT,
> +       MP_NMI,
> +       MP_SMI,
> +       MP_EXTINT
> +};
> +
> +#define MP_IRQ_POLARITY_DEFAULT        0x0
> +#define MP_IRQ_POLARITY_HIGH   0x1
> +#define MP_IRQ_POLARITY_LOW    0x3
> +#define MP_IRQ_POLARITY_MASK   0x3
> +#define MP_IRQ_TRIGGER_DEFAULT 0x0
> +#define MP_IRQ_TRIGGER_EDGE    0x4
> +#define MP_IRQ_TRIGGER_LEVEL   0xc
> +#define MP_IRQ_TRIGGER_MASK    0xc
> +
> +#define MP_APIC_ALL            0xff
> +
> +struct mpc_config_intsrc {
> +       u8 mpc_type;
> +       u8 mpc_irqtype;
> +       u16 mpc_irqflag;
> +       u8 mpc_srcbus;
> +       u8 mpc_srcbusirq;
> +       u8 mpc_dstapic;
> +       u8 mpc_dstirq;
> +};
> +
> +struct mpc_config_lintsrc {
> +       u8 mpc_type;
> +       u8 mpc_irqtype;
> +       u16 mpc_irqflag;
> +       u8 mpc_srcbusid;
> +       u8 mpc_srcbusirq;
> +       u8 mpc_destapic;
> +       u8 mpc_destlint;
> +};
> +
> +/* Extended MP configuration table entry types */
> +
> +enum mp_ext_config_entry_type {
> +       MPE_SYSTEM_ADDRESS_SPACE = 128,
> +       MPE_BUS_HIERARCHY,
> +       MPE_COMPAT_ADDRESS_SPACE
> +};
> +
> +struct mp_ext_config {
> +       u8 mpe_type;
> +       u8 mpe_length;
> +};
> +
> +#define ADDRESS_TYPE_IO                0
> +#define ADDRESS_TYPE_MEM       1
> +#define ADDRESS_TYPE_PREFETCH  2
> +
> +struct mp_ext_system_address_space {
> +       u8 mpe_type;
> +       u8 mpe_length;
> +       u8 mpe_busid;
> +       u8 mpe_addr_type;
> +       u32 mpe_addr_base_low;
> +       u32 mpe_addr_base_high;
> +       u32 mpe_addr_length_low;
> +       u32 mpe_addr_length_high;
> +};
> +
> +#define BUS_SUBTRACTIVE_DECODE (1 << 0)
> +
> +struct mp_ext_bus_hierarchy {
> +       u8 mpe_type;
> +       u8 mpe_length;
> +       u8 mpe_busid;
> +       u8 mpe_bus_info;
> +       u8 mpe_parent_busid;
> +       u8 reserved[3];
> +};
> +
> +#define ADDRESS_RANGE_ADD      0
> +#define ADDRESS_RANGE_SUBTRACT 1
> +
> +/*
> + * X100 - X3FF
> + * X500 - X7FF
> + * X900 - XBFF
> + * XD00 - XFFF
> + */
> +#define RANGE_LIST_IO_ISA      0
> +/*
> + * X3B0 - X3BB
> + * X3C0 - X3DF
> + * X7B0 - X7BB
> + * X7C0 - X7DF
> + * XBB0 - XBBB
> + * XBC0 - XBDF
> + * XFB0 - XFBB
> + * XFC0 - XCDF
> + */
> +#define RANGE_LIST_IO_VGA      1
> +
> +struct mp_ext_compat_address_space {
> +       u8 mpe_type;
> +       u8 mpe_length;
> +       u8 mpe_busid;
> +       u8 mpe_addr_modifier;
> +       u32 mpe_range_list;
> +};
> +
> +/**
> + * mp_next_mpc_entry() - Compute MP configuration table end to be used as
> + *                       next base table entry start address
> + *
> + * This computes the end address of current MP configuration table, without
> + * counting any extended configuration table entry.
> + *
> + * @mc:                configuration table header address
> + * @return:    configuration table end address
> + */
> +static inline u32 mp_next_mpc_entry(struct mp_config_table *mc)
> +{
> +       return (u32)mc + mc->mpc_length;
> +}
> +
> +/**
> + * mp_add_mpc_entry() - Add a base MP configuration table entry
> + *
> + * This adds the base MP configuration table entry size with
> + * added base table entry length and increases entry count by 1.
> + *
> + * @mc:                configuration table header address
> + * @length:    length of the added table entry
> + */
> +static inline void mp_add_mpc_entry(struct mp_config_table *mc, u16 length)

Can we avoid using u16 and u8 in parameters? It creates unnecessary
masking I think, and looks ugly. unsigned int or uint should work. I
think u32 is OK if you need it, although I'd even question that.

> +{
> +       mc->mpc_length += length;
> +       mc->mpc_entry_count++;
> +}
> +
> +/**
> + * mp_next_mpe_entry() - Compute MP configuration table end to be used as
> + *                       next extended table entry start address
> + *
> + * This computes the end address of current MP configuration table,
> + * including any extended configuration table entry.
> + *
> + * @mc:                configuration table header address
> + * @return:    configuration table end address
> + */
> +static inline u32 mp_next_mpe_entry(struct mp_config_table *mc)
> +{
> +       return (u32)mc + mc->mpc_length + mc->mpe_length;
> +}
> +
> +/**
> + * mp_add_mpe_entry() - Add an extended MP configuration table entry
> + *
> + * This adds the extended MP configuration table entry size with
> + * added extended table entry length.
> + *
> + * @mc:                configuration table header address
> + * @mpe:       extended table entry base address
> + */
> +static inline void mp_add_mpe_entry(struct mp_config_table *mc,
> +                                   struct mp_ext_config *mpe)
> +{
> +       mc->mpe_length += mpe->mpe_length;
> +}
> +
> +/**
> + * mp_write_floating_table() - Write the MP floating table
> + *
> + * This writes the MP floating table, and points MP configuration table
> + * to its end address so that MP configuration table follows immediately
> + * after the floating table.
> + *
> + * @mf:                MP floating table base address
> + * @return:    MP configuration table header address
> + */
> +struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf);
> +
> +/**
> + * mp_config_table_init() - Initialize the MP configuration table header
> + *
> + * This populates the MP configuration table header with valid bits.
> + *
> + * @mc:                MP configuration table header address
> + */
> +void mp_config_table_init(struct mp_config_table *mc);
> +
> +/**
> + * mp_write_processor() - Write a processor entry
> + *
> + * This writes a processor entry to the configuration table.
> + *
> + * @mc:                MP configuration table header address
> + */
> +void mp_write_processor(struct mp_config_table *mc);
> +
> +/**
> + * mp_write_bus() - Write a bus entry
> + *
> + * This writes a bus entry to the configuration table.
> + *
> + * @mc:                MP configuration table header address
> + * @id:                bus id
> + * @bustype:   bus type name
> + */
> +void mp_write_bus(struct mp_config_table *mc, u8 id, const char *bustype);
> +
> +/**
> + * mp_write_ioapic() - Write an I/O APIC entry
> + *
> + * This writes an I/O APIC entry to the configuration table.
> + *
> + * @mc:                MP configuration table header address
> + * @id:                I/O APIC id
> + * @ver:       I/O APIC version
> + * @apicaddr:  I/O APIC address
> + */
> +void mp_write_ioapic(struct mp_config_table *mc, u8 id, u8 ver, u32 apicaddr);
> +
> +/**
> + * mp_write_intsrc() - Write an I/O interrupt assignment entry
> + *
> + * This writes an I/O interrupt assignment entry to the configuration table.
> + *
> + * @mc:                MP configuration table header address
> + * @irqtype:   IRQ type (INT/NMI/SMI/ExtINT)
> + * @irqflag:   IRQ flag (level/trigger)
> + * @srcbus:    source bus id where the interrupt comes from
> + * @srcbusirq: IRQ number mapped on the source bus
> + * @dstapic:   destination I/O APIC id where the interrupt goes to
> + * @dstirq:    destination I/O APIC pin where the interrupt goes to
> + */
> +void mp_write_intsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
> +                    u8 srcbus, u8 srcbusirq, u8 dstapic, u8 dstirq);
> +
> +/**
> + * mp_write_pci_intsrc() - Write a PCI interrupt assignment entry
> + *
> + * This writes a PCI interrupt assignment entry to the configuration table.
> + *
> + * @mc:                MP configuration table header address
> + * @irqtype:   IRQ type (INT/NMI/SMI/ExtINT)
> + * @srcbus:    PCI bus number where the interrupt comes from
> + * @dev:       device number on the PCI bus
> + * @pin:       PCI interrupt pin (INT A/B/C/D)
> + * @dstapic:   destination I/O APIC id where the interrupt goes to
> + * @dstirq:    destination I/O APIC pin where the interrupt goes to
> + */
> +void mp_write_pci_intsrc(struct mp_config_table *mc, u8 irqtype,
> +                        u8 srcbus, u8 dev, u8 pin, u8 dstapic, u8 dstirq);
> +
> +/**
> + * mp_write_lintsrc() - Write a local interrupt assignment entry
> + *
> + * This writes a local interrupt assignment entry to the configuration table.
> + *
> + * @mc:                MP configuration table header address
> + * @irqtype:   IRQ type (INT/NMI/SMI/ExtINT)
> + * @irqflag:   IRQ flag (level/trigger)
> + * @srcbus:    PCI bus number where the interrupt comes from
> + * @srcbusirq: IRQ number mapped on the source bus
> + * @dstapic:   destination local APIC id where the interrupt goes to
> + * @destlint:  destination local APIC pin where the interrupt goes to
> + */
> +void mp_write_lintsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
> +                     u8 srcbus, u8 srcbusirq, u8 destapic, u8 destlint);
> +
> +
> +/**
> + * mp_write_address_space() - Write a system address space entry
> + *
> + * This writes a system address space entry to the configuration table.
> + *
> + * @mc:                        MP configuration table header address
> + * @busid:             bus id for the bus where system address space is mapped
> + * @addr_type:         system address type
> + * @addr_base_low:     starting address low
> + * @addr_base_high:    starting address high
> + * @addr_length_low:   address length low
> + * @addr_length_high:  address length high
> + */
> +void mp_write_address_space(struct mp_config_table *mc,
> +                           u8 busid, u8 addr_type,
> +                           u32 addr_base_low, u32 addr_base_high,
> +                           u32 addr_length_low, u32 addr_length_high);
> +
> +/**
> + * mp_write_bus_hierarchy() - Write a bus hierarchy descriptor entry
> + *
> + * This writes a bus hierarchy descriptor entry to the configuration table.
> + *
> + * @mc:                        MP configuration table header address
> + * @busid:             bus id
> + * @bus_info:          bit0 indicates if the bus is a subtractive decode bus
> + * @parent_busid:      parent bus id
> + */
> +void mp_write_bus_hierarchy(struct mp_config_table *mc,
> +                           u8 busid, u8 bus_info, u8 parent_busid);
> +
> +/**
> + * mp_write_compat_address_space() - Write a compat bus address space entry
> + *
> + * This writes a compatibility bus address space modifier entry to the
> + * configuration table.
> + *
> + * @mc:                        MP configuration table header address
> + * @busid:             bus id
> + * @addr_modifier:     add or subtract to predefined address range list
> + * @range_list:                list of predefined address space ranges
> + */
> +void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
> +                                  u8 addr_modifier, u32 range_list);
> +
> +/**
> + * mptable_finalize() - Finalize the MP table
> + *
> + * This finalizes the MP table by calculating required checksums.
> + *
> + * @mc:                MP configuration table header address
> + * @return:    MP table end address
> + */
> +u32 mptable_finalize(struct mp_config_table *mc);
> +
> +#endif /* __ASM_MPSPEC_H */
> diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
> index 70ad19b..43489fd 100644
> --- a/arch/x86/lib/Makefile
> +++ b/arch/x86/lib/Makefile
> @@ -15,6 +15,7 @@ obj-y += gcc.o
>  obj-y  += init_helpers.o
>  obj-y  += interrupts.o
>  obj-y  += lpc-uclass.o
> +obj-y  += mpspec.o
>  obj-y += cmd_mtrr.o
>  obj-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o
>  obj-$(CONFIG_SYS_PCAT_TIMER) += pcat_timer.o
> diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
> new file mode 100644
> index 0000000..657df22
> --- /dev/null
> +++ b/arch/x86/lib/mpspec.c
> @@ -0,0 +1,238 @@
> +/*
> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
> + *
> + * Adapted from coreboot src/arch/x86/boot/mpspec.c
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <cpu.h>
> +#include <dm.h>
> +#include <asm/cpu.h>
> +#include <asm/ioapic.h>
> +#include <asm/lapic.h>
> +#include <asm/mpspec.h>
> +#include <asm/tables.h>
> +#include <dm/uclass-internal.h>
> +
> +struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
> +{
> +       u32 mc;
> +
> +       memcpy(mf->mpf_signature, MPF_SIGNATURE, 4);
> +       mf->mpf_physptr = (u32)mf + sizeof(struct mp_floating_table);
> +       mf->mpf_length = 1;
> +       mf->mpf_spec = MPSPEC_V14;
> +       mf->mpf_checksum = 0;
> +       /* We don't use the default configuration table */
> +       mf->mpf_feature1 = 0;
> +       /* Indicate that virtual wire mode is always implemented */
> +       mf->mpf_feature2 = 0;
> +       mf->mpf_feature3 = 0;
> +       mf->mpf_feature4 = 0;
> +       mf->mpf_feature5 = 0;
> +       mf->mpf_checksum = table_compute_checksum(mf, mf->mpf_length * 16);
> +
> +       mc = (u32)mf + sizeof(struct mp_floating_table);
> +       return (struct mp_config_table *)mc;
> +}
> +
> +void mp_config_table_init(struct mp_config_table *mc)
> +{
> +       int start, len;
> +       int i;
> +
> +       memcpy(mc->mpc_signature, MPC_SIGNATURE, 4);
> +       mc->mpc_length = sizeof(struct mp_config_table);
> +       mc->mpc_spec = MPSPEC_V14;
> +       mc->mpc_checksum = 0;
> +       mc->mpc_oemptr = 0;
> +       mc->mpc_oemsize = 0;
> +       mc->mpc_entry_count = 0;
> +       mc->mpc_lapic = LAPIC_DEFAULT_BASE;
> +       mc->mpe_length = 0;
> +       mc->mpe_checksum = 0;
> +       mc->reserved = 0;
> +
> +       /* The oem/product id fields are exactly 8/12 bytes long */
> +       strncpy(mc->mpc_oem, CONFIG_SYS_VENDOR, 8);
> +       strncpy(mc->mpc_product, CONFIG_SYS_BOARD, 12);
> +
> +       /* Fill the remaining bytes with spaces */
> +       len = strlen(CONFIG_SYS_VENDOR);
> +       start = len < 8 ? len : 8;
> +       for (i = start; i < 8; i++)
> +               mc->mpc_oem[i] = ' ';

Can we create a strncpy that pads with spaces?

> +       len = strlen(CONFIG_SYS_BOARD);
> +       start = len < 12 ? len : 12;
> +       for (i = start; i < 12; i++)
> +               mc->mpc_product[i] = ' ';
> +}
> +
> +void mp_write_processor(struct mp_config_table *mc)
> +{
> +       struct mpc_config_processor *mpc;
> +       struct udevice *dev;
> +       u8 boot_apicid, apicver;
> +       u32 cpusignature, cpufeature;
> +       struct cpuid_result result;
> +
> +       boot_apicid = lapicid();
> +       apicver = lapic_read(LAPIC_LVR) & 0xff;
> +       result = cpuid(1);
> +       cpusignature = result.eax;
> +       cpufeature = result.edx;
> +
> +       for (uclass_find_first_device(UCLASS_CPU, &dev);
> +            dev;
> +            uclass_find_next_device(&dev)) {
> +               struct cpu_platdata *plat = dev_get_parent_platdata(dev);
> +               u8 cpuflag = MPC_CPU_EN;
> +
> +               if (!device_active(dev))
> +                       continue;
> +
> +               mpc = (struct mpc_config_processor *)mp_next_mpc_entry(mc);
> +               mpc->mpc_type = MP_PROCESSOR;
> +               mpc->mpc_apicid = plat->cpu_id;
> +               mpc->mpc_apicver = apicver;
> +               if (boot_apicid == plat->cpu_id)
> +                       cpuflag |= MPC_CPU_BP;
> +               mpc->mpc_cpuflag = cpuflag;
> +               mpc->mpc_cpusignature = cpusignature;
> +               mpc->mpc_cpufeature = cpufeature;
> +               mpc->mpc_reserved[0] = 0;
> +               mpc->mpc_reserved[1] = 0;
> +               mp_add_mpc_entry(mc, sizeof(*mpc));
> +       }
> +}
> +
> +void mp_write_bus(struct mp_config_table *mc, u8 id, const char *bustype)
> +{
> +       struct mpc_config_bus *mpc;
> +
> +       mpc = (struct mpc_config_bus *)mp_next_mpc_entry(mc);
> +       mpc->mpc_type = MP_BUS;
> +       mpc->mpc_busid = id;
> +       memcpy(mpc->mpc_bustype, bustype, 6);
> +       mp_add_mpc_entry(mc, sizeof(*mpc));
> +}
> +
> +void mp_write_ioapic(struct mp_config_table *mc, u8 id, u8 ver, u32 apicaddr)
> +{
> +       struct mpc_config_ioapic *mpc;
> +
> +       mpc = (struct mpc_config_ioapic *)mp_next_mpc_entry(mc);
> +       mpc->mpc_type = MP_IOAPIC;
> +       mpc->mpc_apicid = id;
> +       mpc->mpc_apicver = ver;
> +       mpc->mpc_flags = MPC_APIC_USABLE;
> +       mpc->mpc_apicaddr = apicaddr;
> +       mp_add_mpc_entry(mc, sizeof(*mpc));
> +}
> +
> +void mp_write_intsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
> +                    u8 srcbus, u8 srcbusirq, u8 dstapic, u8 dstirq)
> +{
> +       struct mpc_config_intsrc *mpc;
> +
> +       mpc = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
> +       mpc->mpc_type = MP_INTSRC;
> +       mpc->mpc_irqtype = irqtype;
> +       mpc->mpc_irqflag = irqflag;
> +       mpc->mpc_srcbus = srcbus;
> +       mpc->mpc_srcbusirq = srcbusirq;
> +       mpc->mpc_dstapic = dstapic;
> +       mpc->mpc_dstirq = dstirq;
> +       mp_add_mpc_entry(mc, sizeof(*mpc));
> +}
> +
> +void mp_write_pci_intsrc(struct mp_config_table *mc, u8 irqtype,
> +                        u8 srcbus, u8 dev, u8 pin, u8 dstapic, u8 dstirq)
> +{
> +       u8 srcbusirq = (dev << 2) | (pin - 1);
> +
> +       mp_write_intsrc(mc, irqtype, MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_LOW,
> +                       srcbus, srcbusirq, dstapic, dstirq);
> +}
> +
> +void mp_write_lintsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
> +                     u8 srcbus, u8 srcbusirq, u8 destapic, u8 destlint)
> +{
> +       struct mpc_config_lintsrc *mpc;
> +
> +       mpc = (struct mpc_config_lintsrc *)mp_next_mpc_entry(mc);
> +       mpc->mpc_type = MP_LINTSRC;
> +       mpc->mpc_irqtype = irqtype;
> +       mpc->mpc_irqflag = irqflag;
> +       mpc->mpc_srcbusid = srcbus;
> +       mpc->mpc_srcbusirq = srcbusirq;
> +       mpc->mpc_destapic = destapic;
> +       mpc->mpc_destlint = destlint;
> +       mp_add_mpc_entry(mc, sizeof(*mpc));
> +}
> +
> +void mp_write_address_space(struct mp_config_table *mc,
> +                           u8 busid, u8 addr_type,
> +                           u32 addr_base_low, u32 addr_base_high,
> +                           u32 addr_length_low, u32 addr_length_high)
> +{
> +       struct mp_ext_system_address_space *mpe;
> +
> +       mpe = (struct mp_ext_system_address_space *)mp_next_mpe_entry(mc);
> +       mpe->mpe_type = MPE_SYSTEM_ADDRESS_SPACE;
> +       mpe->mpe_length = sizeof(*mpe);
> +       mpe->mpe_busid = busid;
> +       mpe->mpe_addr_type = addr_type;
> +       mpe->mpe_addr_base_low = addr_base_low;
> +       mpe->mpe_addr_base_high = addr_base_high;
> +       mpe->mpe_addr_length_low = addr_length_low;
> +       mpe->mpe_addr_length_high = addr_length_high;
> +       mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
> +}
> +
> +void mp_write_bus_hierarchy(struct mp_config_table *mc,
> +                           u8 busid, u8 bus_info, u8 parent_busid)
> +{
> +       struct mp_ext_bus_hierarchy *mpe;
> +
> +       mpe = (struct mp_ext_bus_hierarchy *)mp_next_mpe_entry(mc);
> +       mpe->mpe_type = MPE_BUS_HIERARCHY;
> +       mpe->mpe_length = sizeof(*mpe);
> +       mpe->mpe_busid = busid;
> +       mpe->mpe_bus_info = bus_info;
> +       mpe->mpe_parent_busid = parent_busid;
> +       mpe->reserved[0] = 0;
> +       mpe->reserved[1] = 0;
> +       mpe->reserved[2] = 0;
> +       mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
> +}
> +
> +void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
> +                                  u8 addr_modifier, u32 range_list)
> +{
> +       struct mp_ext_compat_address_space *mpe;
> +
> +       mpe = (struct mp_ext_compat_address_space *)mp_next_mpe_entry(mc);
> +       mpe->mpe_type = MPE_COMPAT_ADDRESS_SPACE;
> +       mpe->mpe_length = sizeof(*mpe);
> +       mpe->mpe_busid = busid;
> +       mpe->mpe_addr_modifier = addr_modifier;
> +       mpe->mpe_range_list = range_list;
> +       mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
> +}
> +
> +u32 mptable_finalize(struct mp_config_table *mc)
> +{
> +       u32 end;
> +
> +       mc->mpe_checksum = table_compute_checksum((void *)mp_next_mpc_entry(mc),
> +                                                 mc->mpe_length);
> +       mc->mpc_checksum = table_compute_checksum(mc, mc->mpc_length);
> +       end = mp_next_mpe_entry(mc);
> +
> +       debug("Write the MP table at: %x - %x\n", (u32)mc, end);
> +
> +       return end;
> +}
> --
> 1.8.2.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 09/10] x86: Generate a valid MultiProcessor (MP) table
  2015-06-15  8:00 ` [U-Boot] [PATCH 09/10] x86: Generate a valid MultiProcessor (MP) table Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  2015-06-17  7:49     ` Bin Meng
  0 siblings, 1 reply; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

Hi Bin,

On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
> Implement a weak write_mp_table() to create a minimal working MP
> table. This includes an MP floating table, a configuration table
> header and all of the 5 base configuration table entries. The I/O
> interrupt assignment table entry is created based on the same
> information used in the creation of PIRQ routing table from device
> tree. A check duplicated entry logic is applied to prevent writing
> multiple I/O interrupt entries with the same information.
>
> Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
> need actually write the MP table at the F seg, just like we did for
> PIRQ routing and SFI tables. With MP table existence, linux kernel
> will switch to I/O APIC and local APIC to process all the peripheral
> interrupts instead of 8259 PICs. This takes full advantage of the
> multicore hardware and the SMP kernel.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/Kconfig              |   8 +++
>  arch/x86/include/asm/mpspec.h |  10 +++
>  arch/x86/lib/mpspec.c         | 147 ++++++++++++++++++++++++++++++++++++++++++
>  arch/x86/lib/tables.c         |   5 ++
>  4 files changed, 170 insertions(+)
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 1aeae9d..d4b2772 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -319,6 +319,14 @@ config GENERATE_SFI_TABLE
>
>           For more information, see http://simplefirmware.org
>
> +config GENERATE_MP_TABLE
> +       bool "Generate an MP (Multi-Processor) table"
> +       help
> +         Generate an MP (Multi-Processor) table for this board. The MP table
> +         provides a way for the operating system to support for symmetric
> +         multiprocessing as well as symmetric I/O interrupt handling with
> +         the local APIC and I/O APIC.
> +
>  endmenu
>
>  config MAX_PIRQ_LINKS
> diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
> index c489a58..e9e1a2a 100644
> --- a/arch/x86/include/asm/mpspec.h
> +++ b/arch/x86/include/asm/mpspec.h
> @@ -431,4 +431,14 @@ void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
>   */
>  u32 mptable_finalize(struct mp_config_table *mc);
>
> +/**
> + * write_mp_table() - Write MP table
> + *
> + * This writes MP table at a given address.
> + *
> + * @addr:      start address to write MP table
> + * @return:    end address of MP table
> + */
> +u32 write_mp_table(u32 addr);
> +
>  #endif /* __ASM_MPSPEC_H */
> diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
> index 657df22..1e3523f 100644
> --- a/arch/x86/lib/mpspec.c
> +++ b/arch/x86/lib/mpspec.c
> @@ -9,13 +9,17 @@
>  #include <common.h>
>  #include <cpu.h>
>  #include <dm.h>
> +#include <fdtdec.h>
>  #include <asm/cpu.h>
> +#include <asm/irq.h>
>  #include <asm/ioapic.h>
>  #include <asm/lapic.h>
>  #include <asm/mpspec.h>
>  #include <asm/tables.h>
>  #include <dm/uclass-internal.h>
>
> +DECLARE_GLOBAL_DATA_PTR;
> +
>  struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
>  {
>         u32 mc;
> @@ -236,3 +240,146 @@ u32 mptable_finalize(struct mp_config_table *mc)
>
>         return end;
>  }
> +
> +static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
> +                                      u32 apicid, int external_int2)
> +{
> +       int i;
> +
> +       mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                       bus_isa, 0, apicid, 0);
> +       mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                       bus_isa, 1, apicid, 1);
> +       mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                       bus_isa, 0, apicid, 2);
> +
> +       for (i = 3; i < 16; i++)
> +               mp_write_intsrc(mc, MP_INT,
> +                               MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                               bus_isa, i, apicid, i);
> +}
> +
> +static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
> +                           int entry_num, u8 bus, u8 device, u8 pin)

Again can we avoid u8 on function parameters?

Also this one coudl use a comment.

> +{
> +       struct mpc_config_intsrc *intsrc = intsrc_base;
> +       int i;
> +
> +       for (i = 0; i < entry_num; i++) {
> +               if (intsrc->mpc_srcbus == bus &&
> +                   intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
> +                       break;
> +               intsrc++;
> +       }
> +
> +       return (i == entry_num) ? false : true;
> +}
> +
> +static void mptable_add_intsrc(struct mp_config_table *mc,
> +                              int bus_isa, u32 apicid)
> +{
> +       struct mpc_config_intsrc *intsrc_base;
> +       int intsrc_entries = 0;
> +       const void *blob = gd->fdt_blob;
> +       int node;
> +       int len, count;
> +       const u32 *cell;
> +       int i;
> +
> +       /* Legacy Interrupts */
> +       debug("Writing ISA IRQs\n");
> +       mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
> +
> +       /* Get I/O interrupt information from device tree */
> +       node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
> +       if (node < 0) {
> +               debug("%s: Cannot find irq router node\n", __func__);
> +               return;

return -ENOENT

(and it's caller should check for error)

> +       }
> +
> +       cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
> +       if (!cell)
> +               return;
> +
> +       if ((len % sizeof(struct pirq_routing)) == 0)
> +               count = len / sizeof(struct pirq_routing);
> +       else
> +               return;
> +
> +       intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
> +
> +       for (i = 0; i < count; i++) {
> +               struct pirq_routing pr;
> +
> +               pr.bdf = fdt_addr_to_cpu(cell[0]);
> +               pr.pin = fdt_addr_to_cpu(cell[1]);
> +               pr.pirq = fdt_addr_to_cpu(cell[2]);
> +
> +               if (check_dup_entry(intsrc_base, intsrc_entries,
> +                                   PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
> +                       debug("found entry for bus %d device %d INT%c, skipping\n",
> +                             PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
> +                             'A' + pr.pin - 1);
> +                       cell += sizeof(struct pirq_routing) / sizeof(u32);
> +                       continue;
> +               }
> +
> +               /* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
> +               mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
> +                                   PCI_DEV(pr.bdf), pr.pin, apicid,
> +                                   pr.pirq + 16);
> +               intsrc_entries++;
> +               cell += sizeof(struct pirq_routing) / sizeof(u32);
> +       }
> +}
> +
> +static void mptable_add_lintsrc(struct mp_config_table *mc, u32 bus_isa)
> +{
> +       mp_write_lintsrc(mc, MP_EXTINT,
> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                        bus_isa, 0, MP_APIC_ALL, 0);
> +       mp_write_lintsrc(mc, MP_NMI,
> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                        bus_isa, 0, MP_APIC_ALL, 1);
> +}
> +
> +__weak u32 write_mp_table(u32 addr)

Why does this need to be weak?

> +{
> +       struct mp_config_table *mc;
> +       int ioapic_id, ioapic_ver;
> +       int bus_isa = 0xff;
> +       u32 end;
> +
> +       /* 16 byte align the table address */
> +       addr = ALIGN(addr, 16);
> +
> +       /* Write floating table */
> +       mc = mp_write_floating_table((struct mp_floating_table *)addr);
> +
> +       /* Write configuration table header */
> +       mp_config_table_init(mc);
> +
> +       /* Write processor entry */
> +       mp_write_processor(mc);
> +
> +       /* Write bus entry */
> +       mp_write_bus(mc, bus_isa, BUSTYPE_ISA);
> +
> +       /* Write I/O APIC entry */
> +       ioapic_id = io_apic_read(IO_APIC_ID) >> 24;
> +       ioapic_ver = io_apic_read(IO_APIC_VER) & 0xff;
> +       mp_write_ioapic(mc, ioapic_id, ioapic_ver, IO_APIC_ADDR);
> +
> +       /* Write I/O interrupt assignment entry */
> +       mptable_add_intsrc(mc, bus_isa, ioapic_id);
> +
> +       /* Write local interrupt assignment entry */
> +       mptable_add_lintsrc(mc, bus_isa);
> +
> +       /* Finalize the MP table */
> +       end = mptable_finalize(mc);
> +
> +       return end;
> +}
> diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
> index 8031201..d3bef6f 100644
> --- a/arch/x86/lib/tables.c
> +++ b/arch/x86/lib/tables.c
> @@ -6,6 +6,7 @@
>
>  #include <common.h>
>  #include <asm/sfi.h>
> +#include <asm/mpspec.h>
>  #include <asm/tables.h>
>
>  u8 table_compute_checksum(void *v, int len)
> @@ -32,4 +33,8 @@ void write_tables(void)
>         rom_table_end = write_sfi_table(rom_table_end);
>         rom_table_end = ALIGN(rom_table_end, 1024);
>  #endif
> +#ifdef CONFIG_GENERATE_MP_TABLE
> +       rom_table_end = write_mp_table(rom_table_end);
> +       rom_table_end = ALIGN(rom_table_end, 1024);
> +#endif
>  }
> --
> 1.8.2.1
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 10/10] x86: crownbay: Enable writing MP table
  2015-06-15  8:00 ` [U-Boot] [PATCH 10/10] x86: crownbay: Enable writing MP table Bin Meng
@ 2015-06-16  2:46   ` Simon Glass
  0 siblings, 0 replies; 26+ messages in thread
From: Simon Glass @ 2015-06-16  2:46 UTC (permalink / raw)
  To: u-boot

On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
> Enable writing MP table for Intel Crown Bay board.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>
> ---
>
>  configs/crownbay_defconfig | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines
  2015-06-16  2:46   ` Simon Glass
@ 2015-06-17  7:35     ` Bin Meng
  2015-06-17 13:15       ` Simon Glass
  0 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-17  7:35 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
> Hi Bin,
>
> On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
>> I/O APIC registers are addressed indirectly. Add io_apic_read() and
>> io_apic_write() inline routines. Two macros for I/O APIC ID and
>> version register offset are also added.
>>
>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>> ---
>>
>>  arch/x86/include/asm/ioapic.h | 34 ++++++++++++++++++++++++++++++++++
>>  1 file changed, 34 insertions(+)
>>
>> diff --git a/arch/x86/include/asm/ioapic.h b/arch/x86/include/asm/ioapic.h
>> index f5d69db..697eabb 100644
>> --- a/arch/x86/include/asm/ioapic.h
>> +++ b/arch/x86/include/asm/ioapic.h
>> @@ -9,10 +9,44 @@
>>  #ifndef __ASM_IOAPIC_H
>>  #define __ASM_IOAPIC_H
>>
>> +#include <asm/io.h>
>> +
>>  #define IO_APIC_ADDR           0xfec00000
>>
>>  /* Direct addressed register */
>>  #define IO_APIC_INDEX          (IO_APIC_ADDR + 0x00)
>>  #define IO_APIC_DATA           (IO_APIC_ADDR + 0x10)
>>
>> +/* Indirect addressed register offset */
>> +#define IO_APIC_ID             0x00
>> +#define IO_APIC_VER            0x01
>> +
>> +/**
>> + * io_apic_read() - Read I/O APIC register
>> + *
>> + * This routine reads I/O APIC indirect addressed register.
>> + *
>> + * @reg:       address of indirect addressed register
>> + * @return:    register value to read
>> + */
>> +static inline u32 io_apic_read(u32 reg)
>> +{
>> +       writel(reg, IO_APIC_INDEX);
>> +       return readl(IO_APIC_DATA);
>> +}
>
> Is there any reason these need to be inline?
>

No, but I was referring to asm/lapic.h inside which the lapic register
access routines are inline as well. Do you want to remove inline for
lapic and ioapic?

[snip]

Regards,
Bin

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs
  2015-06-16  2:46   ` Simon Glass
@ 2015-06-17  7:42     ` Bin Meng
  2015-06-17 13:15       ` Simon Glass
  0 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-17  7:42 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
> Hi Bin,
>
> On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
>> The MP table provides a way for the operating system to support
>> for symmetric multiprocessing as well as symmetric I/O interrupt
>> handling with the local APIC and I/O APIC. We provide a bunch of
>> APIs for U-Boot to write the floating table, configuration table
>> header as well as base and extended table entries.
>>
>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>> ---
>>
>>  arch/x86/include/asm/mpspec.h | 434 ++++++++++++++++++++++++++++++++++++++++++
>>  arch/x86/lib/Makefile         |   1 +
>>  arch/x86/lib/mpspec.c         | 238 +++++++++++++++++++++++
>>  3 files changed, 673 insertions(+)
>>  create mode 100644 arch/x86/include/asm/mpspec.h
>>  create mode 100644 arch/x86/lib/mpspec.c
>
> Does this avoid needing SFI?
>

Yes, SFI is not needed, although it can be turned on and built pass on
Crown Bay without any problem. I've tested SFI on Crown Bay and the
only issue is that interrupt is still routed to 8259 instead of I/O
APIC.

> I have one request below.
>
>>
>> diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
>> new file mode 100644
>> index 0000000..c489a58
>> --- /dev/null
>> +++ b/arch/x86/include/asm/mpspec.h
>> @@ -0,0 +1,434 @@
>> +/*
>> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
>> + *
>> + * Adapted from coreboot src/arch/x86/include/arch/smp/mpspec.h
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#ifndef __ASM_MPSPEC_H
>> +#define __ASM_MPSPEC_H
>> +
>> +/*
>> + * Structure definitions for SMP machines following the
>> + * Intel MultiProcessor Specification 1.4
>> + */
>> +
>> +#define MPSPEC_V14     4
>> +
>> +#define MPF_SIGNATURE  "_MP_"
>> +
>> +struct mp_floating_table {
>> +       char mpf_signature[4];  /* "_MP_" */
>> +       u32 mpf_physptr;        /* Configuration table address */
>> +       u8 mpf_length;          /* Our length (paragraphs) */
>> +       u8 mpf_spec;            /* Specification version */
>> +       u8 mpf_checksum;        /* Checksum (makes sum 0) */
>> +       u8 mpf_feature1;        /* Predefined or Unique configuration? */
>> +       u8 mpf_feature2;        /* Bit7 set for IMCR/PIC */
>> +       u8 mpf_feature3;        /* Unused (0) */
>> +       u8 mpf_feature4;        /* Unused (0) */
>> +       u8 mpf_feature5;        /* Unused (0) */
>> +};
>> +
>> +#define MPC_SIGNATURE  "PCMP"
>> +
>> +struct mp_config_table {
>> +       char mpc_signature[4];  /* "PCMP" */
>> +       u16 mpc_length;         /* Size of table */
>> +       u8 mpc_spec;            /* Specification version */
>> +       u8 mpc_checksum;        /* Checksum (makes sum 0) */
>> +       char mpc_oem[8];        /* OEM ID */
>> +       char mpc_product[12];   /* Product ID */
>> +       u32 mpc_oemptr;         /* OEM table address */
>> +       u16 mpc_oemsize;        /* OEM table size */
>> +       u16 mpc_entry_count;    /* Number of entries in the table */
>> +       u32 mpc_lapic;          /* Local APIC address */
>> +       u16 mpe_length;         /* Extended table size */
>> +       u8 mpe_checksum;        /* Extended table checksum */
>> +       u8 reserved;
>> +};
>> +
>> +/* Base MP configuration table entry types */
>> +
>> +enum mp_base_config_entry_type {
>> +       MP_PROCESSOR,
>> +       MP_BUS,
>> +       MP_IOAPIC,
>> +       MP_INTSRC,
>> +       MP_LINTSRC
>> +};
>> +
>> +#define MPC_CPU_EN     (1 << 0)
>> +#define MPC_CPU_BP     (1 << 1)
>> +
>> +struct mpc_config_processor {
>> +       u8 mpc_type;
>> +       u8 mpc_apicid;
>> +       u8 mpc_apicver;
>> +       u8 mpc_cpuflag;
>> +       u32 mpc_cpusignature;
>> +       u32 mpc_cpufeature;
>> +       u32 mpc_reserved[2];
>> +};
>> +
>> +#define BUSTYPE_CBUS   "CBUS  "
>> +#define BUSTYPE_CBUSII "CBUSII"
>> +#define BUSTYPE_EISA   "EISA  "
>> +#define BUSTYPE_FUTURE "FUTURE"
>> +#define BUSTYPE_INTERN "INTERN"
>> +#define BUSTYPE_ISA    "ISA   "
>> +#define BUSTYPE_MBI    "MBI   "
>> +#define BUSTYPE_MBII   "MBII  "
>> +#define BUSTYPE_MCA    "MCA   "
>> +#define BUSTYPE_MPI    "MPI   "
>> +#define BUSTYPE_MPSA   "MPSA  "
>> +#define BUSTYPE_NUBUS  "NUBUS "
>> +#define BUSTYPE_PCI    "PCI   "
>> +#define BUSTYPE_PCMCIA "PCMCIA"
>> +#define BUSTYPE_TC     "TC    "
>> +#define BUSTYPE_VL     "VL    "
>> +#define BUSTYPE_VME    "VME   "
>> +#define BUSTYPE_XPRESS "XPRESS"
>> +
>> +struct mpc_config_bus {
>> +       u8 mpc_type;
>> +       u8 mpc_busid;
>> +       u8 mpc_bustype[6];
>> +};
>> +
>> +#define MPC_APIC_USABLE        (1 << 0)
>> +
>> +struct mpc_config_ioapic {
>> +       u8 mpc_type;
>> +       u8 mpc_apicid;
>> +       u8 mpc_apicver;
>> +       u8 mpc_flags;
>> +       u32 mpc_apicaddr;
>> +};
>> +
>> +enum mp_irq_source_types {
>> +       MP_INT,
>> +       MP_NMI,
>> +       MP_SMI,
>> +       MP_EXTINT
>> +};
>> +
>> +#define MP_IRQ_POLARITY_DEFAULT        0x0
>> +#define MP_IRQ_POLARITY_HIGH   0x1
>> +#define MP_IRQ_POLARITY_LOW    0x3
>> +#define MP_IRQ_POLARITY_MASK   0x3
>> +#define MP_IRQ_TRIGGER_DEFAULT 0x0
>> +#define MP_IRQ_TRIGGER_EDGE    0x4
>> +#define MP_IRQ_TRIGGER_LEVEL   0xc
>> +#define MP_IRQ_TRIGGER_MASK    0xc
>> +
>> +#define MP_APIC_ALL            0xff
>> +
>> +struct mpc_config_intsrc {
>> +       u8 mpc_type;
>> +       u8 mpc_irqtype;
>> +       u16 mpc_irqflag;
>> +       u8 mpc_srcbus;
>> +       u8 mpc_srcbusirq;
>> +       u8 mpc_dstapic;
>> +       u8 mpc_dstirq;
>> +};
>> +
>> +struct mpc_config_lintsrc {
>> +       u8 mpc_type;
>> +       u8 mpc_irqtype;
>> +       u16 mpc_irqflag;
>> +       u8 mpc_srcbusid;
>> +       u8 mpc_srcbusirq;
>> +       u8 mpc_destapic;
>> +       u8 mpc_destlint;
>> +};
>> +
>> +/* Extended MP configuration table entry types */
>> +
>> +enum mp_ext_config_entry_type {
>> +       MPE_SYSTEM_ADDRESS_SPACE = 128,
>> +       MPE_BUS_HIERARCHY,
>> +       MPE_COMPAT_ADDRESS_SPACE
>> +};
>> +
>> +struct mp_ext_config {
>> +       u8 mpe_type;
>> +       u8 mpe_length;
>> +};
>> +
>> +#define ADDRESS_TYPE_IO                0
>> +#define ADDRESS_TYPE_MEM       1
>> +#define ADDRESS_TYPE_PREFETCH  2
>> +
>> +struct mp_ext_system_address_space {
>> +       u8 mpe_type;
>> +       u8 mpe_length;
>> +       u8 mpe_busid;
>> +       u8 mpe_addr_type;
>> +       u32 mpe_addr_base_low;
>> +       u32 mpe_addr_base_high;
>> +       u32 mpe_addr_length_low;
>> +       u32 mpe_addr_length_high;
>> +};
>> +
>> +#define BUS_SUBTRACTIVE_DECODE (1 << 0)
>> +
>> +struct mp_ext_bus_hierarchy {
>> +       u8 mpe_type;
>> +       u8 mpe_length;
>> +       u8 mpe_busid;
>> +       u8 mpe_bus_info;
>> +       u8 mpe_parent_busid;
>> +       u8 reserved[3];
>> +};
>> +
>> +#define ADDRESS_RANGE_ADD      0
>> +#define ADDRESS_RANGE_SUBTRACT 1
>> +
>> +/*
>> + * X100 - X3FF
>> + * X500 - X7FF
>> + * X900 - XBFF
>> + * XD00 - XFFF
>> + */
>> +#define RANGE_LIST_IO_ISA      0
>> +/*
>> + * X3B0 - X3BB
>> + * X3C0 - X3DF
>> + * X7B0 - X7BB
>> + * X7C0 - X7DF
>> + * XBB0 - XBBB
>> + * XBC0 - XBDF
>> + * XFB0 - XFBB
>> + * XFC0 - XCDF
>> + */
>> +#define RANGE_LIST_IO_VGA      1
>> +
>> +struct mp_ext_compat_address_space {
>> +       u8 mpe_type;
>> +       u8 mpe_length;
>> +       u8 mpe_busid;
>> +       u8 mpe_addr_modifier;
>> +       u32 mpe_range_list;
>> +};
>> +
>> +/**
>> + * mp_next_mpc_entry() - Compute MP configuration table end to be used as
>> + *                       next base table entry start address
>> + *
>> + * This computes the end address of current MP configuration table, without
>> + * counting any extended configuration table entry.
>> + *
>> + * @mc:                configuration table header address
>> + * @return:    configuration table end address
>> + */
>> +static inline u32 mp_next_mpc_entry(struct mp_config_table *mc)
>> +{
>> +       return (u32)mc + mc->mpc_length;
>> +}
>> +
>> +/**
>> + * mp_add_mpc_entry() - Add a base MP configuration table entry
>> + *
>> + * This adds the base MP configuration table entry size with
>> + * added base table entry length and increases entry count by 1.
>> + *
>> + * @mc:                configuration table header address
>> + * @length:    length of the added table entry
>> + */
>> +static inline void mp_add_mpc_entry(struct mp_config_table *mc, u16 length)
>
> Can we avoid using u16 and u8 in parameters? It creates unnecessary
> masking I think, and looks ugly. unsigned int or uint should work. I
> think u32 is OK if you need it, although I'd even question that.
>

Sure, in v2.

>> +{
>> +       mc->mpc_length += length;
>> +       mc->mpc_entry_count++;
>> +}
>> +
>> +/**
>> + * mp_next_mpe_entry() - Compute MP configuration table end to be used as
>> + *                       next extended table entry start address
>> + *
>> + * This computes the end address of current MP configuration table,
>> + * including any extended configuration table entry.
>> + *
>> + * @mc:                configuration table header address
>> + * @return:    configuration table end address
>> + */
>> +static inline u32 mp_next_mpe_entry(struct mp_config_table *mc)
>> +{
>> +       return (u32)mc + mc->mpc_length + mc->mpe_length;
>> +}
>> +
>> +/**
>> + * mp_add_mpe_entry() - Add an extended MP configuration table entry
>> + *
>> + * This adds the extended MP configuration table entry size with
>> + * added extended table entry length.
>> + *
>> + * @mc:                configuration table header address
>> + * @mpe:       extended table entry base address
>> + */
>> +static inline void mp_add_mpe_entry(struct mp_config_table *mc,
>> +                                   struct mp_ext_config *mpe)
>> +{
>> +       mc->mpe_length += mpe->mpe_length;
>> +}
>> +
>> +/**
>> + * mp_write_floating_table() - Write the MP floating table
>> + *
>> + * This writes the MP floating table, and points MP configuration table
>> + * to its end address so that MP configuration table follows immediately
>> + * after the floating table.
>> + *
>> + * @mf:                MP floating table base address
>> + * @return:    MP configuration table header address
>> + */
>> +struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf);
>> +
>> +/**
>> + * mp_config_table_init() - Initialize the MP configuration table header
>> + *
>> + * This populates the MP configuration table header with valid bits.
>> + *
>> + * @mc:                MP configuration table header address
>> + */
>> +void mp_config_table_init(struct mp_config_table *mc);
>> +
>> +/**
>> + * mp_write_processor() - Write a processor entry
>> + *
>> + * This writes a processor entry to the configuration table.
>> + *
>> + * @mc:                MP configuration table header address
>> + */
>> +void mp_write_processor(struct mp_config_table *mc);
>> +
>> +/**
>> + * mp_write_bus() - Write a bus entry
>> + *
>> + * This writes a bus entry to the configuration table.
>> + *
>> + * @mc:                MP configuration table header address
>> + * @id:                bus id
>> + * @bustype:   bus type name
>> + */
>> +void mp_write_bus(struct mp_config_table *mc, u8 id, const char *bustype);
>> +
>> +/**
>> + * mp_write_ioapic() - Write an I/O APIC entry
>> + *
>> + * This writes an I/O APIC entry to the configuration table.
>> + *
>> + * @mc:                MP configuration table header address
>> + * @id:                I/O APIC id
>> + * @ver:       I/O APIC version
>> + * @apicaddr:  I/O APIC address
>> + */
>> +void mp_write_ioapic(struct mp_config_table *mc, u8 id, u8 ver, u32 apicaddr);
>> +
>> +/**
>> + * mp_write_intsrc() - Write an I/O interrupt assignment entry
>> + *
>> + * This writes an I/O interrupt assignment entry to the configuration table.
>> + *
>> + * @mc:                MP configuration table header address
>> + * @irqtype:   IRQ type (INT/NMI/SMI/ExtINT)
>> + * @irqflag:   IRQ flag (level/trigger)
>> + * @srcbus:    source bus id where the interrupt comes from
>> + * @srcbusirq: IRQ number mapped on the source bus
>> + * @dstapic:   destination I/O APIC id where the interrupt goes to
>> + * @dstirq:    destination I/O APIC pin where the interrupt goes to
>> + */
>> +void mp_write_intsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
>> +                    u8 srcbus, u8 srcbusirq, u8 dstapic, u8 dstirq);
>> +
>> +/**
>> + * mp_write_pci_intsrc() - Write a PCI interrupt assignment entry
>> + *
>> + * This writes a PCI interrupt assignment entry to the configuration table.
>> + *
>> + * @mc:                MP configuration table header address
>> + * @irqtype:   IRQ type (INT/NMI/SMI/ExtINT)
>> + * @srcbus:    PCI bus number where the interrupt comes from
>> + * @dev:       device number on the PCI bus
>> + * @pin:       PCI interrupt pin (INT A/B/C/D)
>> + * @dstapic:   destination I/O APIC id where the interrupt goes to
>> + * @dstirq:    destination I/O APIC pin where the interrupt goes to
>> + */
>> +void mp_write_pci_intsrc(struct mp_config_table *mc, u8 irqtype,
>> +                        u8 srcbus, u8 dev, u8 pin, u8 dstapic, u8 dstirq);
>> +
>> +/**
>> + * mp_write_lintsrc() - Write a local interrupt assignment entry
>> + *
>> + * This writes a local interrupt assignment entry to the configuration table.
>> + *
>> + * @mc:                MP configuration table header address
>> + * @irqtype:   IRQ type (INT/NMI/SMI/ExtINT)
>> + * @irqflag:   IRQ flag (level/trigger)
>> + * @srcbus:    PCI bus number where the interrupt comes from
>> + * @srcbusirq: IRQ number mapped on the source bus
>> + * @dstapic:   destination local APIC id where the interrupt goes to
>> + * @destlint:  destination local APIC pin where the interrupt goes to
>> + */
>> +void mp_write_lintsrc(struct mp_config_table *mc, u8 irqtype, u16 irqflag,
>> +                     u8 srcbus, u8 srcbusirq, u8 destapic, u8 destlint);
>> +
>> +
>> +/**
>> + * mp_write_address_space() - Write a system address space entry
>> + *
>> + * This writes a system address space entry to the configuration table.
>> + *
>> + * @mc:                        MP configuration table header address
>> + * @busid:             bus id for the bus where system address space is mapped
>> + * @addr_type:         system address type
>> + * @addr_base_low:     starting address low
>> + * @addr_base_high:    starting address high
>> + * @addr_length_low:   address length low
>> + * @addr_length_high:  address length high
>> + */
>> +void mp_write_address_space(struct mp_config_table *mc,
>> +                           u8 busid, u8 addr_type,
>> +                           u32 addr_base_low, u32 addr_base_high,
>> +                           u32 addr_length_low, u32 addr_length_high);
>> +
>> +/**
>> + * mp_write_bus_hierarchy() - Write a bus hierarchy descriptor entry
>> + *
>> + * This writes a bus hierarchy descriptor entry to the configuration table.
>> + *
>> + * @mc:                        MP configuration table header address
>> + * @busid:             bus id
>> + * @bus_info:          bit0 indicates if the bus is a subtractive decode bus
>> + * @parent_busid:      parent bus id
>> + */
>> +void mp_write_bus_hierarchy(struct mp_config_table *mc,
>> +                           u8 busid, u8 bus_info, u8 parent_busid);
>> +
>> +/**
>> + * mp_write_compat_address_space() - Write a compat bus address space entry
>> + *
>> + * This writes a compatibility bus address space modifier entry to the
>> + * configuration table.
>> + *
>> + * @mc:                        MP configuration table header address
>> + * @busid:             bus id
>> + * @addr_modifier:     add or subtract to predefined address range list
>> + * @range_list:                list of predefined address space ranges
>> + */
>> +void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
>> +                                  u8 addr_modifier, u32 range_list);
>> +
>> +/**
>> + * mptable_finalize() - Finalize the MP table
>> + *
>> + * This finalizes the MP table by calculating required checksums.
>> + *
>> + * @mc:                MP configuration table header address
>> + * @return:    MP table end address
>> + */
>> +u32 mptable_finalize(struct mp_config_table *mc);
>> +
>> +#endif /* __ASM_MPSPEC_H */
>> diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
>> index 70ad19b..43489fd 100644
>> --- a/arch/x86/lib/Makefile
>> +++ b/arch/x86/lib/Makefile
>> @@ -15,6 +15,7 @@ obj-y += gcc.o
>>  obj-y  += init_helpers.o
>>  obj-y  += interrupts.o
>>  obj-y  += lpc-uclass.o
>> +obj-y  += mpspec.o
>>  obj-y += cmd_mtrr.o
>>  obj-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o
>>  obj-$(CONFIG_SYS_PCAT_TIMER) += pcat_timer.o
>> diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
>> new file mode 100644
>> index 0000000..657df22
>> --- /dev/null
>> +++ b/arch/x86/lib/mpspec.c
>> @@ -0,0 +1,238 @@
>> +/*
>> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
>> + *
>> + * Adapted from coreboot src/arch/x86/boot/mpspec.c
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#include <common.h>
>> +#include <cpu.h>
>> +#include <dm.h>
>> +#include <asm/cpu.h>
>> +#include <asm/ioapic.h>
>> +#include <asm/lapic.h>
>> +#include <asm/mpspec.h>
>> +#include <asm/tables.h>
>> +#include <dm/uclass-internal.h>
>> +
>> +struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
>> +{
>> +       u32 mc;
>> +
>> +       memcpy(mf->mpf_signature, MPF_SIGNATURE, 4);
>> +       mf->mpf_physptr = (u32)mf + sizeof(struct mp_floating_table);
>> +       mf->mpf_length = 1;
>> +       mf->mpf_spec = MPSPEC_V14;
>> +       mf->mpf_checksum = 0;
>> +       /* We don't use the default configuration table */
>> +       mf->mpf_feature1 = 0;
>> +       /* Indicate that virtual wire mode is always implemented */
>> +       mf->mpf_feature2 = 0;
>> +       mf->mpf_feature3 = 0;
>> +       mf->mpf_feature4 = 0;
>> +       mf->mpf_feature5 = 0;
>> +       mf->mpf_checksum = table_compute_checksum(mf, mf->mpf_length * 16);
>> +
>> +       mc = (u32)mf + sizeof(struct mp_floating_table);
>> +       return (struct mp_config_table *)mc;
>> +}
>> +
>> +void mp_config_table_init(struct mp_config_table *mc)
>> +{
>> +       int start, len;
>> +       int i;
>> +
>> +       memcpy(mc->mpc_signature, MPC_SIGNATURE, 4);
>> +       mc->mpc_length = sizeof(struct mp_config_table);
>> +       mc->mpc_spec = MPSPEC_V14;
>> +       mc->mpc_checksum = 0;
>> +       mc->mpc_oemptr = 0;
>> +       mc->mpc_oemsize = 0;
>> +       mc->mpc_entry_count = 0;
>> +       mc->mpc_lapic = LAPIC_DEFAULT_BASE;
>> +       mc->mpe_length = 0;
>> +       mc->mpe_checksum = 0;
>> +       mc->reserved = 0;
>> +
>> +       /* The oem/product id fields are exactly 8/12 bytes long */
>> +       strncpy(mc->mpc_oem, CONFIG_SYS_VENDOR, 8);
>> +       strncpy(mc->mpc_product, CONFIG_SYS_BOARD, 12);
>> +
>> +       /* Fill the remaining bytes with spaces */
>> +       len = strlen(CONFIG_SYS_VENDOR);
>> +       start = len < 8 ? len : 8;
>> +       for (i = start; i < 8; i++)
>> +               mc->mpc_oem[i] = ' ';
>
> Can we create a strncpy that pads with spaces?
>

Sure, in v2.

[snip]

Regards,
Bin

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 09/10] x86: Generate a valid MultiProcessor (MP) table
  2015-06-16  2:46   ` Simon Glass
@ 2015-06-17  7:49     ` Bin Meng
  2015-06-17 13:15       ` Simon Glass
  0 siblings, 1 reply; 26+ messages in thread
From: Bin Meng @ 2015-06-17  7:49 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
> Hi Bin,
>
> On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
>> Implement a weak write_mp_table() to create a minimal working MP
>> table. This includes an MP floating table, a configuration table
>> header and all of the 5 base configuration table entries. The I/O
>> interrupt assignment table entry is created based on the same
>> information used in the creation of PIRQ routing table from device
>> tree. A check duplicated entry logic is applied to prevent writing
>> multiple I/O interrupt entries with the same information.
>>
>> Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
>> need actually write the MP table at the F seg, just like we did for
>> PIRQ routing and SFI tables. With MP table existence, linux kernel
>> will switch to I/O APIC and local APIC to process all the peripheral
>> interrupts instead of 8259 PICs. This takes full advantage of the
>> multicore hardware and the SMP kernel.
>>
>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>> ---
>>
>>  arch/x86/Kconfig              |   8 +++
>>  arch/x86/include/asm/mpspec.h |  10 +++
>>  arch/x86/lib/mpspec.c         | 147 ++++++++++++++++++++++++++++++++++++++++++
>>  arch/x86/lib/tables.c         |   5 ++
>>  4 files changed, 170 insertions(+)
>>
>> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
>> index 1aeae9d..d4b2772 100644
>> --- a/arch/x86/Kconfig
>> +++ b/arch/x86/Kconfig
>> @@ -319,6 +319,14 @@ config GENERATE_SFI_TABLE
>>
>>           For more information, see http://simplefirmware.org
>>
>> +config GENERATE_MP_TABLE
>> +       bool "Generate an MP (Multi-Processor) table"
>> +       help
>> +         Generate an MP (Multi-Processor) table for this board. The MP table
>> +         provides a way for the operating system to support for symmetric
>> +         multiprocessing as well as symmetric I/O interrupt handling with
>> +         the local APIC and I/O APIC.
>> +
>>  endmenu
>>
>>  config MAX_PIRQ_LINKS
>> diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
>> index c489a58..e9e1a2a 100644
>> --- a/arch/x86/include/asm/mpspec.h
>> +++ b/arch/x86/include/asm/mpspec.h
>> @@ -431,4 +431,14 @@ void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
>>   */
>>  u32 mptable_finalize(struct mp_config_table *mc);
>>
>> +/**
>> + * write_mp_table() - Write MP table
>> + *
>> + * This writes MP table at a given address.
>> + *
>> + * @addr:      start address to write MP table
>> + * @return:    end address of MP table
>> + */
>> +u32 write_mp_table(u32 addr);
>> +
>>  #endif /* __ASM_MPSPEC_H */
>> diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
>> index 657df22..1e3523f 100644
>> --- a/arch/x86/lib/mpspec.c
>> +++ b/arch/x86/lib/mpspec.c
>> @@ -9,13 +9,17 @@
>>  #include <common.h>
>>  #include <cpu.h>
>>  #include <dm.h>
>> +#include <fdtdec.h>
>>  #include <asm/cpu.h>
>> +#include <asm/irq.h>
>>  #include <asm/ioapic.h>
>>  #include <asm/lapic.h>
>>  #include <asm/mpspec.h>
>>  #include <asm/tables.h>
>>  #include <dm/uclass-internal.h>
>>
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>>  struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
>>  {
>>         u32 mc;
>> @@ -236,3 +240,146 @@ u32 mptable_finalize(struct mp_config_table *mc)
>>
>>         return end;
>>  }
>> +
>> +static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
>> +                                      u32 apicid, int external_int2)
>> +{
>> +       int i;
>> +
>> +       mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
>> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                       bus_isa, 0, apicid, 0);
>> +       mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                       bus_isa, 1, apicid, 1);
>> +       mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
>> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                       bus_isa, 0, apicid, 2);
>> +
>> +       for (i = 3; i < 16; i++)
>> +               mp_write_intsrc(mc, MP_INT,
>> +                               MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                               bus_isa, i, apicid, i);
>> +}
>> +
>> +static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
>> +                           int entry_num, u8 bus, u8 device, u8 pin)
>
> Again can we avoid u8 on function parameters?

Yes, in v2.

> Also this one coudl use a comment.
>

OK.

>> +{
>> +       struct mpc_config_intsrc *intsrc = intsrc_base;
>> +       int i;
>> +
>> +       for (i = 0; i < entry_num; i++) {
>> +               if (intsrc->mpc_srcbus == bus &&
>> +                   intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
>> +                       break;
>> +               intsrc++;
>> +       }
>> +
>> +       return (i == entry_num) ? false : true;
>> +}
>> +
>> +static void mptable_add_intsrc(struct mp_config_table *mc,
>> +                              int bus_isa, u32 apicid)
>> +{
>> +       struct mpc_config_intsrc *intsrc_base;
>> +       int intsrc_entries = 0;
>> +       const void *blob = gd->fdt_blob;
>> +       int node;
>> +       int len, count;
>> +       const u32 *cell;
>> +       int i;
>> +
>> +       /* Legacy Interrupts */
>> +       debug("Writing ISA IRQs\n");
>> +       mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
>> +
>> +       /* Get I/O interrupt information from device tree */
>> +       node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
>> +       if (node < 0) {
>> +               debug("%s: Cannot find irq router node\n", __func__);
>> +               return;
>
> return -ENOENT
>
> (and it's caller should check for error)
>

OK

>> +       }
>> +
>> +       cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
>> +       if (!cell)
>> +               return;
>> +
>> +       if ((len % sizeof(struct pirq_routing)) == 0)
>> +               count = len / sizeof(struct pirq_routing);
>> +       else
>> +               return;
>> +
>> +       intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
>> +
>> +       for (i = 0; i < count; i++) {
>> +               struct pirq_routing pr;
>> +
>> +               pr.bdf = fdt_addr_to_cpu(cell[0]);
>> +               pr.pin = fdt_addr_to_cpu(cell[1]);
>> +               pr.pirq = fdt_addr_to_cpu(cell[2]);
>> +
>> +               if (check_dup_entry(intsrc_base, intsrc_entries,
>> +                                   PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
>> +                       debug("found entry for bus %d device %d INT%c, skipping\n",
>> +                             PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
>> +                             'A' + pr.pin - 1);
>> +                       cell += sizeof(struct pirq_routing) / sizeof(u32);
>> +                       continue;
>> +               }
>> +
>> +               /* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
>> +               mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
>> +                                   PCI_DEV(pr.bdf), pr.pin, apicid,
>> +                                   pr.pirq + 16);
>> +               intsrc_entries++;
>> +               cell += sizeof(struct pirq_routing) / sizeof(u32);
>> +       }
>> +}
>> +
>> +static void mptable_add_lintsrc(struct mp_config_table *mc, u32 bus_isa)
>> +{
>> +       mp_write_lintsrc(mc, MP_EXTINT,
>> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                        bus_isa, 0, MP_APIC_ALL, 0);
>> +       mp_write_lintsrc(mc, MP_NMI,
>> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                        bus_isa, 0, MP_APIC_ALL, 1);
>> +}
>> +
>> +__weak u32 write_mp_table(u32 addr)
>
> Why does this need to be weak?
>

I wanted to leave an option to have board-specific codes to implement
the writing of mp table by using the APIs provided in this file,
although I think this weak version could probably fit for 90% boards.
We can remove the weak for now, and see how things go?

[snip]

Regards,
Bin

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines
  2015-06-17  7:35     ` Bin Meng
@ 2015-06-17 13:15       ` Simon Glass
  2015-06-17 13:17         ` Bin Meng
  0 siblings, 1 reply; 26+ messages in thread
From: Simon Glass @ 2015-06-17 13:15 UTC (permalink / raw)
  To: u-boot

Hi Bin,

On 17 June 2015 at 01:35, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Bin,
>>
>> On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
>>> I/O APIC registers are addressed indirectly. Add io_apic_read() and
>>> io_apic_write() inline routines. Two macros for I/O APIC ID and
>>> version register offset are also added.
>>>
>>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>>> ---
>>>
>>>  arch/x86/include/asm/ioapic.h | 34 ++++++++++++++++++++++++++++++++++
>>>  1 file changed, 34 insertions(+)
>>>
>>> diff --git a/arch/x86/include/asm/ioapic.h b/arch/x86/include/asm/ioapic.h
>>> index f5d69db..697eabb 100644
>>> --- a/arch/x86/include/asm/ioapic.h
>>> +++ b/arch/x86/include/asm/ioapic.h
>>> @@ -9,10 +9,44 @@
>>>  #ifndef __ASM_IOAPIC_H
>>>  #define __ASM_IOAPIC_H
>>>
>>> +#include <asm/io.h>
>>> +
>>>  #define IO_APIC_ADDR           0xfec00000
>>>
>>>  /* Direct addressed register */
>>>  #define IO_APIC_INDEX          (IO_APIC_ADDR + 0x00)
>>>  #define IO_APIC_DATA           (IO_APIC_ADDR + 0x10)
>>>
>>> +/* Indirect addressed register offset */
>>> +#define IO_APIC_ID             0x00
>>> +#define IO_APIC_VER            0x01
>>> +
>>> +/**
>>> + * io_apic_read() - Read I/O APIC register
>>> + *
>>> + * This routine reads I/O APIC indirect addressed register.
>>> + *
>>> + * @reg:       address of indirect addressed register
>>> + * @return:    register value to read
>>> + */
>>> +static inline u32 io_apic_read(u32 reg)
>>> +{
>>> +       writel(reg, IO_APIC_INDEX);
>>> +       return readl(IO_APIC_DATA);
>>> +}
>>
>> Is there any reason these need to be inline?
>>
>
> No, but I was referring to asm/lapic.h inside which the lapic register
> access routines are inline as well. Do you want to remove inline for
> lapic and ioapic?
>
> [snip]

I'm really just wondering why they are inline. I can't see any
advantage myself. If you agree, then yes let's remove inline for both.

If there is a reason for inline, then let's take this patch as is.

Regards,
Simon

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs
  2015-06-17  7:42     ` Bin Meng
@ 2015-06-17 13:15       ` Simon Glass
  2015-06-17 13:19         ` Bin Meng
  0 siblings, 1 reply; 26+ messages in thread
From: Simon Glass @ 2015-06-17 13:15 UTC (permalink / raw)
  To: u-boot

Hi Bin,

On 17 June 2015 at 01:42, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Bin,
>>
>> On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
>>> The MP table provides a way for the operating system to support
>>> for symmetric multiprocessing as well as symmetric I/O interrupt
>>> handling with the local APIC and I/O APIC. We provide a bunch of
>>> APIs for U-Boot to write the floating table, configuration table
>>> header as well as base and extended table entries.
>>>
>>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>>> ---
>>>
>>>  arch/x86/include/asm/mpspec.h | 434 ++++++++++++++++++++++++++++++++++++++++++
>>>  arch/x86/lib/Makefile         |   1 +
>>>  arch/x86/lib/mpspec.c         | 238 +++++++++++++++++++++++
>>>  3 files changed, 673 insertions(+)
>>>  create mode 100644 arch/x86/include/asm/mpspec.h
>>>  create mode 100644 arch/x86/lib/mpspec.c
>>
>> Does this avoid needing SFI?
>>
>
> Yes, SFI is not needed, although it can be turned on and built pass on
> Crown Bay without any problem. I've tested SFI on Crown Bay and the
> only issue is that interrupt is still routed to 8259 instead of I/O
> APIC.

That's great! I think the README.x86 should be updated to indicate
this too (i.e. mention that multi-CPUs are handled by this new feature
or SFI).

[snip]

Regards,
Simon

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 09/10] x86: Generate a valid MultiProcessor (MP) table
  2015-06-17  7:49     ` Bin Meng
@ 2015-06-17 13:15       ` Simon Glass
  0 siblings, 0 replies; 26+ messages in thread
From: Simon Glass @ 2015-06-17 13:15 UTC (permalink / raw)
  To: u-boot

Hi Bin,

On 17 June 2015 at 01:49, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Bin,
>>
>> On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
>>> Implement a weak write_mp_table() to create a minimal working MP
>>> table. This includes an MP floating table, a configuration table
>>> header and all of the 5 base configuration table entries. The I/O
>>> interrupt assignment table entry is created based on the same
>>> information used in the creation of PIRQ routing table from device
>>> tree. A check duplicated entry logic is applied to prevent writing
>>> multiple I/O interrupt entries with the same information.
>>>
>>> Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
>>> need actually write the MP table at the F seg, just like we did for
>>> PIRQ routing and SFI tables. With MP table existence, linux kernel
>>> will switch to I/O APIC and local APIC to process all the peripheral
>>> interrupts instead of 8259 PICs. This takes full advantage of the
>>> multicore hardware and the SMP kernel.
>>>
>>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>>> ---
>>>
>>>  arch/x86/Kconfig              |   8 +++
>>>  arch/x86/include/asm/mpspec.h |  10 +++
>>>  arch/x86/lib/mpspec.c         | 147 ++++++++++++++++++++++++++++++++++++++++++
>>>  arch/x86/lib/tables.c         |   5 ++
>>>  4 files changed, 170 insertions(+)
>>>
>>> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
>>> index 1aeae9d..d4b2772 100644
>>> --- a/arch/x86/Kconfig
>>> +++ b/arch/x86/Kconfig
>>> @@ -319,6 +319,14 @@ config GENERATE_SFI_TABLE
>>>
>>>           For more information, see http://simplefirmware.org
>>>
>>> +config GENERATE_MP_TABLE
>>> +       bool "Generate an MP (Multi-Processor) table"
>>> +       help
>>> +         Generate an MP (Multi-Processor) table for this board. The MP table
>>> +         provides a way for the operating system to support for symmetric
>>> +         multiprocessing as well as symmetric I/O interrupt handling with
>>> +         the local APIC and I/O APIC.
>>> +
>>>  endmenu
>>>
>>>  config MAX_PIRQ_LINKS
>>> diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
>>> index c489a58..e9e1a2a 100644
>>> --- a/arch/x86/include/asm/mpspec.h
>>> +++ b/arch/x86/include/asm/mpspec.h
>>> @@ -431,4 +431,14 @@ void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
>>>   */
>>>  u32 mptable_finalize(struct mp_config_table *mc);
>>>
>>> +/**
>>> + * write_mp_table() - Write MP table
>>> + *
>>> + * This writes MP table at a given address.
>>> + *
>>> + * @addr:      start address to write MP table
>>> + * @return:    end address of MP table
>>> + */
>>> +u32 write_mp_table(u32 addr);
>>> +
>>>  #endif /* __ASM_MPSPEC_H */
>>> diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
>>> index 657df22..1e3523f 100644
>>> --- a/arch/x86/lib/mpspec.c
>>> +++ b/arch/x86/lib/mpspec.c
>>> @@ -9,13 +9,17 @@
>>>  #include <common.h>
>>>  #include <cpu.h>
>>>  #include <dm.h>
>>> +#include <fdtdec.h>
>>>  #include <asm/cpu.h>
>>> +#include <asm/irq.h>
>>>  #include <asm/ioapic.h>
>>>  #include <asm/lapic.h>
>>>  #include <asm/mpspec.h>
>>>  #include <asm/tables.h>
>>>  #include <dm/uclass-internal.h>
>>>
>>> +DECLARE_GLOBAL_DATA_PTR;
>>> +
>>>  struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
>>>  {
>>>         u32 mc;
>>> @@ -236,3 +240,146 @@ u32 mptable_finalize(struct mp_config_table *mc)
>>>
>>>         return end;
>>>  }
>>> +
>>> +static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
>>> +                                      u32 apicid, int external_int2)
>>> +{
>>> +       int i;
>>> +
>>> +       mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
>>> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                       bus_isa, 0, apicid, 0);
>>> +       mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                       bus_isa, 1, apicid, 1);
>>> +       mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
>>> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                       bus_isa, 0, apicid, 2);
>>> +
>>> +       for (i = 3; i < 16; i++)
>>> +               mp_write_intsrc(mc, MP_INT,
>>> +                               MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                               bus_isa, i, apicid, i);
>>> +}
>>> +
>>> +static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
>>> +                           int entry_num, u8 bus, u8 device, u8 pin)
>>
>> Again can we avoid u8 on function parameters?
>
> Yes, in v2.
>
>> Also this one coudl use a comment.
>>
>
> OK.
>
>>> +{
>>> +       struct mpc_config_intsrc *intsrc = intsrc_base;
>>> +       int i;
>>> +
>>> +       for (i = 0; i < entry_num; i++) {
>>> +               if (intsrc->mpc_srcbus == bus &&
>>> +                   intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
>>> +                       break;
>>> +               intsrc++;
>>> +       }
>>> +
>>> +       return (i == entry_num) ? false : true;
>>> +}
>>> +
>>> +static void mptable_add_intsrc(struct mp_config_table *mc,
>>> +                              int bus_isa, u32 apicid)
>>> +{
>>> +       struct mpc_config_intsrc *intsrc_base;
>>> +       int intsrc_entries = 0;
>>> +       const void *blob = gd->fdt_blob;
>>> +       int node;
>>> +       int len, count;
>>> +       const u32 *cell;
>>> +       int i;
>>> +
>>> +       /* Legacy Interrupts */
>>> +       debug("Writing ISA IRQs\n");
>>> +       mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
>>> +
>>> +       /* Get I/O interrupt information from device tree */
>>> +       node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
>>> +       if (node < 0) {
>>> +               debug("%s: Cannot find irq router node\n", __func__);
>>> +               return;
>>
>> return -ENOENT
>>
>> (and it's caller should check for error)
>>
>
> OK
>
>>> +       }
>>> +
>>> +       cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
>>> +       if (!cell)
>>> +               return;
>>> +
>>> +       if ((len % sizeof(struct pirq_routing)) == 0)
>>> +               count = len / sizeof(struct pirq_routing);
>>> +       else
>>> +               return;
>>> +
>>> +       intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
>>> +
>>> +       for (i = 0; i < count; i++) {
>>> +               struct pirq_routing pr;
>>> +
>>> +               pr.bdf = fdt_addr_to_cpu(cell[0]);
>>> +               pr.pin = fdt_addr_to_cpu(cell[1]);
>>> +               pr.pirq = fdt_addr_to_cpu(cell[2]);
>>> +
>>> +               if (check_dup_entry(intsrc_base, intsrc_entries,
>>> +                                   PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
>>> +                       debug("found entry for bus %d device %d INT%c, skipping\n",
>>> +                             PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
>>> +                             'A' + pr.pin - 1);
>>> +                       cell += sizeof(struct pirq_routing) / sizeof(u32);
>>> +                       continue;
>>> +               }
>>> +
>>> +               /* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
>>> +               mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
>>> +                                   PCI_DEV(pr.bdf), pr.pin, apicid,
>>> +                                   pr.pirq + 16);
>>> +               intsrc_entries++;
>>> +               cell += sizeof(struct pirq_routing) / sizeof(u32);
>>> +       }
>>> +}
>>> +
>>> +static void mptable_add_lintsrc(struct mp_config_table *mc, u32 bus_isa)
>>> +{
>>> +       mp_write_lintsrc(mc, MP_EXTINT,
>>> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                        bus_isa, 0, MP_APIC_ALL, 0);
>>> +       mp_write_lintsrc(mc, MP_NMI,
>>> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                        bus_isa, 0, MP_APIC_ALL, 1);
>>> +}
>>> +
>>> +__weak u32 write_mp_table(u32 addr)
>>
>> Why does this need to be weak?
>>
>
> I wanted to leave an option to have board-specific codes to implement
> the writing of mp table by using the APIs provided in this file,
> although I think this weak version could probably fit for 90% boards.
> We can remove the weak for now, and see how things go?

Sounds good - 90% is a pretty good hit rate. We can always expand this
later when needed.

Regards,
Simon

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines
  2015-06-17 13:15       ` Simon Glass
@ 2015-06-17 13:17         ` Bin Meng
  0 siblings, 0 replies; 26+ messages in thread
From: Bin Meng @ 2015-06-17 13:17 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On Wed, Jun 17, 2015 at 9:15 PM, Simon Glass <sjg@chromium.org> wrote:
> Hi Bin,
>
> On 17 June 2015 at 01:35, Bin Meng <bmeng.cn@gmail.com> wrote:
>> Hi Simon,
>>
>> On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
>>> Hi Bin,
>>>
>>> On 15 June 2015 at 01:59, Bin Meng <bmeng.cn@gmail.com> wrote:
>>>> I/O APIC registers are addressed indirectly. Add io_apic_read() and
>>>> io_apic_write() inline routines. Two macros for I/O APIC ID and
>>>> version register offset are also added.
>>>>
>>>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>>>> ---
>>>>
>>>>  arch/x86/include/asm/ioapic.h | 34 ++++++++++++++++++++++++++++++++++
>>>>  1 file changed, 34 insertions(+)
>>>>
>>>> diff --git a/arch/x86/include/asm/ioapic.h b/arch/x86/include/asm/ioapic.h
>>>> index f5d69db..697eabb 100644
>>>> --- a/arch/x86/include/asm/ioapic.h
>>>> +++ b/arch/x86/include/asm/ioapic.h
>>>> @@ -9,10 +9,44 @@
>>>>  #ifndef __ASM_IOAPIC_H
>>>>  #define __ASM_IOAPIC_H
>>>>
>>>> +#include <asm/io.h>
>>>> +
>>>>  #define IO_APIC_ADDR           0xfec00000
>>>>
>>>>  /* Direct addressed register */
>>>>  #define IO_APIC_INDEX          (IO_APIC_ADDR + 0x00)
>>>>  #define IO_APIC_DATA           (IO_APIC_ADDR + 0x10)
>>>>
>>>> +/* Indirect addressed register offset */
>>>> +#define IO_APIC_ID             0x00
>>>> +#define IO_APIC_VER            0x01
>>>> +
>>>> +/**
>>>> + * io_apic_read() - Read I/O APIC register
>>>> + *
>>>> + * This routine reads I/O APIC indirect addressed register.
>>>> + *
>>>> + * @reg:       address of indirect addressed register
>>>> + * @return:    register value to read
>>>> + */
>>>> +static inline u32 io_apic_read(u32 reg)
>>>> +{
>>>> +       writel(reg, IO_APIC_INDEX);
>>>> +       return readl(IO_APIC_DATA);
>>>> +}
>>>
>>> Is there any reason these need to be inline?
>>>
>>
>> No, but I was referring to asm/lapic.h inside which the lapic register
>> access routines are inline as well. Do you want to remove inline for
>> lapic and ioapic?
>>
>> [snip]
>
> I'm really just wondering why they are inline. I can't see any
> advantage myself. If you agree, then yes let's remove inline for both.
>
> If there is a reason for inline, then let's take this patch as is.
>

Agree. Let's remove the inline. I will prepare a v2.

Regards,
Bin

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs
  2015-06-17 13:15       ` Simon Glass
@ 2015-06-17 13:19         ` Bin Meng
  0 siblings, 0 replies; 26+ messages in thread
From: Bin Meng @ 2015-06-17 13:19 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On Wed, Jun 17, 2015 at 9:15 PM, Simon Glass <sjg@chromium.org> wrote:
> Hi Bin,
>
> On 17 June 2015 at 01:42, Bin Meng <bmeng.cn@gmail.com> wrote:
>> Hi Simon,
>>
>> On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
>>> Hi Bin,
>>>
>>> On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
>>>> The MP table provides a way for the operating system to support
>>>> for symmetric multiprocessing as well as symmetric I/O interrupt
>>>> handling with the local APIC and I/O APIC. We provide a bunch of
>>>> APIs for U-Boot to write the floating table, configuration table
>>>> header as well as base and extended table entries.
>>>>
>>>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>>>> ---
>>>>
>>>>  arch/x86/include/asm/mpspec.h | 434 ++++++++++++++++++++++++++++++++++++++++++
>>>>  arch/x86/lib/Makefile         |   1 +
>>>>  arch/x86/lib/mpspec.c         | 238 +++++++++++++++++++++++
>>>>  3 files changed, 673 insertions(+)
>>>>  create mode 100644 arch/x86/include/asm/mpspec.h
>>>>  create mode 100644 arch/x86/lib/mpspec.c
>>>
>>> Does this avoid needing SFI?
>>>
>>
>> Yes, SFI is not needed, although it can be turned on and built pass on
>> Crown Bay without any problem. I've tested SFI on Crown Bay and the
>> only issue is that interrupt is still routed to 8259 instead of I/O
>> APIC.
>
> That's great! I think the README.x86 should be updated to indicate
> this too (i.e. mention that multi-CPUs are handled by this new feature
> or SFI).
>

Sure, will update README.x86 in v2.

> [snip]
>
> Regards,
> Simon

Regards,
Bin

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2015-06-17 13:19 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <1434355202-28295-1-git-send-email-bmeng.cn@gmail.com>
2015-06-15  7:59 ` [U-Boot] [PATCH 02/10] x86: queensbay: Correct Topcliff device irqs Bin Meng
2015-06-16  2:46   ` Simon Glass
2015-06-15  7:59 ` [U-Boot] [PATCH 03/10] x86: Write correct bus number for the irq router Bin Meng
2015-06-16  2:46   ` Simon Glass
2015-06-15  7:59 ` [U-Boot] [PATCH 04/10] x86: Ignore function number when writing PIRQ routing table Bin Meng
2015-06-16  2:46   ` Simon Glass
2015-06-15  7:59 ` [U-Boot] [PATCH 05/10] x86: Reduce PIRQ routing table size Bin Meng
2015-06-16  2:46   ` Simon Glass
2015-06-15  7:59 ` [U-Boot] [PATCH 06/10] x86: Clean up ioapic header file Bin Meng
2015-06-16  2:46   ` Simon Glass
2015-06-15  7:59 ` [U-Boot] [PATCH 07/10] x86: Add I/O APIC register access inline routines Bin Meng
2015-06-16  2:46   ` Simon Glass
2015-06-17  7:35     ` Bin Meng
2015-06-17 13:15       ` Simon Glass
2015-06-17 13:17         ` Bin Meng
2015-06-15  8:00 ` [U-Boot] [PATCH 08/10] x86: Add MultiProcessor (MP) table APIs Bin Meng
2015-06-16  2:46   ` Simon Glass
2015-06-17  7:42     ` Bin Meng
2015-06-17 13:15       ` Simon Glass
2015-06-17 13:19         ` Bin Meng
2015-06-15  8:00 ` [U-Boot] [PATCH 09/10] x86: Generate a valid MultiProcessor (MP) table Bin Meng
2015-06-16  2:46   ` Simon Glass
2015-06-17  7:49     ` Bin Meng
2015-06-17 13:15       ` Simon Glass
2015-06-15  8:00 ` [U-Boot] [PATCH 10/10] x86: crownbay: Enable writing MP table Bin Meng
2015-06-16  2:46   ` Simon Glass

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.