[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 4/6] xen/arm: platforms: Add Tegra platform to support basic IRQ routing



From: "Chris Patterson" <pattersonc@xxxxxxxxxxxx>

Tegra devices have a legacy interrupt controller (lic, or ictlr) that
must be programmed in parallel with their primary GIC. For all intents
and purposes, we treat these devices attached to this controller as
connected to the primary GIC, as it will be handling their interrupts.

This commit adds support for exposing the ictlr to the hardware domain;
but a future commit will extend this to support exposing a virtualized
version of the ictlr to the hardware domain, and to ensure that
interrupts are unmasked properly when routed to a Xen, or to a domain
other than the hardware domain.

Authored-by: Kyle Temkin <temkink@xxxxxxxxxxxx>
Signed-off-by: Kyle Temkin <temkink@xxxxxxxxxxxx>
Signed-off-by: Chris Patterson <pattersonc@xxxxxxxxxxxx>
---

changes since rfc:
- use bool instead of bool_t
- formatting & code style cleanup
- fix dt compat label (nvidia,tegra120 -> nvidia,tegra124) for K1
- separate mediated legacy interrupt controller into its own module
- split tegra_ictlr_set_interrupt_enable() into
  tegra_lic_set_interrupt_type_normal() and
  tegra_lic_set_interrupt_enable()
- added a couple helper functions to reduce duplicated logic
- added wrapper tegra_lic_readl and writel functions for external use (mlic)
- re-order defines in tegra.h
- cleanup tegra_init() that was previously in patch 6

---

 xen/arch/arm/platforms/Makefile       |   2 +
 xen/arch/arm/platforms/tegra.c        | 313 ++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/platforms/tegra.h |  54 ++++++
 3 files changed, 369 insertions(+)
 create mode 100644 xen/arch/arm/platforms/tegra.c
 create mode 100644 xen/include/asm-arm/platforms/tegra.h

diff --git a/xen/arch/arm/platforms/Makefile b/xen/arch/arm/platforms/Makefile
index 49fa683..d7033d2 100644
--- a/xen/arch/arm/platforms/Makefile
+++ b/xen/arch/arm/platforms/Makefile
@@ -6,5 +6,7 @@ obj-$(CONFIG_ARM_32) += omap5.o
 obj-$(CONFIG_ARM_32) += rcar2.o
 obj-$(CONFIG_ARM_64) += seattle.o
 obj-$(CONFIG_ARM_32) += sunxi.o
+obj-$(CONFIG_ARM_32) += tegra.o
+obj-$(CONFIG_ARM_64) += tegra.o
 obj-$(CONFIG_ARM_64) += xgene-storm.o
 obj-$(CONFIG_ARM_64) += xilinx-zynqmp.o
diff --git a/xen/arch/arm/platforms/tegra.c b/xen/arch/arm/platforms/tegra.c
new file mode 100644
index 0000000..bdd9966
--- /dev/null
+++ b/xen/arch/arm/platforms/tegra.c
@@ -0,0 +1,312 @@
+/*
+ * NVIDIA Tegra specific settings
+ *
+ * Ian Campbell; Copyright (c) 2014 Citrix Systems
+ * Kyle Temkin; Copyright (c) 2016 Assured Information Security, Inc.
+ * Chris Patterson; Copyright (c) 2016 Assured Information Security, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <xen/lib.h>
+#include <xen/stdbool.h>
+#include <xen/sched.h>
+#include <xen/vmap.h>
+
+#include <asm/io.h>
+#include <asm/gic.h>
+#include <asm/platform.h>
+#include <asm/platforms/tegra.h>
+
+/* Permanent mapping to the Tegra legacy interrupt controller. */
+static void __iomem *tegra_ictlr_base;
+
+/*
+ * List of legacy interrupt controllers that can be used to route
+ * Tegra interrupts.
+ */
+static const char * const tegra_interrupt_compat[] __initconst =
+{
+    "nvidia,tegra124-ictlr",  /* Tegra K1 controllers */
+    "nvidia,tegra210-ictlr"   /* Tegra X1 controllers */
+};
+
+/*
+ * Returns true iff the given IRQ belongs to a supported tegra interrupt
+ * controller.
+ */
+static bool tegra_irq_belongs_to_ictlr(const struct dt_raw_irq * rirq)  {
+    int i;
+
+    for ( i = 0; i < ARRAY_SIZE(tegra_interrupt_compat); i++ ) {
+        if ( dt_device_is_compatible(rirq->controller, 
tegra_interrupt_compat[i]) )
+            return true;
+    }
+
+    return false;
+}
+
+/*
+ * Returns true iff the given IRQ is routable -- that is, if it is descended
+ * from the platform's primary GIC.
+ */
+static bool tegra_irq_is_routable(const struct dt_raw_irq * rirq)
+{
+    /* If the IRQ connects directly to our GIC, it's trivially routable. */
+    if ( rirq->controller == dt_interrupt_controller )
+        return true;
+
+    /*
+     * If the IRQ belongs to a legacy interrupt controller, then it's
+     * effectively owned by the GIC, and is routable.
+     */
+    if ( tegra_irq_belongs_to_ictlr(rirq) )
+        return true;
+
+    return false;
+}
+
+/*
+ * Platform-specific reset code for the Tegra devices.
+ * Should not return.
+ */
+static void tegra_reset(void)
+{
+    void __iomem *addr;
+    u32 val;
+
+    addr = ioremap_nocache(TEGRA_RESET_BASE, TEGRA_RESET_SIZE);
+    if ( !addr )
+    {
+        printk(XENLOG_ERR "Tegra: Unable to map tegra reset address. Reset 
failed!\n");
+        return;
+    }
+
+    /* Write into the reset device. */
+    val = readl(addr) | TEGRA_RESET_MASK;
+    writel(val, addr);
+
+    /* Should not get here */
+    iounmap(addr);
+}
+
+/*
+ * Convert irq line to index of servicing legacy interrupt controller.
+ */
+static unsigned int tegra_lic_irq_to_ictlr_index(unsigned int irq)
+{
+    return (irq - NR_LOCAL_IRQS) / TEGRA_IRQS_PER_ICTLR;
+}
+
+/*
+ * Convert irq line to index of irq within servicing interrupt controller.
+ */
+static unsigned int tegra_lic_irq_to_ictlr_irq_index(unsigned int irq)
+{
+    return (irq - NR_LOCAL_IRQS) % TEGRA_IRQS_PER_ICTLR;
+}
+
+/*
+ * Mark interrupt as normal rather than a fast IRQ.
+ */
+static void tegra_lic_set_interrupt_type_normal(unsigned int irq)
+{
+    uint32_t previous_iep_class;
+    unsigned int ictlr_index = tegra_lic_irq_to_ictlr_index(irq);
+    unsigned int ictlr_irq_index = tegra_lic_irq_to_ictlr_irq_index(irq);
+    uint32_t mask = BIT(ictlr_irq_index);
+
+    /* Mark the interrupt as a normal interrupt-- not a fast IRQ. */
+    previous_iep_class = tegra_lic_readl(ictlr_index, 
TEGRA_ICTLR_CPU_IEP_CLASS);
+    tegra_lic_writel(ictlr_index, TEGRA_ICTLR_CPU_IEP_CLASS, 
previous_iep_class & ~mask);
+}
+
+/*
+ * Enable/disable interrupt line for specified irq.
+ */
+static void tegra_lic_set_interrupt_enable(unsigned int irq, bool enabled)
+{
+    unsigned int ictlr_index = tegra_lic_irq_to_ictlr_index(irq);
+    unsigned int ictlr_irq_index = tegra_lic_irq_to_ictlr_irq_index(irq);
+    uint32_t mask = BIT(ictlr_irq_index);
+
+    if ( enabled )
+        tegra_lic_writel(ictlr_index, TEGRA_ICTLR_CPU_IER_SET, mask);
+    else
+        tegra_lic_writel(ictlr_index, TEGRA_ICTLR_CPU_IER_CLR, mask);
+}
+
+/*
+ * Routes an IRQ to a guest, applying sane values to the ictlr masks.
+ * Returns 0 on success, or an error code on failure.
+ */
+static int tegra_route_irq_to_guest(struct domain *d, unsigned int virq,
+                                struct irq_desc *desc, unsigned int priority)
+{
+    /* Program the core GIC to deliver the interrupt to the guest. */
+    int rc = gic_route_irq_to_guest(d, virq, desc, priority);
+
+    /* If we couldn't route the IRQ via the GIC, bail out. */
+    if ( rc )
+    {
+        printk(XENLOG_ERR "Tegra LIC: Couldn't program GIC to route vIRQ %d 
(%d).\n",
+               desc->irq, rc);
+        return rc;
+    }
+
+    /*
+     * If this is a local IRQ, it's not masked by the ictlr, so we
+     * don't need to perform any ictlr manipulation.
+     */
+    if ( desc->irq < NR_LOCAL_IRQS )
+        return rc;
+
+    /*
+     * If this is the hardware domain, it will have real access to the ictlr,
+     * and will program the ictlr itself, so it should start with the ictlr
+     * disabled. If we're not the hwdom, the domain won't interact with the
+     * ictlr, and the interrupt shouldn't be masked.  Either way, first
+     * set the interrupt type to normal (if previously set to fast IRQ).
+     */
+    tegra_lic_set_interrupt_type_normal(desc->irq);
+    tegra_lic_set_interrupt_enable(desc->irq, !is_hardware_domain(d));
+    return rc;
+}
+
+
+/*
+ * Routes an IRQ to Xen. This method both performs the core IRQ routing, and
+ * sets up any ictlr routing necessary.
+ */
+static void tegra_route_irq_to_xen(struct irq_desc *desc, unsigned int 
priority)
+{
+    unsigned int irq = desc->irq;
+
+    /* Program the core GIC to deliver the interrupt to Xen. */
+    gic_route_irq_to_xen(desc, priority);
+
+    /*
+     * If this is a local IRQ, it's not masked by the ictlr, so we
+     * don't need to perform any ictlr manipulation.
+     */
+    if ( irq < NR_LOCAL_IRQS )
+        return;
+
+    /*
+     * Enable the interrupt in the ictlr. Xen only uses the GIC to
+     * perform masking, so we'll enable the interrupt to prevent ictlr
+     * gating of the interrupt.
+     */
+    tegra_lic_set_interrupt_type_normal(desc->irq);
+    tegra_lic_set_interrupt_enable(desc->irq, true);
+}
+
+/*
+ * Read register from specified legacy interrupt interrupt controller.
+ */
+uint32_t tegra_lic_readl(unsigned int ictlr_index, unsigned int 
register_offset)
+{
+    ASSERT(tegra_ictlr_base);
+    ASSERT(ictlr_index < TEGRA_ICTLR_COUNT);
+    ASSERT(register_offset < TEGRA_ICTLR_SIZE);
+    return readl(tegra_ictlr_base + ictlr_index * TEGRA_ICTLR_SIZE +
+                 register_offset);
+}
+
+/*
+ * Write register for specified legacy interrupt interrupt controller.
+ */
+void tegra_lic_writel(unsigned int ictlr_index, unsigned int register_offset, 
uint32_t value)
+{
+    ASSERT(tegra_ictlr_base);
+    ASSERT(ictlr_index < TEGRA_ICTLR_COUNT);
+    ASSERT(register_offset < TEGRA_ICTLR_SIZE);
+    writel(value, tegra_ictlr_base + ictlr_index * TEGRA_ICTLR_SIZE +
+           register_offset);
+}
+
+/*
+ * Initialize the Tegra legacy interrupt controller, placing each interrupt
+ * into a default state. These defaults ensure that stray interrupts don't
+ * affect Xen.
+ */
+static int tegra_lic_init(void)
+{
+    int i;
+
+    /* Map in the tegra ictlr. */
+    tegra_ictlr_base = ioremap_nocache(TEGRA_ICTLR_BASE,
+                                       TEGRA_ICTLR_SIZE * TEGRA_ICTLR_COUNT);
+
+    if ( !tegra_ictlr_base )
+        panic("Failed to map in the Tegra legacy interrupt controller");
+
+    /* Initialize each of the legacy interrupt controllers. */
+    for ( i = 0; i < TEGRA_ICTLR_COUNT; i++ ) {
+
+        /* Clear the interrupt enables for every interrupt. */
+        tegra_lic_writel(i, TEGRA_ICTLR_CPU_IER_CLR, ~0);
+
+        /*
+         * Mark all of our interrupts as normal ARM interrupts (as opposed
+         * to Fast Interrupts.)
+         */
+        tegra_lic_writel(i, TEGRA_ICTLR_CPU_IEP_CLASS, 0);
+    }
+
+    return 0;
+}
+
+/**
+ *  Startup code for the Tegra.
+ */
+static int tegra_init(void)
+{
+    return tegra_lic_init();
+}
+
+
+static const char * const tegra_dt_compat[] __initconst =
+{
+    "nvidia,tegra120",  /* Tegra K1 */
+    "nvidia,tegra210",  /* Tegra X1 */
+    NULL
+};
+
+static const struct dt_device_match tegra_blacklist_dev[] __initconst =
+{
+    /*
+     * The UARTs share a page which runs the risk of mapping the Xen console
+     * UART to dom0, so don't map any of them.
+     */
+    DT_MATCH_COMPATIBLE("nvidia,tegra20-uart"),
+    { /* sentinel */ },
+};
+
+PLATFORM_START(tegra, "Tegra")
+    .blacklist_dev = tegra_blacklist_dev,
+    .compatible = tegra_dt_compat,
+    .init = tegra_init,
+    .reset = tegra_reset,
+    .irq_is_routable = tegra_irq_is_routable,
+    .route_irq_to_xen = tegra_route_irq_to_xen,
+    .route_irq_to_guest = tegra_route_irq_to_guest,
+PLATFORM_END
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/include/asm-arm/platforms/tegra.h 
b/xen/include/asm-arm/platforms/tegra.h
new file mode 100644
index 0000000..e9cd792
--- /dev/null
+++ b/xen/include/asm-arm/platforms/tegra.h
@@ -0,0 +1,54 @@
+/*
+ * NVIDIA Tegra platform definitions
+ *
+ * Kyle Temkin; Copyright (c) 2016 Assured Information Security, Inc.
+ * Chris Patterson; Copyright (c) 2016 Assured Information Security, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __ASM_ARM_PLATFORMS_TEGRA_H
+#define __ASM_ARM_PLATFORMS_TEGRA_H
+
+#define   TEGRA_ICTLR_BASE            0x60004000
+#define   TEGRA_ICTLR_SIZE            0x00000100
+#define   TEGRA_ICTLR_COUNT           6
+#define   TEGRA_IRQS_PER_ICTLR        32
+
+#define   TEGRA_ICTLR_CPU_IER         0x20
+#define   TEGRA_ICTLR_CPU_IER_SET     0x24
+#define   TEGRA_ICTLR_CPU_IER_CLR     0x28
+#define   TEGRA_ICTLR_CPU_IEP_CLASS   0x2C
+
+#define   TEGRA_ICTLR_COP_IER         0x30
+#define   TEGRA_ICTLR_COP_IER_SET     0x34
+#define   TEGRA_ICTLR_COP_IER_CLR     0x38
+#define   TEGRA_ICTLR_COP_IEP_CLASS   0x3c
+
+#define   TEGRA_RESET_BASE            0x7000e400
+#define   TEGRA_RESET_SIZE            4
+#define   TEGRA_RESET_MASK            0x10
+
+uint32_t tegra_lic_readl(unsigned int ictlr_index, unsigned int 
register_offset);
+void tegra_lic_writel(unsigned int ictlr_index, unsigned int register_offset, 
uint32_t value);
+
+#endif /* __ASM_ARM_PLATFORMS_TEGRA_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
-- 
2.1.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.