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

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



On Thu, 6 Apr 2017, Chris Patterson wrote:
> 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;
> +    }

Remove the warning.


> +    /*
> +     * 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)

__init


> +{
> +    return tegra_lic_init();
> +}
> +
> +
> +static const char * const tegra_dt_compat[] __initconst =
> +{
> +    "nvidia,tegra120",  /* Tegra K1 */

This is still tegra120 (not tegra124), is that intended? If so, it is
still missing from arch/arm*/boot/dts. Do you have a pointer?

Also, do we need both tegra_dt_compat and tegra_interrupt_compat? Can we
keep only one?


> +    "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®.