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_ */