exynos.c 8.3 KB
Newer Older
1
/*
2
 * SAMSUNG EXYNOS Flattened Device Tree enabled machine
3
 *
4 5
 * Copyright (c) 2010-2014 Samsung Electronics Co., Ltd.
 *		http://www.samsung.com
6 7 8 9 10 11
 *
 * 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.
 */

12
#include <linux/init.h>
13
#include <linux/io.h>
14
#include <linux/kernel.h>
15
#include <linux/serial_s3c.h>
16
#include <linux/of.h>
17
#include <linux/of_address.h>
18 19
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
20
#include <linux/platform_device.h>
21
#include <linux/pm_domain.h>
22
#include <linux/irqchip.h>
23

24
#include <asm/cacheflush.h>
25
#include <asm/hardware/cache-l2x0.h>
26
#include <asm/mach/arch.h>
27
#include <asm/mach/map.h>
28
#include <asm/memory.h>
29

30 31
#include <mach/map.h>

32
#include "common.h"
33
#include "mfc.h"
34 35
#include "regs-pmu.h"

36
void __iomem *pmu_base_addr;
37

38 39
static struct map_desc exynos4_iodesc[] __initdata = {
	{
40 41 42 43
		.virtual	= (unsigned long)S5P_VA_SROMC,
		.pfn		= __phys_to_pfn(EXYNOS4_PA_SROMC),
		.length		= SZ_4K,
		.type		= MT_DEVICE,
44
	}, {
45 46 47 48 49 50 51 52 53 54 55 56
		.virtual	= (unsigned long)S5P_VA_CMU,
		.pfn		= __phys_to_pfn(EXYNOS4_PA_CMU),
		.length		= SZ_128K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S5P_VA_COREPERI_BASE,
		.pfn		= __phys_to_pfn(EXYNOS4_PA_COREPERI),
		.length		= SZ_8K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S5P_VA_DMC0,
		.pfn		= __phys_to_pfn(EXYNOS4_PA_DMC0),
57 58 59 60 61 62
		.length		= SZ_64K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S5P_VA_DMC1,
		.pfn		= __phys_to_pfn(EXYNOS4_PA_DMC1),
		.length		= SZ_64K,
63 64 65 66
		.type		= MT_DEVICE,
	},
};

67 68 69 70 71 72 73 74 75 76 77
static struct map_desc exynos5_iodesc[] __initdata = {
	{
		.virtual	= (unsigned long)S5P_VA_SROMC,
		.pfn		= __phys_to_pfn(EXYNOS5_PA_SROMC),
		.length		= SZ_4K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S5P_VA_CMU,
		.pfn		= __phys_to_pfn(EXYNOS5_PA_CMU),
		.length		= 144 * SZ_1K,
		.type		= MT_DEVICE,
78 79 80
	},
};

81
static struct platform_device exynos_cpuidle = {
82
	.name              = "exynos_cpuidle",
83
#ifdef CONFIG_ARM_EXYNOS_CPUIDLE
84
	.dev.platform_data = exynos_enter_aftr,
85
#endif
86
	.id                = -1,
87 88
};

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
void __iomem *sysram_base_addr;
void __iomem *sysram_ns_base_addr;

void __init exynos_sysram_init(void)
{
	struct device_node *node;

	for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") {
		if (!of_device_is_available(node))
			continue;
		sysram_base_addr = of_iomap(node, 0);
		break;
	}

	for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") {
		if (!of_device_is_available(node))
			continue;
		sysram_ns_base_addr = of_iomap(node, 0);
		break;
	}
}

111
static void __init exynos_init_late(void)
112
{
113 114 115 116
	if (of_machine_is_compatible("samsung,exynos5440"))
		/* to be supported later */
		return;

117
	exynos_pm_init();
118 119
}

120
static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
121 122 123
					int depth, void *data)
{
	struct map_desc iodesc;
124
	const __be32 *reg;
125
	int len;
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

	if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid") &&
		!of_flat_dt_is_compatible(node, "samsung,exynos5440-clock"))
		return 0;

	reg = of_get_flat_dt_prop(node, "reg", &len);
	if (reg == NULL || len != (sizeof(unsigned long) * 2))
		return 0;

	iodesc.pfn = __phys_to_pfn(be32_to_cpu(reg[0]));
	iodesc.length = be32_to_cpu(reg[1]) - 1;
	iodesc.virtual = (unsigned long)S5P_VA_CHIPID;
	iodesc.type = MT_DEVICE;
	iotable_init(&iodesc, 1);
	return 1;
}

143 144 145 146 147
/*
 * exynos_map_io
 *
 * register the standard cpu IO areas
 */
148 149
static void __init exynos_map_io(void)
{
150
	if (soc_is_exynos4())
151 152
		iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc));

153
	if (soc_is_exynos5())
154 155
		iotable_init(exynos5_iodesc, ARRAY_SIZE(exynos5_iodesc));
}
156

157
static void __init exynos_init_io(void)
158
{
159 160
	debug_ll_io_init();

161
	of_scan_flat_dt(exynos_fdt_map_chipid, NULL);
162

163 164 165
	/* detect cpu id and rev. */
	s5p_init_cpu(S5P_VA_CHIPID);

166
	exynos_map_io();
167 168
}

169 170 171 172 173 174 175 176 177 178 179 180 181
/*
 * Set or clear the USE_DELAYED_RESET_ASSERTION option. Used by smp code
 * and suspend.
 *
 * This is necessary only on Exynos4 SoCs. When system is running
 * USE_DELAYED_RESET_ASSERTION should be set so the ARM CLK clock down
 * feature could properly detect global idle state when secondary CPU is
 * powered down.
 *
 * However this should not be set when such system is going into suspend.
 */
void exynos_set_delayed_reset_assertion(bool enable)
{
182
	if (of_machine_is_compatible("samsung,exynos4")) {
183 184 185 186 187 188 189 190 191 192 193 194 195
		unsigned int tmp, core_id;

		for (core_id = 0; core_id < num_possible_cpus(); core_id++) {
			tmp = pmu_raw_readl(EXYNOS_ARM_CORE_OPTION(core_id));
			if (enable)
				tmp |= S5P_USE_DELAYED_RESET_ASSERTION;
			else
				tmp &= ~(S5P_USE_DELAYED_RESET_ASSERTION);
			pmu_raw_writel(tmp, EXYNOS_ARM_CORE_OPTION(core_id));
		}
	}
}

196 197 198 199 200
/*
 * Apparently, these SoCs are not able to wake-up from suspend using
 * the PMU. Too bad. Should they suddenly become capable of such a
 * feat, the matches below should be moved to suspend.c.
 */
201
static const struct of_device_id exynos_dt_pmu_match[] = {
202
	{ .compatible = "samsung,exynos5260-pmu" },
203
	{ .compatible = "samsung,exynos5410-pmu" },
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
	{ /*sentinel*/ },
};

static void exynos_map_pmu(void)
{
	struct device_node *np;

	np = of_find_matching_node(NULL, exynos_dt_pmu_match);
	if (np)
		pmu_base_addr = of_iomap(np, 0);
}

static void __init exynos_init_irq(void)
{
	irqchip_init();
	/*
	 * Since platsmp.c needs pmu base address by the time
	 * DT is not unflatten so we can't use DT APIs before
	 * init_irq
	 */
	exynos_map_pmu();
}

227
static const struct of_device_id exynos_cpufreq_matches[] = {
228
	{ .compatible = "samsung,exynos3250", .data = "cpufreq-dt" },
229
	{ .compatible = "samsung,exynos4210", .data = "cpufreq-dt" },
230
	{ .compatible = "samsung,exynos5250", .data = "cpufreq-dt" },
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	{ /* sentinel */ }
};

static void __init exynos_cpufreq_init(void)
{
	struct device_node *root = of_find_node_by_path("/");
	const struct of_device_id *match;

	match = of_match_node(exynos_cpufreq_matches, root);
	if (!match) {
		platform_device_register_simple("exynos-cpufreq", -1, NULL, 0);
		return;
	}

	platform_device_register_simple(match->data, -1, NULL, 0);
}

248
static void __init exynos_dt_machine_init(void)
249
{
250 251 252 253
	/*
	 * This is called from smp_prepare_cpus if we've built for SMP, but
	 * we still need to set it up for PM and firmware ops if not.
	 */
254
	if (!IS_ENABLED(CONFIG_SMP))
255 256
		exynos_sysram_init();

257
#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
258 259
	if (of_machine_is_compatible("samsung,exynos4210") ||
	    of_machine_is_compatible("samsung,exynos3250"))
260 261
		exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data;
#endif
262
	if (of_machine_is_compatible("samsung,exynos4210") ||
263 264 265
	    of_machine_is_compatible("samsung,exynos4212") ||
	    (of_machine_is_compatible("samsung,exynos4412") &&
	     of_machine_is_compatible("samsung,trats2")) ||
266
	    of_machine_is_compatible("samsung,exynos3250") ||
267
	    of_machine_is_compatible("samsung,exynos5250"))
268
		platform_device_register(&exynos_cpuidle);
269

270
	exynos_cpufreq_init();
271 272

	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
273
}
274

275
static char const *const exynos_dt_compat[] __initconst = {
276 277
	"samsung,exynos3",
	"samsung,exynos3250",
278
	"samsung,exynos4",
279 280 281
	"samsung,exynos4210",
	"samsung,exynos4212",
	"samsung,exynos4412",
282
	"samsung,exynos4415",
283
	"samsung,exynos5",
284
	"samsung,exynos5250",
285
	"samsung,exynos5260",
286 287 288 289 290 291 292 293 294 295 296 297 298
	"samsung,exynos5420",
	"samsung,exynos5440",
	NULL
};

static void __init exynos_reserve(void)
{
#ifdef CONFIG_S5P_DEV_MFC
	int i;
	char *mfc_mem[] = {
		"samsung,mfc-v5",
		"samsung,mfc-v6",
		"samsung,mfc-v7",
299
		"samsung,mfc-v8",
300 301 302 303 304 305 306 307
	};

	for (i = 0; i < ARRAY_SIZE(mfc_mem); i++)
		if (of_scan_flat_dt(s5p_fdt_alloc_mfc_mem, mfc_mem[i]))
			break;
#endif
}

308 309 310 311 312 313 314 315 316
static void __init exynos_dt_fixup(void)
{
	/*
	 * Some versions of uboot pass garbage entries in the memory node,
	 * use the old CONFIG_ARM_NR_BANKS
	 */
	of_fdt_limit_memory(8);
}

317 318 319
DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)")
	/* Maintainer: Thomas Abraham <thomas.abraham@linaro.org> */
	/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
320 321
	.l2c_aux_val	= 0x3c400001,
	.l2c_aux_mask	= 0xc20fffff,
322 323 324
	.smp		= smp_ops(exynos_smp_ops),
	.map_io		= exynos_init_io,
	.init_early	= exynos_firmware_init,
325
	.init_irq	= exynos_init_irq,
326 327 328 329
	.init_machine	= exynos_dt_machine_init,
	.init_late	= exynos_init_late,
	.dt_compat	= exynos_dt_compat,
	.reserve	= exynos_reserve,
330
	.dt_fixup	= exynos_dt_fixup,
331
MACHINE_END