From fd4dd6f788519cfeeb382343919e0e984b1a43ee Mon Sep 17 00:00:00 2001
From: Daniel J Blueman <daniel@numascale.com>
Date: Thu, 9 Apr 2015 19:25:56 +0800
Subject: [PATCH] Add numachip2 support

Signed-off-by: Daniel J Blueman <daniel@numascale.com>
---
 arch/x86/include/asm/numachip/numachip2_csr.h |  79 ++++++++
 arch/x86/kernel/apic/Makefile                 |   1 +
 arch/x86/kernel/apic/apic_numachip.c          |   2 +-
 arch/x86/kernel/apic/apic_numachip2.c         | 271 ++++++++++++++++++++++++++
 4 files changed, 352 insertions(+), 1 deletion(-)
 create mode 100644 arch/x86/include/asm/numachip/numachip2_csr.h
 create mode 100644 arch/x86/kernel/apic/apic_numachip2.c

diff --git a/arch/x86/include/asm/numachip/numachip2_csr.h b/arch/x86/include/asm/numachip/numachip2_csr.h
new file mode 100644
index 0000000..fc2614c
--- /dev/null
+++ b/arch/x86/include/asm/numachip/numachip2_csr.h
@@ -0,0 +1,79 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Numascale NumaConnect-Specific Header file
+ *
+ * Copyright (C) 2011 Numascale AS. All rights reserved.
+ *
+ * Send feedback to <support@numascale.com>
+ *
+ */
+
+#ifndef _ASM_X86_NUMACHIP_NUMACHIP_CSR_H
+#define _ASM_X86_NUMACHIP_NUMACHIP_CSR_H
+
+/* local CSR */
+#define NC2_BASE 0xf0000000
+#define NC2_SIZE 0x1000000
+#define NC2_TIMER (NC2_BASE + 0x0)
+#define NC2_APIC (NC2_BASE + 0x100000)
+
+/* PCI space */
+#define NC2_INFO 0x1090
+#define PCI_MMIO_CONF(bus, dev, fnreg) \
+    (((bus) << 20) | ((dev) << 15) | (fnreg))
+
+#include <asm/io.h>
+
+struct numachip_info {
+	uint8_t partition; // 0 for observer
+	uint16_t fabric_nodes : 12;
+	uint16_t part_start : 12;
+	uint16_t part_nodes : 12;
+	uint8_t ver : 4;
+	uint8_t ht : 3;
+	uint8_t neigh_ht : 3;
+	uint8_t neigh_link : 2;
+	uint8_t neigh_sublink : 1;
+	bool symmetric : 1;
+	bool devices : 1;
+} __attribute__((packed)) __attribute__((aligned(4)));
+
+extern struct numachip_info *numachip_info;
+
+static inline uint32_t read32_early(uint8_t bus, uint8_t dev, uint16_t fnreg)
+{
+	uint32_t val;
+	uint64_t addr;
+	volatile void __iomem *vaddr;
+
+	rdmsrl(MSR_FAM10H_MMIO_CONF_BASE, addr);
+	addr = (addr & ~0x3f) | PCI_MMIO_CONF(bus, dev, fnreg);
+	vaddr = ioremap_nocache(addr, sizeof(val));
+	val = readl(vaddr);
+	iounmap(vaddr);
+
+	return val;
+}
+
+static inline uint64_t numachip2_csr(uint16_t reg)
+{
+	uint64_t base;
+	rdmsrl(MSR_FAM10H_MMIO_CONF_BASE, base);
+	base &= ~0x3f;
+	return base | ((0x18 + numachip_info->ht) << 15) | reg;
+}
+
+static inline void numachip2_write32(uint16_t reg, uint32_t val)
+{
+	writel(val, __va(numachip2_csr(reg)));
+}
+
+static inline uint32_t numachip2_read32(uint16_t reg)
+{
+	return readl(__va(numachip2_csr(reg)));
+}
+
+#endif
diff --git a/arch/x86/kernel/apic/Makefile b/arch/x86/kernel/apic/Makefile
index 8bb12dd..cd0d320 100644
--- a/arch/x86/kernel/apic/Makefile
+++ b/arch/x86/kernel/apic/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SMP)		+= ipi.o
 
 ifeq ($(CONFIG_X86_64),y)
 # APIC probe will depend on the listing order here
+obj-$(CONFIG_X86_NUMACHIP)	+= apic_numachip2.o
 obj-$(CONFIG_X86_NUMACHIP)	+= apic_numachip.o
 obj-$(CONFIG_X86_UV)		+= x2apic_uv_x.o
 obj-$(CONFIG_X86_X2APIC)	+= x2apic_phys.o
diff --git a/arch/x86/kernel/apic/apic_numachip.c b/arch/x86/kernel/apic/apic_numachip.c
index d9c90da..59af43e 100644
--- a/arch/x86/kernel/apic/apic_numachip.c
+++ b/arch/x86/kernel/apic/apic_numachip.c
@@ -191,7 +191,7 @@ static int parse_oemn(struct acpi_table_header *table)
 
 static int __init numachip_system_init(void)
 {
-	if (!numachip_system)
+	if (numachip_system != 1)
 		return 0;
 
 	if (acpi_table_parse("OEMN", parse_oemn))
diff --git a/arch/x86/kernel/apic/apic_numachip2.c b/arch/x86/kernel/apic/apic_numachip2.c
new file mode 100644
index 0000000..db35311
--- /dev/null
+++ b/arch/x86/kernel/apic/apic_numachip2.c
@@ -0,0 +1,271 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Numascale NumaConnect-Specific APIC Code
+ *
+ * Copyright (C) 2011 Numascale AS. All rights reserved.
+ *
+ * Send feedback to <support@numascale.com>
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/hardirq.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/jump_label.h>
+
+#include <asm/smp.h>
+#include <asm/apic.h>
+#include <asm/ipi.h>
+#include <asm/apic_flat_64.h>
+#include <asm/pgtable.h>
+#include <asm/numachip/numachip2_csr.h>
+
+extern int numachip_system;
+static const struct apic apic_numachip2;
+// static void __iomem *csr;
+
+#ifdef TESTING
+static struct static_key symmetric = STATIC_KEY_INIT_TRUE;
+
+/* For regular-geometry systems, elide expensive remote fetch */
+static inline int cpu_apicid(const int cpu)
+{
+	if (static_key_false(&symmetric))
+		return per_cpu(x86_cpu_to_apicid, cpu);
+
+	// FIXME
+}
+#endif
+static unsigned int get_apic_id(unsigned long x)
+{
+	uint64_t mcfg;
+	unsigned int ret;
+
+	rdmsrl(MSR_FAM10H_MMIO_CONF_BASE, mcfg);
+	ret = ((mcfg >> (28 - 8)) & 0xfff00) | (x >> 24);
+	return ret;
+}
+
+static unsigned long set_apic_id(unsigned int id)
+{
+	return id << 24;
+}
+
+static unsigned int read_xapic_id(void)
+{
+	return get_apic_id(apic_read(APIC_ID));
+}
+
+static int numachip2_apic_id_valid(int apicid)
+{
+	/* Trust what bootloader passes in MADT */
+	return 1;
+}
+
+static int numachip2_apic_id_registered(void)
+{
+	return physid_isset(read_xapic_id(), phys_cpu_present_map);
+}
+
+static int numachip2_phys_pkg_id(int initial_apic_id, int index_msb)
+{
+	return initial_apic_id >> index_msb;
+}
+
+static int numachip2_wakeup_secondary(int apicid, unsigned long start_rip)
+{
+	writel((apicid << 12) | (5 << 8), __va(NC2_APIC));
+	writel((apicid << 12) | (6 << 8) | (start_rip >> 12), __va(NC2_APIC));
+
+	atomic_set(&init_deasserted, 1);
+	return 0;
+}
+
+static void numachip2_send_IPI_one(int cpu, int vector)
+{
+	u32 apicid = per_cpu(x86_cpu_to_apicid, cpu);
+
+#ifdef NC2_TEST
+	u64 mcfg;
+	rdmsrl(MSR_FAM10H_MMIO_CONF_BASE, mcfg);
+	/* Use local APIC where SCI ID matches */
+	if ((apicid >> 8) == ((mcfg >> 28) & 0xfff)) {
+		unsigned long flags;
+		local_irq_save(flags);
+		__default_send_IPI_dest_field(apicid & 0xff, vector, APIC_DEST_PHYSICAL);
+		local_irq_restore(flags);
+		return;
+	}
+#endif
+	writel((apicid << 12) | ((vector == NMI_VECTOR ? 4 : 0) << 8) | vector, __va(NC2_APIC));
+}
+
+static void numachip2_send_IPI_mask(const struct cpumask *mask, int vector)
+{
+	unsigned int cpu;
+
+	for_each_cpu(cpu, mask)
+		numachip2_send_IPI_one(cpu, vector);
+}
+
+static void numachip2_send_IPI_mask_allbutself(const struct cpumask *mask,
+						int vector)
+{
+	unsigned int this_cpu = smp_processor_id();
+	unsigned int cpu;
+
+	for_each_cpu(cpu, mask) {
+		if (cpu != this_cpu)
+			numachip2_send_IPI_one(cpu, vector);
+	}
+}
+
+static void numachip2_send_IPI_allbutself(int vector)
+{
+	unsigned int this_cpu = smp_processor_id();
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		if (cpu != this_cpu)
+			numachip2_send_IPI_one(cpu, vector);
+	}
+}
+
+static void numachip2_send_IPI_all(int vector)
+{
+	numachip2_send_IPI_mask(cpu_online_mask, vector);
+}
+
+static void numachip2_send_IPI_self(int vector)
+{
+	apic_write(APIC_SELF_IPI, vector);
+}
+
+static int __init numachip2_probe(void)
+{
+	return apic == &apic_numachip2;
+}
+
+static void fixup_cpu_id(struct cpuinfo_x86 *c, int node)
+{
+	u64 val;
+	u32 nodes = 1;
+
+	this_cpu_write(cpu_llc_id, node);
+
+	/* Account for nodes per socket in multi-core-module processors */
+	if (static_cpu_has_safe(X86_FEATURE_NODEID_MSR)) {
+		rdmsrl(MSR_FAM10H_NODE_ID, val);
+		nodes = ((val >> 3) & 7) + 1;
+	}
+
+	c->phys_proc_id = node / nodes;
+}
+
+static int __init numachip2_system_init(void)
+{
+	unsigned off;
+	uint32_t *infop, id;
+
+	if (numachip_system != 2)
+		return 0;
+
+	numachip_info = kzalloc_node(sizeof(*numachip_info), GFP_KERNEL, 0);
+	BUG_ON(!numachip_info);
+	infop = (uint32_t *)numachip_info;
+
+	// find HT count
+	id = read32_early(0, 0x18, 0x0000);
+	BUG_ON((id & 0xffff) != PCI_VENDOR_ID_AMD);
+	id = (read32_early(0, 0x18, 0x0060) >> 4) & 7;
+	BUG_ON((read32_early(0, 0x18 + id, 0x0000) & 0xffff) != 0x1b47);
+
+	// load info struct
+	for (off = 0; off < sizeof(numachip_info) / sizeof(*infop); off++)
+		infop[off] = read32_early(0, 0x18 + id, NC2_INFO + off * 4);
+
+	pr_err("numachip2: partition=%u fabric_nodes=%u part_start=0x%03x part_nodes=%u ver=%u ht=%u neigh_ht=%u neigh_link=%u neigh_sublink=%u symmetric=%u devices=%u\n",
+		numachip_info->partition, numachip_info->fabric_nodes, numachip_info->part_start,
+		numachip_info->part_nodes, numachip_info->ver, numachip_info->ht, numachip_info->neigh_ht,
+		numachip_info->neigh_link, numachip_info->neigh_sublink, numachip_info->symmetric,
+		numachip_info->devices);
+
+	init_extra_mapping_uc(NC2_BASE, NC2_SIZE);
+//	csr = ioremap_nocache(NC2_BASE, NC2_SIZE);
+	x86_cpuinit.fixup_cpu_id = fixup_cpu_id;
+
+	return 0;
+}
+early_initcall(numachip2_system_init);
+
+static int numachip2_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+	if (strncmp(oem_id, "NUMASC", 6) || strncmp(oem_table_id, "NCONECT2", 8))
+		return 0;
+
+	numachip_system = 2;
+	return 1;
+}
+
+static const struct apic apic_numachip2 __refconst = {
+	.name				= "NumaConnect2 system",
+	.probe				= numachip2_probe,
+	.acpi_madt_oem_check		= numachip2_acpi_madt_oem_check,
+	.apic_id_valid			= numachip2_apic_id_valid,
+	.apic_id_registered		= numachip2_apic_id_registered,
+
+	.irq_delivery_mode		= dest_Fixed,
+	.irq_dest_mode			= 0, /* physical */
+
+	.target_cpus			= online_target_cpus,
+	.disable_esr			= 0,
+	.dest_logical			= 0,
+	.check_apicid_used		= NULL,
+
+	.vector_allocation_domain	= default_vector_allocation_domain,
+	.init_apic_ldr			= flat_init_apic_ldr,
+
+	.ioapic_phys_id_map		= NULL,
+	.setup_apic_routing		= NULL,
+	.cpu_present_to_apicid		= default_cpu_present_to_apicid,
+	.apicid_to_cpu_present		= NULL,
+	.check_phys_apicid_present	= default_check_phys_apicid_present,
+	.phys_pkg_id			= numachip2_phys_pkg_id,
+
+	.get_apic_id			= get_apic_id,
+	.set_apic_id			= set_apic_id,
+	.apic_id_mask			= 0xff << 24,
+
+	.cpu_mask_to_apicid_and		= default_cpu_mask_to_apicid_and,
+
+	.send_IPI_one			= numachip2_send_IPI_one,
+	.send_IPI_mask			= numachip2_send_IPI_mask,
+	.send_IPI_mask_allbutself	= numachip2_send_IPI_mask_allbutself,
+	.send_IPI_allbutself		= numachip2_send_IPI_allbutself,
+	.send_IPI_all			= numachip2_send_IPI_all,
+	.send_IPI_self			= numachip2_send_IPI_self,
+
+	.wakeup_secondary_cpu		= numachip2_wakeup_secondary,
+	.wait_for_init_deassert		= NULL,
+	.inquire_remote_apic		= NULL, /* REMRD not supported */
+
+	.read				= native_apic_mem_read,
+	.write				= native_apic_mem_write,
+	.eoi_write			= native_apic_mem_write,
+	.icr_read			= native_apic_icr_read,
+	.icr_write			= native_apic_icr_write,
+	.wait_icr_idle			= native_apic_wait_icr_idle,
+	.safe_wait_icr_idle		= native_safe_apic_wait_icr_idle,
+};
+apic_driver(apic_numachip2);
-- 
2.1.0

