drivers/interrupt_controller: Add Intel VT-D interrupt remapping driver

Such interrupt remapping controller may be found along with Intel VT-D
hardware. Its base-address is via ACPI, and it enables up to 64K
interrupt indexes.

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt
index e8bc013..47f7a67 100644
--- a/drivers/interrupt_controller/CMakeLists.txt
+++ b/drivers/interrupt_controller/CMakeLists.txt
@@ -18,3 +18,4 @@
 zephyr_sources_ifdef(CONFIG_SWERV_PIC               intc_swerv_pic.c)
 zephyr_sources_ifdef(CONFIG_NPCX_MIWU               intc_miwu.c)
 zephyr_sources_ifdef(CONFIG_LEON_IRQMP              intc_irqmp.c)
+zephyr_sources_ifdef(CONFIG_INTEL_VTD_ICTL          intc_intel_vtd.c)
diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig
index 6ab2084..6dc4a6b 100644
--- a/drivers/interrupt_controller/Kconfig
+++ b/drivers/interrupt_controller/Kconfig
@@ -64,4 +64,6 @@
 
 source "drivers/interrupt_controller/Kconfig.npcx"
 
+source "drivers/interrupt_controller/Kconfig.intel_vtd"
+
 endmenu
diff --git a/drivers/interrupt_controller/Kconfig.intel_vtd b/drivers/interrupt_controller/Kconfig.intel_vtd
new file mode 100644
index 0000000..05c5f8a
--- /dev/null
+++ b/drivers/interrupt_controller/Kconfig.intel_vtd
@@ -0,0 +1,25 @@
+# Intel VT-D interrupt remapping controller configuration
+
+# Copyright (c) 2020 Intel Corporation
+# SPDX-License-Identifier: Apache-2.0
+
+menuconfig INTEL_VTD_ICTL
+	bool "Intel VT-D interrupt remapping controller"
+	depends on ACPI && X86 && 64BIT
+	select PCIE_MSI_MULTI_VECTOR
+	help
+	  Such interrupt remapping hardware is provided through Intel VT-D
+	  technology. It's being used, currently, only for MSI/MSI-X
+	  multi-vector support. If you have such PCIe device requiring
+	  multi-vector support, you will need to enable this.
+
+if INTEL_VTD_ICTL
+
+config INTEL_VTD_ICTL_INIT_PRIORITY
+	int "Initialization priority"
+	default 0
+	help
+	  This device should be initialized as soon as possible, before any
+	  other device that would require it for MSI/MSI-X multi-vector support.
+
+endif # INTEL_VTD_ICTL
diff --git a/drivers/interrupt_controller/intc_intel_vtd.c b/drivers/interrupt_controller/intc_intel_vtd.c
new file mode 100644
index 0000000..bbac563
--- /dev/null
+++ b/drivers/interrupt_controller/intc_intel_vtd.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020 Intel Corporation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT intel_vt_d
+
+#include <errno.h>
+
+#include <kernel.h>
+#include <arch/cpu.h>
+
+#include <soc.h>
+#include <device.h>
+#include <init.h>
+#include <string.h>
+
+#include <zephyr.h>
+
+#include <arch/x86/intel_vtd.h>
+#include <drivers/interrupt_controller/intel_vtd.h>
+
+#include "intc_intel_vtd.h"
+
+static void vtd_write_reg64(const struct device *dev,
+			    uint16_t reg, uint64_t value)
+{
+	uintptr_t base_address = DEVICE_MMIO_GET(dev);
+
+	sys_write64(value, (base_address + reg));
+}
+
+static uint32_t vtd_read_reg(const struct device *dev, uint16_t reg)
+{
+	uintptr_t base_address = DEVICE_MMIO_GET(dev);
+
+	return sys_read32(base_address + reg);
+}
+
+static void vtd_send_cmd(const struct device *dev,
+			 uint16_t cmd_bit, uint16_t status_bit)
+{
+	uintptr_t base_address = DEVICE_MMIO_GET(dev);
+
+	sys_set_bit((base_address + VTD_GCMD_REG), cmd_bit);
+
+	while (!sys_test_bit((base_address + VTD_GSTS_REG),
+			     status_bit)) {
+		/* Do nothing */
+	}
+
+}
+
+static int vtd_ictl_allocate_entries(const struct device *dev,
+				     uint8_t n_entries)
+{
+	struct vtd_ictl_data *data = dev->data;
+	int irte_idx_start;
+
+	if ((data->irte_num_used + n_entries) > IRTE_NUM) {
+		return -EBUSY;
+	}
+
+	irte_idx_start = data->irte_num_used;
+	data->irte_num_used += n_entries;
+
+	return irte_idx_start;
+}
+
+static uint32_t vtd_ictl_remap_msi(const struct device *dev,
+				   msi_vector_t *vector)
+{
+	return VTD_MSI_MAP(vector->arch.irte);
+}
+
+static int vtd_ictl_remap(const struct device *dev,
+			  msi_vector_t *vector)
+{
+	struct vtd_ictl_data *data = dev->data;
+	uint8_t irte_idx = vector->arch.irte;
+
+	memset(&data->irte[irte_idx], 0, sizeof(struct vtd_irte));
+
+	data->irte[irte_idx].l.vector = vector->arch.vector;
+	data->irte[irte_idx].l.dst_id = arch_curr_cpu()->id;
+	data->irte[irte_idx].l.present = 1;
+
+	return 0;
+}
+
+static int vtd_ictl_init(const struct device *dev)
+{
+	struct vtd_ictl_data *data = dev->data;
+	unsigned int key = irq_lock();
+	uint64_t eime = 0;
+	uint64_t irta;
+
+	DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
+
+	if (IS_ENABLED(CONFIG_X2APIC)) {
+		eime = VTD_IRTA_EIME;
+	}
+
+	irta = VTD_IRTA_REG_GEN_CONTENT((uintptr_t)data->irte,
+					IRTA_SIZE, eime);
+
+	vtd_write_reg64(dev, VTD_IRTA_REG, irta);
+
+	vtd_send_cmd(dev, VTD_GCMD_SIRTP, VTD_GSTS_SIRTPS);
+	vtd_send_cmd(dev, VTD_GCMD_IRE, VTD_GSTS_IRES);
+
+	printk("Intel VT-D up and running (status 0x%x)\n",
+	       vtd_read_reg(dev, VTD_GSTS_REG));
+
+	irq_unlock(key);
+
+	return 0;
+}
+
+static const struct vtd_driver_api vtd_api = {
+	.allocate_entries = vtd_ictl_allocate_entries,
+	.remap_msi = vtd_ictl_remap_msi,
+	.remap = vtd_ictl_remap,
+};
+
+static struct vtd_ictl_data vtd_ictl_data_0;
+
+static const struct vtd_ictl_cfg vtd_ictl_cfg_0 = {
+	DEVICE_MMIO_ROM_INIT(DT_DRV_INST(0)),
+};
+
+DEVICE_DEFINE(vtd_ictl, DT_INST_LABEL(0),
+	      vtd_ictl_init, device_pm_control_nop,
+	      &vtd_ictl_data_0, &vtd_ictl_cfg_0,
+	      PRE_KERNEL_1, CONFIG_INTEL_VTD_ICTL_INIT_PRIORITY, &vtd_api);
diff --git a/drivers/interrupt_controller/intc_intel_vtd.h b/drivers/interrupt_controller/intc_intel_vtd.h
new file mode 100644
index 0000000..912ec1e
--- /dev/null
+++ b/drivers/interrupt_controller/intc_intel_vtd.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Intel Corporation
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_INTEL_VTD_H_
+#define ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_INTEL_VTD_H_
+
+#define VTD_INT_SHV	BIT(3)
+#define VTD_INT_FORMAT	BIT(4)
+
+/* We don't care about int_idx[15], since the size is fixed to 256,
+ * it's always 0
+ */
+#define VTD_MSI_MAP(int_idx) \
+	((0x0FEE << 20) | (int_idx << 5) | VTD_INT_SHV | VTD_INT_FORMAT)
+
+/* Interrupt Remapping Table Entry (IRTE) for Remapped Interrupts */
+struct vtd_irte {
+	struct {
+		uint64_t present		: 1;
+		uint64_t fpd			: 1;
+		uint64_t dst_mode		: 1;
+		uint64_t redirection_hint	: 1;
+		uint64_t trigger_mode		: 1;
+		uint64_t delivery_mode		: 3;
+		uint64_t available		: 4;
+		uint64_t _reserved_0		: 3;
+		uint64_t irte_mode		: 1;
+		uint64_t vector			: 8;
+		uint64_t _reserved_1		: 8;
+		uint64_t dst_id			: 32;
+	} l;
+
+	struct {
+		uint64_t src_id			: 16;
+		uint64_t src_id_qualifier	: 2;
+		uint64_t src_validation_type	: 2;
+		uint64_t _reserved		: 44;
+	} h;
+} __packed;
+
+/* The table must be 4KB aligned, which is exactly 256 entries.
+ * And since we allow only 256 entries as a maximum: let's align to it.
+ */
+#define IRTE_NUM 256
+#define IRTA_SIZE 7  /* size = 2^(X+1) where IRTA_SIZE is X 2^8 = 256 */
+
+struct vtd_ictl_data {
+	struct vtd_irte irte[IRTE_NUM];
+	int irte_num_used;
+};
+
+struct vtd_ictl_cfg {
+	DEVICE_MMIO_ROM;
+};
+
+#endif /* ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_INTEL_VTD_H_ */