diff options
author | Scott Williams <scwilliams@nvidia.com> | 2011-07-12 18:05:12 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:46:54 -0800 |
commit | 108c397e52eed8e8f6fa510cdba20d0c7665de21 (patch) | |
tree | 813f43fda7ad5859e920f6d89eb6f0d71bb1f3bb /arch/arm/mach-tegra/gic.c | |
parent | 27a435eb62ef122692e904cbd2959ac191bc43cc (diff) |
ARM: tegra: Add Tegra GIC extensions
Implement extensions to the standard ARM GIC API for Tegra3 power management.
Change-Id: If8b2ce2b366e48bb5ca82d3de2acab1fd0a81bb9
Signed-off-by: Scott Williams <scwilliams@nvidia.com>
Signed-off-by: Dan Willemsen <dwillemsen@nvidia.com>
Rebase-Id: Rd7527cd57edf054c871f5d04d7e9185643f79843
Diffstat (limited to 'arch/arm/mach-tegra/gic.c')
-rw-r--r-- | arch/arm/mach-tegra/gic.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/gic.c b/arch/arm/mach-tegra/gic.c new file mode 100644 index 000000000000..50cecc4eed64 --- /dev/null +++ b/arch/arm/mach-tegra/gic.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010-2011, NVIDIA Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 <linux/kernel.h> +#include <linux/cpumask.h> /* Required by asm/hardware/gic.h */ +#include <linux/io.h> +#include <linux/irqnr.h> + +#include <asm/hardware/gic.h> + +#include <mach/iomap.h> +#include <mach/irqs.h> + +#include "gic.h" +#include "pm.h" + +#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) +static void __iomem *gic_cpu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100); + +void tegra_gic_cpu_disable(void) +{ + writel(0, gic_cpu_base + GIC_CPU_CTRL); +} + +void tegra_gic_cpu_enable(void) +{ + writel(1, gic_cpu_base + GIC_CPU_CTRL); +} + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + +void tegra_gic_pass_through_disable(void) +{ + u32 val = readl(gic_cpu_base + GIC_CPU_CTRL); + val |= 2; /* enableNS = disable GIC pass through */ + writel(val, gic_cpu_base + GIC_CPU_CTRL); +} + +#endif +#endif + +#if defined(CONFIG_PM_SLEEP) + +int tegra_gic_pending_interrupt(void) +{ + u32 irq = readl(gic_cpu_base + GIC_CPU_HIGHPRI); + irq &= 0x3FF; + + return irq; +} + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + +static void __iomem *gic_dist_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); +static u32 gic_affinity[INT_GIC_NR/4]; + +void tegra_gic_dist_disable(void) +{ + writel(0, gic_dist_base + GIC_DIST_CTRL); +} + +void tegra_gic_dist_enable(void) +{ + writel(1, gic_dist_base + GIC_DIST_CTRL); +} + +void tegra_gic_disable_affinity(void) +{ + unsigned int i; + + BUG_ON(is_lp_cluster()); + + /* The GIC distributor TARGET register is one byte per IRQ. */ + for (i = 32; i < INT_GIC_NR; i += 4) { + /* Save the affinity. */ + gic_affinity[i/4] = __raw_readl(gic_dist_base + + GIC_DIST_TARGET + i); + + /* Force this interrupt to CPU0. */ + __raw_writel(0x01010101, gic_dist_base + GIC_DIST_TARGET + i); + } + + wmb(); +} + +void tegra_gic_restore_affinity(void) +{ + unsigned int i; + + BUG_ON(is_lp_cluster()); + + /* The GIC distributor TARGET register is one byte per IRQ. */ + for (i = 32; i < INT_GIC_NR; i += 4) { +#ifdef CONFIG_BUG + u32 reg = __raw_readl(gic_dist_base + GIC_DIST_TARGET + i); + if (reg & 0xFEFEFEFE) + panic("GIC affinity changed!"); +#endif + /* Restore this interrupt's affinity. */ + __raw_writel(gic_affinity[i/4], gic_dist_base + + GIC_DIST_TARGET + i); + } + + wmb(); +} + +void tegra_gic_affinity_to_cpu0(void) +{ + unsigned int i; + + BUG_ON(is_lp_cluster()); + + for (i = 32; i < INT_GIC_NR; i += 4) + __raw_writel(0x01010101, gic_dist_base + GIC_DIST_TARGET + i); + wmb(); +} +#endif +#endif |