162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Samsung Exynos Flattened Device Tree enabled machine 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2010-2014 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci// http://www.samsung.com 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/of_fdt.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/irqchip.h> 1562306a36Sopenharmony_ci#include <linux/soc/samsung/exynos-regs-pmu.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/cacheflush.h> 1862306a36Sopenharmony_ci#include <asm/hardware/cache-l2x0.h> 1962306a36Sopenharmony_ci#include <asm/mach/arch.h> 2062306a36Sopenharmony_ci#include <asm/mach/map.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "common.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define S3C_ADDR_BASE 0xF6000000 2562306a36Sopenharmony_ci#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x)) 2662306a36Sopenharmony_ci#define S5P_VA_CHIPID S3C_ADDR(0x02000000) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct platform_device exynos_cpuidle = { 2962306a36Sopenharmony_ci .name = "exynos_cpuidle", 3062306a36Sopenharmony_ci#ifdef CONFIG_ARM_EXYNOS_CPUIDLE 3162306a36Sopenharmony_ci .dev.platform_data = exynos_enter_aftr, 3262306a36Sopenharmony_ci#endif 3362306a36Sopenharmony_ci .id = -1, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_civoid __iomem *sysram_base_addr __ro_after_init; 3762306a36Sopenharmony_ciphys_addr_t sysram_base_phys __ro_after_init; 3862306a36Sopenharmony_civoid __iomem *sysram_ns_base_addr __ro_after_init; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ciunsigned long exynos_cpu_id; 4162306a36Sopenharmony_cistatic unsigned int exynos_cpu_rev; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciunsigned int exynos_rev(void) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci return exynos_cpu_rev; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_civoid __init exynos_sysram_init(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct device_node *node; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") { 5362306a36Sopenharmony_ci struct resource res; 5462306a36Sopenharmony_ci if (!of_device_is_available(node)) 5562306a36Sopenharmony_ci continue; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci of_address_to_resource(node, 0, &res); 5862306a36Sopenharmony_ci sysram_base_addr = ioremap(res.start, resource_size(&res)); 5962306a36Sopenharmony_ci sysram_base_phys = res.start; 6062306a36Sopenharmony_ci of_node_put(node); 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") { 6562306a36Sopenharmony_ci if (!of_device_is_available(node)) 6662306a36Sopenharmony_ci continue; 6762306a36Sopenharmony_ci sysram_ns_base_addr = of_iomap(node, 0); 6862306a36Sopenharmony_ci of_node_put(node); 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int __init exynos_fdt_map_chipid(unsigned long node, const char *uname, 7462306a36Sopenharmony_ci int depth, void *data) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct map_desc iodesc; 7762306a36Sopenharmony_ci const __be32 *reg; 7862306a36Sopenharmony_ci int len; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid")) 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci reg = of_get_flat_dt_prop(node, "reg", &len); 8462306a36Sopenharmony_ci if (reg == NULL || len != (sizeof(unsigned long) * 2)) 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci iodesc.pfn = __phys_to_pfn(be32_to_cpu(reg[0])); 8862306a36Sopenharmony_ci iodesc.length = be32_to_cpu(reg[1]) - 1; 8962306a36Sopenharmony_ci iodesc.virtual = (unsigned long)S5P_VA_CHIPID; 9062306a36Sopenharmony_ci iodesc.type = MT_DEVICE; 9162306a36Sopenharmony_ci iotable_init(&iodesc, 1); 9262306a36Sopenharmony_ci return 1; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void __init exynos_init_io(void) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci debug_ll_io_init(); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci of_scan_flat_dt(exynos_fdt_map_chipid, NULL); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* detect cpu id and rev. */ 10262306a36Sopenharmony_ci exynos_cpu_id = readl_relaxed(S5P_VA_CHIPID); 10362306a36Sopenharmony_ci exynos_cpu_rev = exynos_cpu_id & 0xFF; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci pr_info("Samsung CPU ID: 0x%08lx\n", exynos_cpu_id); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* 11062306a36Sopenharmony_ci * Set or clear the USE_DELAYED_RESET_ASSERTION option. Used by smp code 11162306a36Sopenharmony_ci * and suspend. 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * This is necessary only on Exynos4 SoCs. When system is running 11462306a36Sopenharmony_ci * USE_DELAYED_RESET_ASSERTION should be set so the ARM CLK clock down 11562306a36Sopenharmony_ci * feature could properly detect global idle state when secondary CPU is 11662306a36Sopenharmony_ci * powered down. 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * However this should not be set when such system is going into suspend. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_civoid exynos_set_delayed_reset_assertion(bool enable) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci if (of_machine_is_compatible("samsung,exynos4")) { 12362306a36Sopenharmony_ci unsigned int tmp, core_id; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci for (core_id = 0; core_id < num_possible_cpus(); core_id++) { 12662306a36Sopenharmony_ci tmp = pmu_raw_readl(EXYNOS_ARM_CORE_OPTION(core_id)); 12762306a36Sopenharmony_ci if (enable) 12862306a36Sopenharmony_ci tmp |= S5P_USE_DELAYED_RESET_ASSERTION; 12962306a36Sopenharmony_ci else 13062306a36Sopenharmony_ci tmp &= ~(S5P_USE_DELAYED_RESET_ASSERTION); 13162306a36Sopenharmony_ci pmu_raw_writel(tmp, EXYNOS_ARM_CORE_OPTION(core_id)); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * Apparently, these SoCs are not able to wake-up from suspend using 13862306a36Sopenharmony_ci * the PMU. Too bad. Should they suddenly become capable of such a 13962306a36Sopenharmony_ci * feat, the matches below should be moved to suspend.c. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic const struct of_device_id exynos_dt_pmu_match[] = { 14262306a36Sopenharmony_ci { .compatible = "samsung,exynos5260-pmu" }, 14362306a36Sopenharmony_ci { .compatible = "samsung,exynos5410-pmu" }, 14462306a36Sopenharmony_ci { /*sentinel*/ }, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void exynos_map_pmu(void) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct device_node *np; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci np = of_find_matching_node(NULL, exynos_dt_pmu_match); 15262306a36Sopenharmony_ci if (np) 15362306a36Sopenharmony_ci pmu_base_addr = of_iomap(np, 0); 15462306a36Sopenharmony_ci of_node_put(np); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void __init exynos_init_irq(void) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci irqchip_init(); 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * Since platsmp.c needs pmu base address by the time 16262306a36Sopenharmony_ci * DT is not unflatten so we can't use DT APIs before 16362306a36Sopenharmony_ci * init_irq 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci exynos_map_pmu(); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void __init exynos_dt_machine_init(void) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * This is called from smp_prepare_cpus if we've built for SMP, but 17262306a36Sopenharmony_ci * we still need to set it up for PM and firmware ops if not. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SMP)) 17562306a36Sopenharmony_ci exynos_sysram_init(); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE) 17862306a36Sopenharmony_ci if (of_machine_is_compatible("samsung,exynos4210") || 17962306a36Sopenharmony_ci of_machine_is_compatible("samsung,exynos3250")) 18062306a36Sopenharmony_ci exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data; 18162306a36Sopenharmony_ci#endif 18262306a36Sopenharmony_ci if (of_machine_is_compatible("samsung,exynos4210") || 18362306a36Sopenharmony_ci of_machine_is_compatible("samsung,exynos4212") || 18462306a36Sopenharmony_ci (of_machine_is_compatible("samsung,exynos4412") && 18562306a36Sopenharmony_ci (of_machine_is_compatible("samsung,trats2") || 18662306a36Sopenharmony_ci of_machine_is_compatible("samsung,midas") || 18762306a36Sopenharmony_ci of_machine_is_compatible("samsung,p4note"))) || 18862306a36Sopenharmony_ci of_machine_is_compatible("samsung,exynos3250") || 18962306a36Sopenharmony_ci of_machine_is_compatible("samsung,exynos5250")) 19062306a36Sopenharmony_ci platform_device_register(&exynos_cpuidle); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic char const *const exynos_dt_compat[] __initconst = { 19462306a36Sopenharmony_ci "samsung,exynos3", 19562306a36Sopenharmony_ci "samsung,exynos3250", 19662306a36Sopenharmony_ci "samsung,exynos4", 19762306a36Sopenharmony_ci "samsung,exynos4210", 19862306a36Sopenharmony_ci "samsung,exynos4212", 19962306a36Sopenharmony_ci "samsung,exynos4412", 20062306a36Sopenharmony_ci "samsung,exynos5", 20162306a36Sopenharmony_ci "samsung,exynos5250", 20262306a36Sopenharmony_ci "samsung,exynos5260", 20362306a36Sopenharmony_ci "samsung,exynos5420", 20462306a36Sopenharmony_ci NULL 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void __init exynos_dt_fixup(void) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * Some versions of uboot pass garbage entries in the memory node, 21162306a36Sopenharmony_ci * use the old CONFIG_ARM_NR_BANKS 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci of_fdt_limit_memory(8); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciDT_MACHINE_START(EXYNOS_DT, "Samsung Exynos (Flattened Device Tree)") 21762306a36Sopenharmony_ci .l2c_aux_val = 0x08400000, 21862306a36Sopenharmony_ci .l2c_aux_mask = 0xf60fffff, 21962306a36Sopenharmony_ci .smp = smp_ops(exynos_smp_ops), 22062306a36Sopenharmony_ci .map_io = exynos_init_io, 22162306a36Sopenharmony_ci .init_early = exynos_firmware_init, 22262306a36Sopenharmony_ci .init_irq = exynos_init_irq, 22362306a36Sopenharmony_ci .init_machine = exynos_dt_machine_init, 22462306a36Sopenharmony_ci .init_late = exynos_pm_init, 22562306a36Sopenharmony_ci .dt_compat = exynos_dt_compat, 22662306a36Sopenharmony_ci .dt_fixup = exynos_dt_fixup, 22762306a36Sopenharmony_ciMACHINE_END 228