x86: Add Intel Tangier support

Add Intel Tangier SoC support.

Intel Tangier SoC is a core part of Intel Merrifield platform. For
example, Intel Edison board is based on such platform.

The patch is based on work done by the following people (in alphabetical
order):
	Aiden Park <aiden.park@intel.com>
	Dukjoon Jeon <dukjoon.jeon@intel.com>
	eric.park <eric.park@intel.com>
	Fabien Chereau <fabien.chereau@intel.com>
	Scott D Phillips <scott.d.phillips@intel.com>
	Sebastien Colleur <sebastienx.colleur@intel.com>
	Steve Sakoman <steve.sakoman@intel.com>
	Vincent Tinelli <vincent.tinelli@intel.com>

Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Signed-off-by: Vincent Tinelli <vincent.tinelli@intel.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile
index e1c84ce..999429e 100644
--- a/arch/x86/cpu/Makefile
+++ b/arch/x86/cpu/Makefile
@@ -34,6 +34,7 @@
 obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/
 obj-$(CONFIG_INTEL_QUARK) += quark/
 obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/
+obj-$(CONFIG_INTEL_TANGIER) += tangier/
 obj-y += lapic.o ioapic.o
 obj-y += irq.o
 ifndef CONFIG_$(SPL_)X86_64
diff --git a/arch/x86/cpu/tangier/Kconfig b/arch/x86/cpu/tangier/Kconfig
new file mode 100644
index 0000000..92d3352
--- /dev/null
+++ b/arch/x86/cpu/tangier/Kconfig
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2017 Intel Corporation
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+config INTEL_TANGIER
+	bool
+	depends on INTEL_MID
+
+config SYS_CAR_ADDR
+	hex
+	default 0x19200000
+
+config SYS_CAR_SIZE
+	hex
+	default 0x4000
+	help
+	  Space in bytes in eSRAM used as Cache-As-RAM (CAR).
+	  Note this size must not exceed eSRAM's total size.
diff --git a/arch/x86/cpu/tangier/Makefile b/arch/x86/cpu/tangier/Makefile
new file mode 100644
index 0000000..d146b3f
--- /dev/null
+++ b/arch/x86/cpu/tangier/Makefile
@@ -0,0 +1,7 @@
+#
+# Copyright (c) 2017 Intel Corporation
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-y += car.o tangier.o sdram.o
diff --git a/arch/x86/cpu/tangier/car.S b/arch/x86/cpu/tangier/car.S
new file mode 100644
index 0000000..6982106
--- /dev/null
+++ b/arch/x86/cpu/tangier/car.S
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2010-2011
+ * Graeme Russ, <graeme.russ@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+.section .text
+
+.globl car_init
+car_init:
+	jmp	car_init_ret
diff --git a/arch/x86/cpu/tangier/sdram.c b/arch/x86/cpu/tangier/sdram.c
new file mode 100644
index 0000000..5743077
--- /dev/null
+++ b/arch/x86/cpu/tangier/sdram.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/e820.h>
+#include <asm/global_data.h>
+#include <asm/sfi.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * SFI tables are part of the first stage bootloader.
+ *
+ * U-Boot finds the System Table by searching 16-byte boundaries between
+ * physical address 0x000E0000 and 0x000FFFFF. U-Boot shall search this region
+ * starting at the low address and shall stop searching when the 1st valid SFI
+ * System Table is found.
+ */
+#define SFI_BASE_ADDR		0x000E0000
+#define SFI_LENGTH		0x00020000
+#define SFI_TABLE_LENGTH	16
+
+static int sfi_table_check(struct sfi_table_header *sbh)
+{
+	char chksum = 0;
+	char *pos = (char *)sbh;
+	u32 i;
+
+	if (sbh->len < SFI_TABLE_LENGTH)
+		return -ENXIO;
+
+	if (sbh->len > SFI_LENGTH)
+		return -ENXIO;
+
+	for (i = 0; i < sbh->len; i++)
+		chksum += *pos++;
+
+	if (chksum)
+		error("sfi: Invalid checksum\n");
+
+	/* Checksum is OK if zero */
+	return chksum ? -EILSEQ : 0;
+}
+
+static int sfi_table_is_type(struct sfi_table_header *sbh, const char *signature)
+{
+	return !strncmp(sbh->sig, signature, SFI_SIGNATURE_SIZE) &&
+	       !sfi_table_check(sbh);
+}
+
+static struct sfi_table_simple *sfi_get_table_by_sig(unsigned long addr,
+						     const char *signature)
+{
+	struct sfi_table_simple *sb;
+	u32 i;
+
+	for (i = 0; i < SFI_LENGTH; i += SFI_TABLE_LENGTH) {
+		sb = (struct sfi_table_simple *)(addr + i);
+		if (sfi_table_is_type(&sb->header, signature))
+			return sb;
+	}
+
+	return NULL;
+}
+
+static struct sfi_table_simple *sfi_search_mmap(void)
+{
+	struct sfi_table_header *sbh;
+	struct sfi_table_simple *sb;
+	u32 sys_entry_cnt;
+	u32 i;
+
+	/* Find SYST table */
+	sb = sfi_get_table_by_sig(SFI_BASE_ADDR, SFI_SIG_SYST);
+	if (!sb) {
+		error("sfi: failed to locate SYST table\n");
+		return NULL;
+	}
+
+	sys_entry_cnt = (sb->header.len - sizeof(*sbh)) / 8;
+
+	/* Search through each SYST entry for MMAP table */
+	for (i = 0; i < sys_entry_cnt; i++) {
+		sbh = (struct sfi_table_header *)(unsigned long)sb->pentry[i];
+
+		if (sfi_table_is_type(sbh, SFI_SIG_MMAP))
+			return (struct sfi_table_simple *)sbh;
+	}
+
+	error("sfi: failed to locate SFI MMAP table\n");
+	return NULL;
+}
+
+#define sfi_for_each_mentry(i, sb, mentry)				\
+	for (i = 0, mentry = (struct sfi_mem_entry *)sb->pentry;	\
+	     i < SFI_GET_NUM_ENTRIES(sb, struct sfi_mem_entry);		\
+	     i++, mentry++)						\
+
+static unsigned sfi_setup_e820(unsigned max_entries, struct e820entry *entries)
+{
+	struct sfi_table_simple *sb;
+	struct sfi_mem_entry *mentry;
+	unsigned long long start, end, size;
+	int type, total = 0;
+	u32 i;
+
+	sb = sfi_search_mmap();
+	if (!sb)
+		return 0;
+
+	sfi_for_each_mentry(i, sb, mentry) {
+		start = mentry->phys_start;
+		size = mentry->pages << 12;
+		end = start + size;
+
+		if (start > end)
+			continue;
+
+		/* translate SFI mmap type to E820 map type */
+		switch (mentry->type) {
+		case SFI_MEM_CONV:
+			type = E820_RAM;
+			break;
+		case SFI_MEM_UNUSABLE:
+		case SFI_RUNTIME_SERVICE_DATA:
+			continue;
+		default:
+			type = E820_RESERVED;
+		}
+
+		if (total == E820MAX)
+			break;
+		entries[total].addr = start;
+		entries[total].size = size;
+		entries[total].type = type;
+
+		total++;
+	}
+
+	return total;
+}
+
+static int sfi_get_bank_size(void)
+{
+	struct sfi_table_simple *sb;
+	struct sfi_mem_entry *mentry;
+	int bank = 0;
+	u32 i;
+
+	sb = sfi_search_mmap();
+	if (!sb)
+		return 0;
+
+	sfi_for_each_mentry(i, sb, mentry) {
+		if (mentry->type != SFI_MEM_CONV)
+			continue;
+
+		gd->bd->bi_dram[bank].start = mentry->phys_start;
+		gd->bd->bi_dram[bank].size = mentry->pages << 12;
+		bank++;
+	}
+
+	return bank;
+}
+
+static phys_size_t sfi_get_ram_size(void)
+{
+	struct sfi_table_simple *sb;
+	struct sfi_mem_entry *mentry;
+	phys_size_t ram = 0;
+	u32 i;
+
+	sb = sfi_search_mmap();
+	if (!sb)
+		return 0;
+
+	sfi_for_each_mentry(i, sb, mentry) {
+		if (mentry->type != SFI_MEM_CONV)
+			continue;
+
+		ram += mentry->pages << 12;
+	}
+
+	debug("sfi: RAM size %llu\n", ram);
+	return ram;
+}
+
+unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
+{
+	return sfi_setup_e820(max_entries, entries);
+}
+
+int dram_init_banksize(void)
+{
+	sfi_get_bank_size();
+	return 0;
+}
+
+int dram_init(void)
+{
+	gd->ram_size = sfi_get_ram_size();
+	return 0;
+}
diff --git a/arch/x86/cpu/tangier/tangier.c b/arch/x86/cpu/tangier/tangier.c
new file mode 100644
index 0000000..20d6c60
--- /dev/null
+++ b/arch/x86/cpu/tangier/tangier.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/scu.h>
+#include <asm/u-boot-x86.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Miscellaneous platform dependent initializations
+ */
+int arch_cpu_init(void)
+{
+	return x86_cpu_init_f();
+}
+
+int checkcpu(void)
+{
+	return 0;
+}
+
+int print_cpuinfo(void)
+{
+	return default_print_cpuinfo();
+}
+
+void reset_cpu(ulong addr)
+{
+	scu_ipc_simple_command(IPCMSG_COLD_RESET, 0);
+}