forked from Minki/linux
7136d457f3
OMAP4/5 has been (ab)using the gic_arch_extn to provide wakeup from suspend, and it makes a lot of sense to convert this code to use stacked domains instead. This patch does just this, updating the DT files to actually reflect what the HW provides. BIG FAT WARNING: because the DTs were so far lying by not exposing the WUGEN HW block, kernels with this patch applied won't have any suspend-resume facility when booted with old DTs, and old kernels with updated DTs won't even boot. On a platform with this patch applied, the system looks like this: root@bacon-fat:~# cat /proc/interrupts CPU0 CPU1 16: 0 0 WUGEN 37 gp_timer 19: 233799 155916 GIC 27 arch_timer 23: 0 0 WUGEN 9 l3-dbg-irq 24: 1 0 WUGEN 10 l3-app-irq 27: 282 0 WUGEN 13 omap-dma-engine 44: 0 0 4ae10000.gpio 13 DMA 294: 0 0 WUGEN 20 gpmc 297: 506 0 WUGEN 56 48070000.i2c 298: 0 0 WUGEN 57 48072000.i2c 299: 0 0 WUGEN 61 48060000.i2c 300: 0 0 WUGEN 62 4807a000.i2c 301: 8 0 WUGEN 60 4807c000.i2c 308: 2439 0 WUGEN 74 OMAP UART2 312: 362 0 WUGEN 83 mmc2 313: 502 0 WUGEN 86 mmc0 314: 13 0 WUGEN 94 mmc1 350: 0 0 PRCM pinctrl, pinctrl 406: 35155709 0 GIC 109 ehci_hcd:usb1 407: 0 0 WUGEN 7 palmas 409: 0 0 WUGEN 119 twl6040 410: 0 0 twl6040 5 twl6040_irq_ready 411: 0 0 twl6040 0 twl6040_irq_th IPI0: 0 1 CPU wakeup interrupts IPI1: 0 0 Timer broadcast interrupts IPI2: 95334 902334 Rescheduling interrupts IPI3: 0 0 Function call interrupts IPI4: 479 648 Single function call interrupts IPI5: 0 0 CPU stop interrupts IPI6: 0 0 IRQ work interrupts IPI7: 0 0 completion interrupts Err: 0 Acked-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Link: https://lkml.kernel.org/r/1426088629-15377-8-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
301 lines
6.7 KiB
C
301 lines
6.7 KiB
C
/*
|
|
* OMAP4 specific common source file.
|
|
*
|
|
* Copyright (C) 2010 Texas Instruments, Inc.
|
|
* Author:
|
|
* Santosh Shilimkar <santosh.shilimkar@ti.com>
|
|
*
|
|
*
|
|
* This program is free software,you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/irqchip.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/export.h>
|
|
#include <linux/irqchip/arm-gic.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/reboot.h>
|
|
#include <linux/genalloc.h>
|
|
|
|
#include <asm/hardware/cache-l2x0.h>
|
|
#include <asm/mach/map.h>
|
|
#include <asm/memblock.h>
|
|
#include <asm/smp_twd.h>
|
|
|
|
#include "omap-wakeupgen.h"
|
|
#include "soc.h"
|
|
#include "iomap.h"
|
|
#include "common.h"
|
|
#include "prminst44xx.h"
|
|
#include "prcm_mpu44xx.h"
|
|
#include "omap4-sar-layout.h"
|
|
#include "omap-secure.h"
|
|
#include "sram.h"
|
|
|
|
#ifdef CONFIG_CACHE_L2X0
|
|
static void __iomem *l2cache_base;
|
|
#endif
|
|
|
|
static void __iomem *sar_ram_base;
|
|
static void __iomem *gic_dist_base_addr;
|
|
static void __iomem *twd_base;
|
|
|
|
#define IRQ_LOCALTIMER 29
|
|
|
|
#ifdef CONFIG_OMAP4_ERRATA_I688
|
|
/* Used to implement memory barrier on DRAM path */
|
|
#define OMAP4_DRAM_BARRIER_VA 0xfe600000
|
|
|
|
void __iomem *dram_sync, *sram_sync;
|
|
|
|
static phys_addr_t paddr;
|
|
static u32 size;
|
|
|
|
void omap_bus_sync(void)
|
|
{
|
|
if (dram_sync && sram_sync) {
|
|
writel_relaxed(readl_relaxed(dram_sync), dram_sync);
|
|
writel_relaxed(readl_relaxed(sram_sync), sram_sync);
|
|
isb();
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(omap_bus_sync);
|
|
|
|
static int __init omap4_sram_init(void)
|
|
{
|
|
struct device_node *np;
|
|
struct gen_pool *sram_pool;
|
|
|
|
np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu");
|
|
if (!np)
|
|
pr_warn("%s:Unable to allocate sram needed to handle errata I688\n",
|
|
__func__);
|
|
sram_pool = of_get_named_gen_pool(np, "sram", 0);
|
|
if (!sram_pool)
|
|
pr_warn("%s:Unable to get sram pool needed to handle errata I688\n",
|
|
__func__);
|
|
else
|
|
sram_sync = (void *)gen_pool_alloc(sram_pool, PAGE_SIZE);
|
|
|
|
return 0;
|
|
}
|
|
omap_arch_initcall(omap4_sram_init);
|
|
|
|
/* Steal one page physical memory for barrier implementation */
|
|
int __init omap_barrier_reserve_memblock(void)
|
|
{
|
|
|
|
size = ALIGN(PAGE_SIZE, SZ_1M);
|
|
paddr = arm_memblock_steal(size, SZ_1M);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void __init omap_barriers_init(void)
|
|
{
|
|
struct map_desc dram_io_desc[1];
|
|
|
|
dram_io_desc[0].virtual = OMAP4_DRAM_BARRIER_VA;
|
|
dram_io_desc[0].pfn = __phys_to_pfn(paddr);
|
|
dram_io_desc[0].length = size;
|
|
dram_io_desc[0].type = MT_MEMORY_RW_SO;
|
|
iotable_init(dram_io_desc, ARRAY_SIZE(dram_io_desc));
|
|
dram_sync = (void __iomem *) dram_io_desc[0].virtual;
|
|
|
|
pr_info("OMAP4: Map 0x%08llx to 0x%08lx for dram barrier\n",
|
|
(long long) paddr, dram_io_desc[0].virtual);
|
|
|
|
}
|
|
#else
|
|
void __init omap_barriers_init(void)
|
|
{}
|
|
#endif
|
|
|
|
void gic_dist_disable(void)
|
|
{
|
|
if (gic_dist_base_addr)
|
|
writel_relaxed(0x0, gic_dist_base_addr + GIC_DIST_CTRL);
|
|
}
|
|
|
|
void gic_dist_enable(void)
|
|
{
|
|
if (gic_dist_base_addr)
|
|
writel_relaxed(0x1, gic_dist_base_addr + GIC_DIST_CTRL);
|
|
}
|
|
|
|
bool gic_dist_disabled(void)
|
|
{
|
|
return !(readl_relaxed(gic_dist_base_addr + GIC_DIST_CTRL) & 0x1);
|
|
}
|
|
|
|
void gic_timer_retrigger(void)
|
|
{
|
|
u32 twd_int = readl_relaxed(twd_base + TWD_TIMER_INTSTAT);
|
|
u32 gic_int = readl_relaxed(gic_dist_base_addr + GIC_DIST_PENDING_SET);
|
|
u32 twd_ctrl = readl_relaxed(twd_base + TWD_TIMER_CONTROL);
|
|
|
|
if (twd_int && !(gic_int & BIT(IRQ_LOCALTIMER))) {
|
|
/*
|
|
* The local timer interrupt got lost while the distributor was
|
|
* disabled. Ack the pending interrupt, and retrigger it.
|
|
*/
|
|
pr_warn("%s: lost localtimer interrupt\n", __func__);
|
|
writel_relaxed(1, twd_base + TWD_TIMER_INTSTAT);
|
|
if (!(twd_ctrl & TWD_TIMER_CONTROL_PERIODIC)) {
|
|
writel_relaxed(1, twd_base + TWD_TIMER_COUNTER);
|
|
twd_ctrl |= TWD_TIMER_CONTROL_ENABLE;
|
|
writel_relaxed(twd_ctrl, twd_base + TWD_TIMER_CONTROL);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_CACHE_L2X0
|
|
|
|
void __iomem *omap4_get_l2cache_base(void)
|
|
{
|
|
return l2cache_base;
|
|
}
|
|
|
|
void omap4_l2c310_write_sec(unsigned long val, unsigned reg)
|
|
{
|
|
unsigned smc_op;
|
|
|
|
switch (reg) {
|
|
case L2X0_CTRL:
|
|
smc_op = OMAP4_MON_L2X0_CTRL_INDEX;
|
|
break;
|
|
|
|
case L2X0_AUX_CTRL:
|
|
smc_op = OMAP4_MON_L2X0_AUXCTRL_INDEX;
|
|
break;
|
|
|
|
case L2X0_DEBUG_CTRL:
|
|
smc_op = OMAP4_MON_L2X0_DBG_CTRL_INDEX;
|
|
break;
|
|
|
|
case L310_PREFETCH_CTRL:
|
|
smc_op = OMAP4_MON_L2X0_PREFETCH_INDEX;
|
|
break;
|
|
|
|
case L310_POWER_CTRL:
|
|
pr_info_once("OMAP L2C310: ROM does not support power control setting\n");
|
|
return;
|
|
|
|
default:
|
|
WARN_ONCE(1, "OMAP L2C310: ignoring write to reg 0x%x\n", reg);
|
|
return;
|
|
}
|
|
|
|
omap_smc1(smc_op, val);
|
|
}
|
|
|
|
int __init omap_l2_cache_init(void)
|
|
{
|
|
/* Static mapping, never released */
|
|
l2cache_base = ioremap(OMAP44XX_L2CACHE_BASE, SZ_4K);
|
|
if (WARN_ON(!l2cache_base))
|
|
return -ENOMEM;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void __iomem *omap4_get_sar_ram_base(void)
|
|
{
|
|
return sar_ram_base;
|
|
}
|
|
|
|
/*
|
|
* SAR RAM used to save and restore the HW
|
|
* context in low power modes
|
|
*/
|
|
static int __init omap4_sar_ram_init(void)
|
|
{
|
|
unsigned long sar_base;
|
|
|
|
/*
|
|
* To avoid code running on other OMAPs in
|
|
* multi-omap builds
|
|
*/
|
|
if (cpu_is_omap44xx())
|
|
sar_base = OMAP44XX_SAR_RAM_BASE;
|
|
else if (soc_is_omap54xx())
|
|
sar_base = OMAP54XX_SAR_RAM_BASE;
|
|
else
|
|
return -ENOMEM;
|
|
|
|
/* Static mapping, never released */
|
|
sar_ram_base = ioremap(sar_base, SZ_16K);
|
|
if (WARN_ON(!sar_ram_base))
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
omap_early_initcall(omap4_sar_ram_init);
|
|
|
|
static const struct of_device_id intc_match[] = {
|
|
{ .compatible = "ti,omap4-wugen-mpu", },
|
|
{ .compatible = "ti,omap5-wugen-mpu", },
|
|
{ },
|
|
};
|
|
|
|
static struct device_node *intc_node;
|
|
|
|
unsigned int omap4_xlate_irq(unsigned int hwirq)
|
|
{
|
|
struct of_phandle_args irq_data;
|
|
unsigned int irq;
|
|
|
|
if (!intc_node)
|
|
intc_node = of_find_matching_node(NULL, intc_match);
|
|
|
|
if (WARN_ON(!intc_node))
|
|
return hwirq;
|
|
|
|
irq_data.np = intc_node;
|
|
irq_data.args_count = 3;
|
|
irq_data.args[0] = 0;
|
|
irq_data.args[1] = hwirq - OMAP44XX_IRQ_GIC_START;
|
|
irq_data.args[2] = IRQ_TYPE_LEVEL_HIGH;
|
|
|
|
irq = irq_create_of_mapping(&irq_data);
|
|
if (WARN_ON(!irq))
|
|
irq = hwirq;
|
|
|
|
return irq;
|
|
}
|
|
|
|
void __init omap_gic_of_init(void)
|
|
{
|
|
struct device_node *np;
|
|
|
|
intc_node = of_find_matching_node(NULL, intc_match);
|
|
if (WARN_ON(!intc_node)) {
|
|
pr_err("No WUGEN found in DT, system will misbehave.\n");
|
|
pr_err("UPDATE YOUR DEVICE TREE!\n");
|
|
}
|
|
|
|
/* Extract GIC distributor and TWD bases for OMAP4460 ROM Errata WA */
|
|
if (!cpu_is_omap446x())
|
|
goto skip_errata_init;
|
|
|
|
np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic");
|
|
gic_dist_base_addr = of_iomap(np, 0);
|
|
WARN_ON(!gic_dist_base_addr);
|
|
|
|
np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-twd-timer");
|
|
twd_base = of_iomap(np, 0);
|
|
WARN_ON(!twd_base);
|
|
|
|
skip_errata_init:
|
|
irqchip_init();
|
|
}
|