162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP4 specific common source file. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Texas Instruments, Inc. 662306a36Sopenharmony_ci * Author: 762306a36Sopenharmony_ci * Santosh Shilimkar <santosh.shilimkar@ti.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/irq.h> 1462306a36Sopenharmony_ci#include <linux/irqchip.h> 1562306a36Sopenharmony_ci#include <linux/memblock.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_irq.h> 1862306a36Sopenharmony_ci#include <linux/export.h> 1962306a36Sopenharmony_ci#include <linux/irqchip/arm-gic.h> 2062306a36Sopenharmony_ci#include <linux/of_address.h> 2162306a36Sopenharmony_ci#include <linux/reboot.h> 2262306a36Sopenharmony_ci#include <linux/genalloc.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/hardware/cache-l2x0.h> 2562306a36Sopenharmony_ci#include <asm/mach/map.h> 2662306a36Sopenharmony_ci#include <asm/memblock.h> 2762306a36Sopenharmony_ci#include <asm/smp_twd.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "omap-wakeupgen.h" 3062306a36Sopenharmony_ci#include "soc.h" 3162306a36Sopenharmony_ci#include "iomap.h" 3262306a36Sopenharmony_ci#include "common.h" 3362306a36Sopenharmony_ci#include "prminst44xx.h" 3462306a36Sopenharmony_ci#include "prcm_mpu44xx.h" 3562306a36Sopenharmony_ci#include "omap4-sar-layout.h" 3662306a36Sopenharmony_ci#include "omap-secure.h" 3762306a36Sopenharmony_ci#include "sram.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#ifdef CONFIG_CACHE_L2X0 4062306a36Sopenharmony_cistatic void __iomem *l2cache_base; 4162306a36Sopenharmony_ci#endif 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void __iomem *sar_ram_base; 4462306a36Sopenharmony_cistatic void __iomem *gic_dist_base_addr; 4562306a36Sopenharmony_cistatic void __iomem *twd_base; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define IRQ_LOCALTIMER 29 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#ifdef CONFIG_OMAP_INTERCONNECT_BARRIER 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Used to implement memory barrier on DRAM path */ 5262306a36Sopenharmony_ci#define OMAP4_DRAM_BARRIER_VA 0xfe600000 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void __iomem *dram_sync, *sram_sync; 5562306a36Sopenharmony_cistatic phys_addr_t dram_sync_paddr; 5662306a36Sopenharmony_cistatic u32 dram_sync_size; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * The OMAP4 bus structure contains asynchronous bridges which can buffer 6062306a36Sopenharmony_ci * data writes from the MPU. These asynchronous bridges can be found on 6162306a36Sopenharmony_ci * paths between the MPU to EMIF, and the MPU to L3 interconnects. 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * We need to be careful about re-ordering which can happen as a result 6462306a36Sopenharmony_ci * of different accesses being performed via different paths, and 6562306a36Sopenharmony_ci * therefore different asynchronous bridges. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * OMAP4 interconnect barrier which is called for each mb() and wmb(). 7062306a36Sopenharmony_ci * This is to ensure that normal paths to DRAM (normal memory, cacheable 7162306a36Sopenharmony_ci * accesses) are properly synchronised with writes to DMA coherent memory 7262306a36Sopenharmony_ci * (normal memory, uncacheable) and device writes. 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * The mb() and wmb() barriers only operate only on the MPU->MA->EMIF 7562306a36Sopenharmony_ci * path, as we need to ensure that data is visible to other system 7662306a36Sopenharmony_ci * masters prior to writes to those system masters being seen. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * Note: the SRAM path is not synchronised via mb() and wmb(). 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic void omap4_mb(void) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci if (dram_sync) 8362306a36Sopenharmony_ci writel_relaxed(0, dram_sync); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * OMAP4 Errata i688 - asynchronous bridge corruption when entering WFI. 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * If a data is stalled inside asynchronous bridge because of back 9062306a36Sopenharmony_ci * pressure, it may be accepted multiple times, creating pointer 9162306a36Sopenharmony_ci * misalignment that will corrupt next transfers on that data path until 9262306a36Sopenharmony_ci * next reset of the system. No recovery procedure once the issue is hit, 9362306a36Sopenharmony_ci * the path remains consistently broken. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Async bridges can be found on paths between MPU to EMIF and MPU to L3 9662306a36Sopenharmony_ci * interconnects. 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * This situation can happen only when the idle is initiated by a Master 9962306a36Sopenharmony_ci * Request Disconnection (which is trigged by software when executing WFI 10062306a36Sopenharmony_ci * on the CPU). 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * The work-around for this errata needs all the initiators connected 10362306a36Sopenharmony_ci * through an async bridge to ensure that data path is properly drained 10462306a36Sopenharmony_ci * before issuing WFI. This condition will be met if one Strongly ordered 10562306a36Sopenharmony_ci * access is performed to the target right before executing the WFI. 10662306a36Sopenharmony_ci * 10762306a36Sopenharmony_ci * In MPU case, L3 T2ASYNC FIFO and DDR T2ASYNC FIFO needs to be drained. 10862306a36Sopenharmony_ci * IO barrier ensure that there is no synchronisation loss on initiators 10962306a36Sopenharmony_ci * operating on both interconnect port simultaneously. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * This is a stronger version of the OMAP4 memory barrier below, and 11262306a36Sopenharmony_ci * operates on both the MPU->MA->EMIF path but also the MPU->OCP path 11362306a36Sopenharmony_ci * as well, and is necessary prior to executing a WFI. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_civoid omap_interconnect_sync(void) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci if (dram_sync && sram_sync) { 11862306a36Sopenharmony_ci writel_relaxed(readl_relaxed(dram_sync), dram_sync); 11962306a36Sopenharmony_ci writel_relaxed(readl_relaxed(sram_sync), sram_sync); 12062306a36Sopenharmony_ci isb(); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int __init omap4_sram_init(void) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct device_node *np; 12762306a36Sopenharmony_ci struct gen_pool *sram_pool; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!soc_is_omap44xx() && !soc_is_omap54xx()) 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu"); 13362306a36Sopenharmony_ci if (!np) 13462306a36Sopenharmony_ci pr_warn("%s:Unable to allocate sram needed to handle errata I688\n", 13562306a36Sopenharmony_ci __func__); 13662306a36Sopenharmony_ci sram_pool = of_gen_pool_get(np, "sram", 0); 13762306a36Sopenharmony_ci if (!sram_pool) 13862306a36Sopenharmony_ci pr_warn("%s:Unable to get sram pool needed to handle errata I688\n", 13962306a36Sopenharmony_ci __func__); 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci sram_sync = (void __iomem *)gen_pool_alloc(sram_pool, PAGE_SIZE); 14262306a36Sopenharmony_ci of_node_put(np); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciomap_arch_initcall(omap4_sram_init); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* Steal one page physical memory for barrier implementation */ 14962306a36Sopenharmony_civoid __init omap_barrier_reserve_memblock(void) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci dram_sync_size = ALIGN(PAGE_SIZE, SZ_1M); 15262306a36Sopenharmony_ci dram_sync_paddr = arm_memblock_steal(dram_sync_size, SZ_1M); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_civoid __init omap_barriers_init(void) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct map_desc dram_io_desc[1]; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci dram_io_desc[0].virtual = OMAP4_DRAM_BARRIER_VA; 16062306a36Sopenharmony_ci dram_io_desc[0].pfn = __phys_to_pfn(dram_sync_paddr); 16162306a36Sopenharmony_ci dram_io_desc[0].length = dram_sync_size; 16262306a36Sopenharmony_ci dram_io_desc[0].type = MT_MEMORY_RW_SO; 16362306a36Sopenharmony_ci iotable_init(dram_io_desc, ARRAY_SIZE(dram_io_desc)); 16462306a36Sopenharmony_ci dram_sync = (void __iomem *) dram_io_desc[0].virtual; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci pr_info("OMAP4: Map %pa to %p for dram barrier\n", 16762306a36Sopenharmony_ci &dram_sync_paddr, dram_sync); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci soc_mb = omap4_mb; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#endif 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid gic_dist_disable(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci if (gic_dist_base_addr) 17762306a36Sopenharmony_ci writel_relaxed(0x0, gic_dist_base_addr + GIC_DIST_CTRL); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_civoid gic_dist_enable(void) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci if (gic_dist_base_addr) 18362306a36Sopenharmony_ci writel_relaxed(0x1, gic_dist_base_addr + GIC_DIST_CTRL); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cibool gic_dist_disabled(void) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci return !(readl_relaxed(gic_dist_base_addr + GIC_DIST_CTRL) & 0x1); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_civoid gic_timer_retrigger(void) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci u32 twd_int = readl_relaxed(twd_base + TWD_TIMER_INTSTAT); 19462306a36Sopenharmony_ci u32 gic_int = readl_relaxed(gic_dist_base_addr + GIC_DIST_PENDING_SET); 19562306a36Sopenharmony_ci u32 twd_ctrl = readl_relaxed(twd_base + TWD_TIMER_CONTROL); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (twd_int && !(gic_int & BIT(IRQ_LOCALTIMER))) { 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * The local timer interrupt got lost while the distributor was 20062306a36Sopenharmony_ci * disabled. Ack the pending interrupt, and retrigger it. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci pr_warn("%s: lost localtimer interrupt\n", __func__); 20362306a36Sopenharmony_ci writel_relaxed(1, twd_base + TWD_TIMER_INTSTAT); 20462306a36Sopenharmony_ci if (!(twd_ctrl & TWD_TIMER_CONTROL_PERIODIC)) { 20562306a36Sopenharmony_ci writel_relaxed(1, twd_base + TWD_TIMER_COUNTER); 20662306a36Sopenharmony_ci twd_ctrl |= TWD_TIMER_CONTROL_ENABLE; 20762306a36Sopenharmony_ci writel_relaxed(twd_ctrl, twd_base + TWD_TIMER_CONTROL); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci#ifdef CONFIG_CACHE_L2X0 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_civoid __iomem *omap4_get_l2cache_base(void) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci return l2cache_base; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_civoid omap4_l2c310_write_sec(unsigned long val, unsigned reg) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci unsigned smc_op; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci switch (reg) { 22462306a36Sopenharmony_ci case L2X0_CTRL: 22562306a36Sopenharmony_ci smc_op = OMAP4_MON_L2X0_CTRL_INDEX; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci case L2X0_AUX_CTRL: 22962306a36Sopenharmony_ci smc_op = OMAP4_MON_L2X0_AUXCTRL_INDEX; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci case L2X0_DEBUG_CTRL: 23362306a36Sopenharmony_ci smc_op = OMAP4_MON_L2X0_DBG_CTRL_INDEX; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci case L310_PREFETCH_CTRL: 23762306a36Sopenharmony_ci smc_op = OMAP4_MON_L2X0_PREFETCH_INDEX; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci case L310_POWER_CTRL: 24162306a36Sopenharmony_ci pr_info_once("OMAP L2C310: ROM does not support power control setting\n"); 24262306a36Sopenharmony_ci return; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci default: 24562306a36Sopenharmony_ci WARN_ONCE(1, "OMAP L2C310: ignoring write to reg 0x%x\n", reg); 24662306a36Sopenharmony_ci return; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci omap_smc1(smc_op, val); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciint __init omap_l2_cache_init(void) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci /* Static mapping, never released */ 25562306a36Sopenharmony_ci l2cache_base = ioremap(OMAP44XX_L2CACHE_BASE, SZ_4K); 25662306a36Sopenharmony_ci if (WARN_ON(!l2cache_base)) 25762306a36Sopenharmony_ci return -ENOMEM; 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci#endif 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_civoid __iomem *omap4_get_sar_ram_base(void) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci return sar_ram_base; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * SAR RAM used to save and restore the HW context in low power modes. 26962306a36Sopenharmony_ci * Note that we need to initialize this very early for kexec. See 27062306a36Sopenharmony_ci * omap4_mpuss_early_init(). 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_civoid __init omap4_sar_ram_init(void) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci unsigned long sar_base; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* 27762306a36Sopenharmony_ci * To avoid code running on other OMAPs in 27862306a36Sopenharmony_ci * multi-omap builds 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci if (cpu_is_omap44xx()) 28162306a36Sopenharmony_ci sar_base = OMAP44XX_SAR_RAM_BASE; 28262306a36Sopenharmony_ci else if (soc_is_omap54xx()) 28362306a36Sopenharmony_ci sar_base = OMAP54XX_SAR_RAM_BASE; 28462306a36Sopenharmony_ci else 28562306a36Sopenharmony_ci return; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Static mapping, never released */ 28862306a36Sopenharmony_ci sar_ram_base = ioremap(sar_base, SZ_16K); 28962306a36Sopenharmony_ci if (WARN_ON(!sar_ram_base)) 29062306a36Sopenharmony_ci return; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const struct of_device_id intc_match[] = { 29462306a36Sopenharmony_ci { .compatible = "ti,omap4-wugen-mpu", }, 29562306a36Sopenharmony_ci { .compatible = "ti,omap5-wugen-mpu", }, 29662306a36Sopenharmony_ci { }, 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic struct device_node *intc_node; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_civoid __init omap_gic_of_init(void) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct device_node *np; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci intc_node = of_find_matching_node(NULL, intc_match); 30662306a36Sopenharmony_ci if (WARN_ON(!intc_node)) { 30762306a36Sopenharmony_ci pr_err("No WUGEN found in DT, system will misbehave.\n"); 30862306a36Sopenharmony_ci pr_err("UPDATE YOUR DEVICE TREE!\n"); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Extract GIC distributor and TWD bases for OMAP4460 ROM Errata WA */ 31262306a36Sopenharmony_ci if (!cpu_is_omap446x()) 31362306a36Sopenharmony_ci goto skip_errata_init; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic"); 31662306a36Sopenharmony_ci gic_dist_base_addr = of_iomap(np, 0); 31762306a36Sopenharmony_ci of_node_put(np); 31862306a36Sopenharmony_ci WARN_ON(!gic_dist_base_addr); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-twd-timer"); 32162306a36Sopenharmony_ci twd_base = of_iomap(np, 0); 32262306a36Sopenharmony_ci of_node_put(np); 32362306a36Sopenharmony_ci WARN_ON(!twd_base); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciskip_errata_init: 32662306a36Sopenharmony_ci irqchip_init(); 32762306a36Sopenharmony_ci} 328