arch/x86: Implement arch specifics for software MSI multi-vector
Which requires Intel VT-D to work, since we don't allocate contiguous
vectors.
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
diff --git a/arch/x86/core/pcie.c b/arch/x86/core/pcie.c
index 14b4879..a2492c1 100644
--- a/arch/x86/core/pcie.c
+++ b/arch/x86/core/pcie.c
@@ -13,6 +13,8 @@
#endif
#ifdef CONFIG_PCIE_MSI
+#include <kernel_arch_func.h>
+#include <device.h>
#include <drivers/pcie/msi.h>
#endif
@@ -155,19 +157,145 @@
#ifdef CONFIG_PCIE_MSI
+#ifdef CONFIG_INTEL_VTD_ICTL
+
+#include <drivers/interrupt_controller/intel_vtd.h>
+
+static const struct device *vtd;
+
+static bool get_vtd(void)
+{
+ if (vtd != NULL) {
+ return true;
+ }
+#define DT_DRV_COMPAT intel_vt_d
+ vtd = device_get_binding(DT_INST_LABEL(0));
+#undef DT_DRV_COMPAT
+
+ return vtd == NULL ? false : true;
+}
+
+#endif /* CONFIG_INTEL_VTD_ICTL */
+
/* these functions are explained in include/drivers/pcie/msi.h */
-uint32_t pcie_msi_map(unsigned int irq)
+uint32_t pcie_msi_map(unsigned int irq,
+ msi_vector_t *vector)
{
+ uint32_t map;
+
ARG_UNUSED(irq);
- return 0xFEE00000U; /* standard delivery to BSP local APIC */
-}
-
-uint16_t pcie_msi_mdr(unsigned int irq)
-{
- unsigned char vector = Z_IRQ_TO_INTERRUPT_VECTOR(irq);
-
- return 0x4000U | vector; /* edge triggered */
-}
-
+#ifdef CONFIG_INTEL_VTD_ICTL
+ if (vector != NULL) {
+ map = vtd_remap_msi(vtd, vectors);
+ } else
#endif
+ {
+ map = 0xFEE00000U; /* standard delivery to BSP local APIC */
+ }
+
+ return map;
+}
+
+uint16_t pcie_msi_mdr(unsigned int irq,
+ msi_vector_t *vector)
+{
+ ARG_UNUSED(vectors);
+
+ if (vector == NULL) {
+ /* edge triggered */
+ return 0x4000U | Z_IRQ_TO_INTERRUPT_VECTOR(irq);
+ }
+
+ /* VT-D requires it to be 0, so let's return 0 by default */
+ return 0;
+}
+
+#ifdef CONFIG_INTEL_VTD_ICTL
+
+#include <arch/x86/msi.h>
+
+static inline uint32_t _read_pcie_irq_data(pcie_bdf_t bdf)
+{
+ uint32_t data;
+
+ data = pcie_conf_read(bdf, PCIE_CONF_INTR);
+
+ pcie_conf_write(bdf, PCIE_CONF_INTR, data | PCIE_CONF_INTR_IRQ_NONE);
+
+ return data;
+}
+
+static inline void _write_pcie_irq_data(pcie_bdf_t bdf, uint32_t data)
+{
+ pcie_conf_write(bdf, PCIE_CONF_INTR, data);
+}
+
+uint8_t arch_pcie_msi_vectors_allocate(unsigned int priority,
+ msi_vector_t *vectors,
+ uint8_t n_vector)
+{
+ if (n_vector > 1) {
+ int prev_vector = -1;
+ int irte;
+ int i;
+
+ if (!get_vtd()) {
+ return 0;
+ }
+
+ irte = vtd_allocate_entries(vtd, n_vector);
+ if (irte < 0) {
+ return 0;
+ }
+
+ for (i = irte; i < (irte + n_vector); i++) {
+ vectors[i].arch.irte = i;
+ vectors[i].arch.remap = true;
+ }
+
+ for (i = 0; i < n_vector; i++) {
+ uint32_t data;
+
+ data = _read_pcie_irq_data(vectors[i].bdf);
+
+ vectors[i].arch.irq = pcie_alloc_irq(vectors[i].bdf);
+
+ _write_pcie_irq_data(vectors[i].bdf, data);
+
+ vectors[i].arch.vector =
+ z_x86_allocate_vector(priority, prev_vector);
+ if (vectors[i].arch.vector < 0) {
+ return 0;
+ }
+
+ prev_vector = vectors[i].arch.vector;
+ }
+ } else {
+ vectors[0].arch.vector = z_x86_allocate_vector(priority, -1);
+ }
+
+ return n_vector;
+}
+
+bool arch_pcie_msi_vector_connect(msi_vector_t *vector,
+ void (*routine)(const void *parameter),
+ const void *parameter,
+ uint32_t flags)
+{
+ if (vector->arch.remap) {
+ if (!get_vtd()) {
+ return false;
+ }
+
+ vtd_remap(vtd, vector);
+ }
+
+ z_x86_irq_connect_on_vector(vector->arch.irq, vector->arch.vector,
+ routine, parameter, flags);
+
+ return true;
+}
+
+#endif /* CONFIG_INTEL_VTD_ICTL */
+#endif /* CONFIG_PCIE_MSI */