Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | // SPDX-License-Identifier: GPL-2.0-only /* * Device Tree support for Armada 370 and XP platforms. * * Copyright (C) 2012 Marvell * * Lior Amsalem <alior@marvell.com> * Gregory CLEMENT <gregory.clement@free-electrons.com> * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/of_address.h> #include <linux/of_fdt.h> #include <linux/io.h> #include <linux/clocksource.h> #include <linux/dma-mapping.h> #include <linux/memblock.h> #include <linux/mbus.h> #include <linux/slab.h> #include <linux/irqchip.h> #include <asm/hardware/cache-l2x0.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #include <asm/mach/time.h> #include <asm/smp_scu.h> #include "armada-370-xp.h" #include "common.h" #include "coherency.h" #include "mvebu-soc-id.h" static void __iomem *scu_base; /* * Enables the SCU when available. Obviously, this is only useful on * Cortex-A based SOCs, not on PJ4B based ones. */ static void __init mvebu_scu_enable(void) { struct device_node *np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); if (np) { scu_base = of_iomap(np, 0); scu_enable(scu_base); of_node_put(np); } } void __iomem *mvebu_get_scu_base(void) { return scu_base; } /* * When returning from suspend, the platform goes through the * bootloader, which executes its DDR3 training code. This code has * the unfortunate idea of using the first 10 KB of each DRAM bank to * exercise the RAM and calculate the optimal timings. Therefore, this * area of RAM is overwritten, and shouldn't be used by the kernel if * suspend/resume is supported. */ #ifdef CONFIG_SUSPEND #define MVEBU_DDR_TRAINING_AREA_SZ (10 * SZ_1K) static int __init mvebu_scan_mem(unsigned long node, const char *uname, int depth, void *data) { const char *type = of_get_flat_dt_prop(node, "device_type", NULL); const __be32 *reg, *endp; int l; if (type == NULL || strcmp(type, "memory")) return 0; reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); if (reg == NULL) reg = of_get_flat_dt_prop(node, "reg", &l); if (reg == NULL) return 0; endp = reg + (l / sizeof(__be32)); while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { u64 base, size; base = dt_mem_next_cell(dt_root_addr_cells, ®); size = dt_mem_next_cell(dt_root_size_cells, ®); memblock_reserve(base, MVEBU_DDR_TRAINING_AREA_SZ); } return 0; } static void __init mvebu_memblock_reserve(void) { of_scan_flat_dt(mvebu_scan_mem, NULL); } #else static void __init mvebu_memblock_reserve(void) {} #endif static void __init mvebu_init_irq(void) { irqchip_init(); mvebu_scu_enable(); coherency_init(); BUG_ON(mvebu_mbus_dt_init(coherency_available())); } static void __init i2c_quirk(void) { struct device_node *np; u32 dev, rev; /* * Only revisons more recent than A0 support the offload * mechanism. We can exit only if we are sure that we can * get the SoC revision and it is more recent than A0. */ if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > MV78XX0_A0_REV) return; for_each_compatible_node(np, NULL, "marvell,mv78230-i2c") { struct property *new_compat; new_compat = kzalloc(sizeof(*new_compat), GFP_KERNEL); new_compat->name = kstrdup("compatible", GFP_KERNEL); new_compat->length = sizeof("marvell,mv78230-a0-i2c"); new_compat->value = kstrdup("marvell,mv78230-a0-i2c", GFP_KERNEL); of_update_property(np, new_compat); } } static void __init mvebu_dt_init(void) { if (of_machine_is_compatible("marvell,armadaxp")) i2c_quirk(); } static void __init armada_370_xp_dt_fixup(void) { #ifdef CONFIG_SMP smp_set_ops(smp_ops(armada_xp_smp_ops)); #endif } static const char * const armada_370_xp_dt_compat[] __initconst = { "marvell,armada-370-xp", NULL, }; DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)") .l2c_aux_val = 0, .l2c_aux_mask = ~0, .init_machine = mvebu_dt_init, .init_irq = mvebu_init_irq, .restart = mvebu_restart, .reserve = mvebu_memblock_reserve, .dt_compat = armada_370_xp_dt_compat, .dt_fixup = armada_370_xp_dt_fixup, MACHINE_END static const char * const armada_375_dt_compat[] __initconst = { "marvell,armada375", NULL, }; DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") .l2c_aux_val = 0, .l2c_aux_mask = ~0, .init_irq = mvebu_init_irq, .init_machine = mvebu_dt_init, .restart = mvebu_restart, .dt_compat = armada_375_dt_compat, MACHINE_END static const char * const armada_38x_dt_compat[] __initconst = { "marvell,armada380", "marvell,armada385", NULL, }; DT_MACHINE_START(ARMADA_38X_DT, "Marvell Armada 380/385 (Device Tree)") .l2c_aux_val = 0, .l2c_aux_mask = ~0, .init_irq = mvebu_init_irq, .restart = mvebu_restart, .dt_compat = armada_38x_dt_compat, MACHINE_END static const char * const armada_39x_dt_compat[] __initconst = { "marvell,armada390", "marvell,armada398", NULL, }; DT_MACHINE_START(ARMADA_39X_DT, "Marvell Armada 39x (Device Tree)") .l2c_aux_val = 0, .l2c_aux_mask = ~0, .init_irq = mvebu_init_irq, .restart = mvebu_restart, .dt_compat = armada_39x_dt_compat, MACHINE_END |