1bca28f8fSTomasz Figa /* 2bca28f8fSTomasz Figa * Copyright (C) 2012 Samsung Electronics. 3bca28f8fSTomasz Figa * Kyungmin Park <kyungmin.park@samsung.com> 4bca28f8fSTomasz Figa * Tomasz Figa <t.figa@samsung.com> 5bca28f8fSTomasz Figa * 6bca28f8fSTomasz Figa * This program is free software,you can redistribute it and/or modify 7bca28f8fSTomasz Figa * it under the terms of the GNU General Public License version 2 as 8bca28f8fSTomasz Figa * published by the Free Software Foundation. 9bca28f8fSTomasz Figa */ 10bca28f8fSTomasz Figa 11bca28f8fSTomasz Figa #include <linux/kernel.h> 12bca28f8fSTomasz Figa #include <linux/io.h> 13bca28f8fSTomasz Figa #include <linux/init.h> 14bca28f8fSTomasz Figa #include <linux/of.h> 15bca28f8fSTomasz Figa #include <linux/of_address.h> 16bca28f8fSTomasz Figa 172b9d9c32STomasz Figa #include <asm/cacheflush.h> 182b9d9c32STomasz Figa #include <asm/cputype.h> 19bca28f8fSTomasz Figa #include <asm/firmware.h> 202b9d9c32STomasz Figa #include <asm/suspend.h> 21bca28f8fSTomasz Figa 22bca28f8fSTomasz Figa #include <mach/map.h> 23bca28f8fSTomasz Figa 24b3205deaSSachin Kamat #include "common.h" 25bca28f8fSTomasz Figa #include "smc.h" 26bca28f8fSTomasz Figa 272b9d9c32STomasz Figa #define EXYNOS_SLEEP_MAGIC 0x00000bad 282b9d9c32STomasz Figa #define EXYNOS_BOOT_ADDR 0x8 292b9d9c32STomasz Figa #define EXYNOS_BOOT_FLAG 0xc 302b9d9c32STomasz Figa 310b7778a8SBartlomiej Zolnierkiewicz static int exynos_do_idle(unsigned long mode) 32bca28f8fSTomasz Figa { 330b7778a8SBartlomiej Zolnierkiewicz switch (mode) { 340b7778a8SBartlomiej Zolnierkiewicz case FW_DO_IDLE_AFTR: 350b7778a8SBartlomiej Zolnierkiewicz exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0); 360b7778a8SBartlomiej Zolnierkiewicz break; 370b7778a8SBartlomiej Zolnierkiewicz case FW_DO_IDLE_SLEEP: 38bca28f8fSTomasz Figa exynos_smc(SMC_CMD_SLEEP, 0, 0, 0); 390b7778a8SBartlomiej Zolnierkiewicz } 40bca28f8fSTomasz Figa return 0; 41bca28f8fSTomasz Figa } 42bca28f8fSTomasz Figa 43bca28f8fSTomasz Figa static int exynos_cpu_boot(int cpu) 44bca28f8fSTomasz Figa { 45989ff3fdSKyungmin Park /* 466457158aSChanwoo Choi * Exynos3250 doesn't need to send smc command for secondary CPU boot 476457158aSChanwoo Choi * because Exynos3250 removes WFE in secure mode. 486457158aSChanwoo Choi */ 496457158aSChanwoo Choi if (soc_is_exynos3250()) 506457158aSChanwoo Choi return 0; 516457158aSChanwoo Choi 526457158aSChanwoo Choi /* 53989ff3fdSKyungmin Park * The second parameter of SMC_CMD_CPU1BOOT command means CPU id. 54989ff3fdSKyungmin Park * But, Exynos4212 has only one secondary CPU so second parameter 55989ff3fdSKyungmin Park * isn't used for informing secure firmware about CPU id. 56989ff3fdSKyungmin Park */ 57989ff3fdSKyungmin Park if (soc_is_exynos4212()) 58989ff3fdSKyungmin Park cpu = 0; 59989ff3fdSKyungmin Park 60bca28f8fSTomasz Figa exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0); 61bca28f8fSTomasz Figa return 0; 62bca28f8fSTomasz Figa } 63bca28f8fSTomasz Figa 64bca28f8fSTomasz Figa static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr) 65bca28f8fSTomasz Figa { 66b3205deaSSachin Kamat void __iomem *boot_reg; 67b3205deaSSachin Kamat 68b3205deaSSachin Kamat if (!sysram_ns_base_addr) 69b3205deaSSachin Kamat return -ENODEV; 70b3205deaSSachin Kamat 71fe388facSOlof Johansson boot_reg = sysram_ns_base_addr + 0x1c; 72989ff3fdSKyungmin Park 7335e75645SSachin Kamat /* 7435e75645SSachin Kamat * Almost all Exynos-series of SoCs that run in secure mode don't need 7535e75645SSachin Kamat * additional offset for every CPU, with Exynos4412 being the only 7635e75645SSachin Kamat * exception. 7735e75645SSachin Kamat */ 7835e75645SSachin Kamat if (soc_is_exynos4412()) 79989ff3fdSKyungmin Park boot_reg += 4 * cpu; 80bca28f8fSTomasz Figa 81bca28f8fSTomasz Figa __raw_writel(boot_addr, boot_reg); 82bca28f8fSTomasz Figa return 0; 83bca28f8fSTomasz Figa } 84bca28f8fSTomasz Figa 852b9d9c32STomasz Figa static int exynos_cpu_suspend(unsigned long arg) 862b9d9c32STomasz Figa { 872b9d9c32STomasz Figa flush_cache_all(); 882b9d9c32STomasz Figa outer_flush_all(); 892b9d9c32STomasz Figa 902b9d9c32STomasz Figa exynos_smc(SMC_CMD_SLEEP, 0, 0, 0); 912b9d9c32STomasz Figa 922b9d9c32STomasz Figa pr_info("Failed to suspend the system\n"); 932b9d9c32STomasz Figa writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG); 942b9d9c32STomasz Figa return 1; 952b9d9c32STomasz Figa } 962b9d9c32STomasz Figa 972b9d9c32STomasz Figa static int exynos_suspend(void) 982b9d9c32STomasz Figa { 992b9d9c32STomasz Figa if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { 1002b9d9c32STomasz Figa /* Save Power control and Diagnostic registers */ 1012b9d9c32STomasz Figa asm ("mrc p15, 0, %0, c15, c0, 0\n" 1022b9d9c32STomasz Figa "mrc p15, 0, %1, c15, c0, 1\n" 1032b9d9c32STomasz Figa : "=r" (cp15_save_power), "=r" (cp15_save_diag) 1042b9d9c32STomasz Figa : : "cc"); 1052b9d9c32STomasz Figa } 1062b9d9c32STomasz Figa 1072b9d9c32STomasz Figa writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + EXYNOS_BOOT_FLAG); 1082b9d9c32STomasz Figa writel(virt_to_phys(exynos_cpu_resume_ns), 1092b9d9c32STomasz Figa sysram_ns_base_addr + EXYNOS_BOOT_ADDR); 1102b9d9c32STomasz Figa 1112b9d9c32STomasz Figa return cpu_suspend(0, exynos_cpu_suspend); 1122b9d9c32STomasz Figa } 1132b9d9c32STomasz Figa 1142b9d9c32STomasz Figa static int exynos_resume(void) 1152b9d9c32STomasz Figa { 1162b9d9c32STomasz Figa writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG); 1172b9d9c32STomasz Figa 1182b9d9c32STomasz Figa return 0; 1192b9d9c32STomasz Figa } 1202b9d9c32STomasz Figa 121bca28f8fSTomasz Figa static const struct firmware_ops exynos_firmware_ops = { 122bca28f8fSTomasz Figa .do_idle = exynos_do_idle, 123bca28f8fSTomasz Figa .set_cpu_boot_addr = exynos_set_cpu_boot_addr, 124bca28f8fSTomasz Figa .cpu_boot = exynos_cpu_boot, 1252b9d9c32STomasz Figa .suspend = exynos_suspend, 1262b9d9c32STomasz Figa .resume = exynos_resume, 127bca28f8fSTomasz Figa }; 128bca28f8fSTomasz Figa 129bca28f8fSTomasz Figa void __init exynos_firmware_init(void) 130bca28f8fSTomasz Figa { 131bca28f8fSTomasz Figa struct device_node *nd; 132bca28f8fSTomasz Figa const __be32 *addr; 133bca28f8fSTomasz Figa 134bca28f8fSTomasz Figa nd = of_find_compatible_node(NULL, NULL, 135bca28f8fSTomasz Figa "samsung,secure-firmware"); 136bca28f8fSTomasz Figa if (!nd) 137bca28f8fSTomasz Figa return; 138bca28f8fSTomasz Figa 139bca28f8fSTomasz Figa addr = of_get_address(nd, 0, NULL, NULL); 140bca28f8fSTomasz Figa if (!addr) { 141bca28f8fSTomasz Figa pr_err("%s: No address specified.\n", __func__); 142bca28f8fSTomasz Figa return; 143bca28f8fSTomasz Figa } 144bca28f8fSTomasz Figa 145bca28f8fSTomasz Figa pr_info("Running under secure firmware.\n"); 146bca28f8fSTomasz Figa 147bca28f8fSTomasz Figa register_firmware_ops(&exynos_firmware_ops); 148bca28f8fSTomasz Figa } 149