162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device Tree support for Armada 370 and XP platforms. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Marvell 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Lior Amsalem <alior@marvell.com> 862306a36Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com> 962306a36Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/of_address.h> 1562306a36Sopenharmony_ci#include <linux/of_fdt.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/clocksource.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/memblock.h> 2062306a36Sopenharmony_ci#include <linux/mbus.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/irqchip.h> 2362306a36Sopenharmony_ci#include <asm/hardware/cache-l2x0.h> 2462306a36Sopenharmony_ci#include <asm/mach/arch.h> 2562306a36Sopenharmony_ci#include <asm/mach/map.h> 2662306a36Sopenharmony_ci#include <asm/mach/time.h> 2762306a36Sopenharmony_ci#include <asm/smp_scu.h> 2862306a36Sopenharmony_ci#include "armada-370-xp.h" 2962306a36Sopenharmony_ci#include "common.h" 3062306a36Sopenharmony_ci#include "coherency.h" 3162306a36Sopenharmony_ci#include "mvebu-soc-id.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void __iomem *scu_base; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Enables the SCU when available. Obviously, this is only useful on 3762306a36Sopenharmony_ci * Cortex-A based SOCs, not on PJ4B based ones. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic void __init mvebu_scu_enable(void) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct device_node *np = 4262306a36Sopenharmony_ci of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 4362306a36Sopenharmony_ci if (np) { 4462306a36Sopenharmony_ci scu_base = of_iomap(np, 0); 4562306a36Sopenharmony_ci scu_enable(scu_base); 4662306a36Sopenharmony_ci of_node_put(np); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_civoid __iomem *mvebu_get_scu_base(void) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return scu_base; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * When returning from suspend, the platform goes through the 5762306a36Sopenharmony_ci * bootloader, which executes its DDR3 training code. This code has 5862306a36Sopenharmony_ci * the unfortunate idea of using the first 10 KB of each DRAM bank to 5962306a36Sopenharmony_ci * exercise the RAM and calculate the optimal timings. Therefore, this 6062306a36Sopenharmony_ci * area of RAM is overwritten, and shouldn't be used by the kernel if 6162306a36Sopenharmony_ci * suspend/resume is supported. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 6562306a36Sopenharmony_ci#define MVEBU_DDR_TRAINING_AREA_SZ (10 * SZ_1K) 6662306a36Sopenharmony_cistatic int __init mvebu_scan_mem(unsigned long node, const char *uname, 6762306a36Sopenharmony_ci int depth, void *data) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci const char *type = of_get_flat_dt_prop(node, "device_type", NULL); 7062306a36Sopenharmony_ci const __be32 *reg, *endp; 7162306a36Sopenharmony_ci int l; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (type == NULL || strcmp(type, "memory")) 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); 7762306a36Sopenharmony_ci if (reg == NULL) 7862306a36Sopenharmony_ci reg = of_get_flat_dt_prop(node, "reg", &l); 7962306a36Sopenharmony_ci if (reg == NULL) 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci endp = reg + (l / sizeof(__be32)); 8362306a36Sopenharmony_ci while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { 8462306a36Sopenharmony_ci u64 base, size; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci base = dt_mem_next_cell(dt_root_addr_cells, ®); 8762306a36Sopenharmony_ci size = dt_mem_next_cell(dt_root_size_cells, ®); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci memblock_reserve(base, MVEBU_DDR_TRAINING_AREA_SZ); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void __init mvebu_memblock_reserve(void) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci of_scan_flat_dt(mvebu_scan_mem, NULL); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci#else 10062306a36Sopenharmony_cistatic void __init mvebu_memblock_reserve(void) {} 10162306a36Sopenharmony_ci#endif 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void __init mvebu_init_irq(void) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci irqchip_init(); 10662306a36Sopenharmony_ci mvebu_scu_enable(); 10762306a36Sopenharmony_ci coherency_init(); 10862306a36Sopenharmony_ci BUG_ON(mvebu_mbus_dt_init(coherency_available())); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void __init i2c_quirk(void) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct device_node *np; 11462306a36Sopenharmony_ci u32 dev, rev; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * Only revisons more recent than A0 support the offload 11862306a36Sopenharmony_ci * mechanism. We can exit only if we are sure that we can 11962306a36Sopenharmony_ci * get the SoC revision and it is more recent than A0. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > MV78XX0_A0_REV) 12262306a36Sopenharmony_ci return; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci for_each_compatible_node(np, NULL, "marvell,mv78230-i2c") { 12562306a36Sopenharmony_ci struct property *new_compat; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci new_compat = kzalloc(sizeof(*new_compat), GFP_KERNEL); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci new_compat->name = kstrdup("compatible", GFP_KERNEL); 13062306a36Sopenharmony_ci new_compat->length = sizeof("marvell,mv78230-a0-i2c"); 13162306a36Sopenharmony_ci new_compat->value = kstrdup("marvell,mv78230-a0-i2c", 13262306a36Sopenharmony_ci GFP_KERNEL); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci of_update_property(np, new_compat); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void __init mvebu_dt_init(void) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci if (of_machine_is_compatible("marvell,armadaxp")) 14162306a36Sopenharmony_ci i2c_quirk(); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void __init armada_370_xp_dt_fixup(void) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci#ifdef CONFIG_SMP 14762306a36Sopenharmony_ci smp_set_ops(smp_ops(armada_xp_smp_ops)); 14862306a36Sopenharmony_ci#endif 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic const char * const armada_370_xp_dt_compat[] __initconst = { 15262306a36Sopenharmony_ci "marvell,armada-370-xp", 15362306a36Sopenharmony_ci NULL, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciDT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)") 15762306a36Sopenharmony_ci .l2c_aux_val = 0, 15862306a36Sopenharmony_ci .l2c_aux_mask = ~0, 15962306a36Sopenharmony_ci .init_machine = mvebu_dt_init, 16062306a36Sopenharmony_ci .init_irq = mvebu_init_irq, 16162306a36Sopenharmony_ci .restart = mvebu_restart, 16262306a36Sopenharmony_ci .reserve = mvebu_memblock_reserve, 16362306a36Sopenharmony_ci .dt_compat = armada_370_xp_dt_compat, 16462306a36Sopenharmony_ci .dt_fixup = armada_370_xp_dt_fixup, 16562306a36Sopenharmony_ciMACHINE_END 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const char * const armada_375_dt_compat[] __initconst = { 16862306a36Sopenharmony_ci "marvell,armada375", 16962306a36Sopenharmony_ci NULL, 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciDT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") 17362306a36Sopenharmony_ci .l2c_aux_val = 0, 17462306a36Sopenharmony_ci .l2c_aux_mask = ~0, 17562306a36Sopenharmony_ci .init_irq = mvebu_init_irq, 17662306a36Sopenharmony_ci .init_machine = mvebu_dt_init, 17762306a36Sopenharmony_ci .restart = mvebu_restart, 17862306a36Sopenharmony_ci .dt_compat = armada_375_dt_compat, 17962306a36Sopenharmony_ciMACHINE_END 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const char * const armada_38x_dt_compat[] __initconst = { 18262306a36Sopenharmony_ci "marvell,armada380", 18362306a36Sopenharmony_ci "marvell,armada385", 18462306a36Sopenharmony_ci NULL, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ciDT_MACHINE_START(ARMADA_38X_DT, "Marvell Armada 380/385 (Device Tree)") 18862306a36Sopenharmony_ci .l2c_aux_val = 0, 18962306a36Sopenharmony_ci .l2c_aux_mask = ~0, 19062306a36Sopenharmony_ci .init_irq = mvebu_init_irq, 19162306a36Sopenharmony_ci .restart = mvebu_restart, 19262306a36Sopenharmony_ci .dt_compat = armada_38x_dt_compat, 19362306a36Sopenharmony_ciMACHINE_END 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic const char * const armada_39x_dt_compat[] __initconst = { 19662306a36Sopenharmony_ci "marvell,armada390", 19762306a36Sopenharmony_ci "marvell,armada398", 19862306a36Sopenharmony_ci NULL, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciDT_MACHINE_START(ARMADA_39X_DT, "Marvell Armada 39x (Device Tree)") 20262306a36Sopenharmony_ci .l2c_aux_val = 0, 20362306a36Sopenharmony_ci .l2c_aux_mask = ~0, 20462306a36Sopenharmony_ci .init_irq = mvebu_init_irq, 20562306a36Sopenharmony_ci .restart = mvebu_restart, 20662306a36Sopenharmony_ci .dt_compat = armada_39x_dt_compat, 20762306a36Sopenharmony_ciMACHINE_END 208