diff options
22 files changed, 666 insertions, 6 deletions
diff --git a/Documentation/arm/firmware.txt b/Documentation/arm/firmware.txt new file mode 100644 index 000000000000..c2e468fe7b0b --- /dev/null +++ b/Documentation/arm/firmware.txt @@ -0,0 +1,88 @@ +Interface for registering and calling firmware-specific operations for ARM. +---- +Written by Tomasz Figa <t.figa@samsung.com> + +Some boards are running with secure firmware running in TrustZone secure +world, which changes the way some things have to be initialized. This makes +a need to provide an interface for such platforms to specify available firmware +operations and call them when needed. + +Firmware operations can be specified using struct firmware_ops + + struct firmware_ops { + /* + * Enters CPU idle mode + */ + int (*do_idle)(void); + /* + * Sets boot address of specified physical CPU + */ + int (*set_cpu_boot_addr)(int cpu, unsigned long boot_addr); + /* + * Boots specified physical CPU + */ + int (*cpu_boot)(int cpu); + /* + * Initializes L2 cache + */ + int (*l2x0_init)(void); + }; + +and then registered with register_firmware_ops function + + void register_firmware_ops(const struct firmware_ops *ops) + +the ops pointer must be non-NULL. + +There is a default, empty set of operations provided, so there is no need to +set anything if platform does not require firmware operations. + +To call a firmware operation, a helper macro is provided + + #define call_firmware_op(op, ...) \ + ((firmware_ops->op) ? firmware_ops->op(__VA_ARGS__) : (-ENOSYS)) + +the macro checks if the operation is provided and calls it or otherwise returns +-ENOSYS to signal that given operation is not available (for example, to allow +fallback to legacy operation). + +Example of registering firmware operations: + + /* board file */ + + static int platformX_do_idle(void) + { + /* tell platformX firmware to enter idle */ + return 0; + } + + static int platformX_cpu_boot(int i) + { + /* tell platformX firmware to boot CPU i */ + return 0; + } + + static const struct firmware_ops platformX_firmware_ops = { + .do_idle = exynos_do_idle, + .cpu_boot = exynos_cpu_boot, + /* other operations not available on platformX */ + }; + + /* init_early callback of machine descriptor */ + static void __init board_init_early(void) + { + register_firmware_ops(&platformX_firmware_ops); + } + +Example of using a firmware operation: + + /* some platform code, e.g. SMP initialization */ + + __raw_writel(virt_to_phys(exynos4_secondary_startup), + CPU1_BOOT_REG); + + /* Call Exynos specific smc call */ + if (call_firmware_op(cpu_boot, cpu) == -ENOSYS) + cpu_boot_legacy(...); /* Try legacy way */ + + gic_raise_softirq(cpumask_of(cpu), 1); diff --git a/Documentation/devicetree/bindings/arm/samsung-boards.txt b/Documentation/devicetree/bindings/arm/samsung-boards.txt index 0bf68be56fd1..2168ed31e1b0 100644 --- a/Documentation/devicetree/bindings/arm/samsung-boards.txt +++ b/Documentation/devicetree/bindings/arm/samsung-boards.txt @@ -6,3 +6,13 @@ Required root node properties: - compatible = should be one or more of the following. (a) "samsung,smdkv310" - for Samsung's SMDKV310 eval board. (b) "samsung,exynos4210" - for boards based on Exynos4210 SoC. + +Optional: + - firmware node, specifying presence and type of secure firmware: + - compatible: only "samsung,secure-firmware" is currently supported + - reg: address of non-secure SYSRAM used for communication with firmware + + firmware@0203F000 { + compatible = "samsung,secure-firmware"; + reg = <0x0203F000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/misc/smc.txt b/Documentation/devicetree/bindings/misc/smc.txt new file mode 100644 index 000000000000..02b428136177 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/smc.txt @@ -0,0 +1,14 @@ +Broadcom Secure Monitor Bounce buffer +----------------------------------------------------- +This binding defines the location of the bounce buffer +used for non-secure to secure communications. + +Required properties: +- compatible : "bcm,kona-smc" +- reg : Location and size of bounce buffer + +Example: + smc@0x3404c000 { + compatible = "bcm,bcm11351-smc", "bcm,kona-smc"; + reg = <0x3404c000 0x400>; //1 KiB in SRAM + }; diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi index 8f71f40722b9..41b2c6c33f09 100644 --- a/arch/arm/boot/dts/bcm11351.dtsi +++ b/arch/arm/boot/dts/bcm11351.dtsi @@ -31,6 +31,11 @@ <0x3ff00100 0x100>; }; + smc@0x3404c000 { + compatible = "bcm,bcm11351-smc", "bcm,kona-smc"; + reg = <0x3404c000 0x400>; //1 KiB in SRAM + }; + uart@3e000000 { compatible = "bcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart"; status = "disabled"; diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 53e68b163196..48434cbe3e89 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -2,6 +2,8 @@ # Makefile for the linux kernel. # +obj-y += firmware.o + obj-$(CONFIG_ICST) += icst.o obj-$(CONFIG_SA1111) += sa1111.o obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o diff --git a/arch/arm/common/firmware.c b/arch/arm/common/firmware.c new file mode 100644 index 000000000000..27ddccb1131f --- /dev/null +++ b/arch/arm/common/firmware.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2012 Samsung Electronics. + * Kyungmin Park <kyungmin.park@samsung.com> + * Tomasz Figa <t.figa@samsung.com> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/suspend.h> + +#include <asm/firmware.h> + +static const struct firmware_ops default_firmware_ops; + +const struct firmware_ops *firmware_ops = &default_firmware_ops; diff --git a/arch/arm/include/asm/firmware.h b/arch/arm/include/asm/firmware.h new file mode 100644 index 000000000000..15631300c238 --- /dev/null +++ b/arch/arm/include/asm/firmware.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Samsung Electronics. + * Kyungmin Park <kyungmin.park@samsung.com> + * Tomasz Figa <t.figa@samsung.com> + * + * 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. + */ + +#ifndef __ASM_ARM_FIRMWARE_H +#define __ASM_ARM_FIRMWARE_H + +#include <linux/bug.h> + +/* + * struct firmware_ops + * + * A structure to specify available firmware operations. + * + * A filled up structure can be registered with register_firmware_ops(). + */ +struct firmware_ops { + /* + * Enters CPU idle mode + */ + int (*do_idle)(void); + /* + * Sets boot address of specified physical CPU + */ + int (*set_cpu_boot_addr)(int cpu, unsigned long boot_addr); + /* + * Boots specified physical CPU + */ + int (*cpu_boot)(int cpu); + /* + * Initializes L2 cache + */ + int (*l2x0_init)(void); +}; + +/* Global pointer for current firmware_ops structure, can't be NULL. */ +extern const struct firmware_ops *firmware_ops; + +/* + * call_firmware_op(op, ...) + * + * Checks if firmware operation is present and calls it, + * otherwise returns -ENOSYS + */ +#define call_firmware_op(op, ...) \ + ((firmware_ops->op) ? firmware_ops->op(__VA_ARGS__) : (-ENOSYS)) + +/* + * register_firmware_ops(ops) + * + * A function to register platform firmware_ops struct. + */ +static inline void register_firmware_ops(const struct firmware_ops *ops) +{ + BUG_ON(!ops); + + firmware_ops = ops; +} + +#endif diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile index bbf412261e5e..6adb6aecf48f 100644 --- a/arch/arm/mach-bcm/Makefile +++ b/arch/arm/mach-bcm/Makefile @@ -10,4 +10,6 @@ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -obj-$(CONFIG_ARCH_BCM) := board_bcm.o +obj-$(CONFIG_ARCH_BCM) := board_bcm.o bcm_kona_smc.o bcm_kona_smc_asm.o +plus_sec := $(call as-instr,.arch_extension sec,+sec) +AFLAGS_bcm_kona_smc_asm.o :=-Wa,-march=armv7-a$(plus_sec) diff --git a/arch/arm/mach-bcm/bcm_kona_smc.c b/arch/arm/mach-bcm/bcm_kona_smc.c new file mode 100644 index 000000000000..56d9d19b2470 --- /dev/null +++ b/arch/arm/mach-bcm/bcm_kona_smc.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <stdarg.h> +#include <linux/smp.h> +#include <linux/io.h> +#include <linux/ioport.h> + +#include <asm/cacheflush.h> +#include <linux/of_address.h> + +#include "bcm_kona_smc.h" + +struct secure_bridge_data { + void __iomem *bounce; /* virtual address */ + u32 __iomem buffer_addr; /* physical address */ + int initialized; +} bridge_data; + +struct bcm_kona_smc_data { + unsigned service_id; + unsigned arg0; + unsigned arg1; + unsigned arg2; + unsigned arg3; +}; + +static const struct of_device_id bcm_kona_smc_ids[] __initconst = { + {.compatible = "bcm,kona-smc"}, + {}, +}; + +/* Map in the bounce area */ +void __init bcm_kona_smc_init(void) +{ + struct device_node *node; + + /* Read buffer addr and size from the device tree node */ + node = of_find_matching_node(NULL, bcm_kona_smc_ids); + BUG_ON(!node); + + /* Don't care about size or flags of the DT node */ + bridge_data.buffer_addr = + be32_to_cpu(*of_get_address(node, 0, NULL, NULL)); + BUG_ON(!bridge_data.buffer_addr); + + bridge_data.bounce = of_iomap(node, 0); + BUG_ON(!bridge_data.bounce); + + bridge_data.initialized = 1; + + pr_info("Secure API initialized!\n"); +} + +/* __bcm_kona_smc() should only run on CPU 0, with pre-emption disabled */ +static void __bcm_kona_smc(void *info) +{ + struct bcm_kona_smc_data *data = info; + u32 *args = bridge_data.bounce; + int rc = 0; + + /* Must run on CPU 0 */ + BUG_ON(smp_processor_id() != 0); + + /* Check map in the bounce area */ + BUG_ON(!bridge_data.initialized); + + /* Copy one 32 bit word into the bounce area */ + args[0] = data->arg0; + args[1] = data->arg1; + args[2] = data->arg2; + args[3] = data->arg3; + + /* Flush caches for input data passed to Secure Monitor */ + if (data->service_id != SSAPI_BRCM_START_VC_CORE) + flush_cache_all(); + + /* Trap into Secure Monitor */ + rc = bcm_kona_smc_asm(data->service_id, bridge_data.buffer_addr); + + if (rc != SEC_ROM_RET_OK) + pr_err("Secure Monitor call failed (0x%x)!\n", rc); +} + +unsigned bcm_kona_smc(unsigned service_id, unsigned arg0, unsigned arg1, + unsigned arg2, unsigned arg3) +{ + struct bcm_kona_smc_data data; + + data.service_id = service_id; + data.arg0 = arg0; + data.arg1 = arg1; + data.arg2 = arg2; + data.arg3 = arg3; + + /* + * Due to a limitation of the secure monitor, we must use the SMP + * infrastructure to forward all secure monitor calls to Core 0. + */ + if (get_cpu() != 0) + smp_call_function_single(0, __bcm_kona_smc, (void *)&data, 1); + else + __bcm_kona_smc(&data); + + put_cpu(); + + return 0; +} diff --git a/arch/arm/mach-bcm/bcm_kona_smc.h b/arch/arm/mach-bcm/bcm_kona_smc.h new file mode 100644 index 000000000000..3bedbed1c21b --- /dev/null +++ b/arch/arm/mach-bcm/bcm_kona_smc.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2013 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef BCM_KONA_SMC_H +#define BCM_KONA_SMC_H + +#include <linux/types.h> +#define FLAGS (SEC_ROM_ICACHE_ENABLE_MASK | SEC_ROM_DCACHE_ENABLE_MASK | \ + SEC_ROM_IRQ_ENABLE_MASK | SEC_ROM_FIQ_ENABLE_MASK) + +/*! + * Definitions for IRQ & FIQ Mask for ARM + */ + +#define FIQ_IRQ_MASK 0xC0 +#define FIQ_MASK 0x40 +#define IRQ_MASK 0x80 + +/*! + * Secure Mode FLAGs + */ + +/* When set, enables ICache within the secure mode */ +#define SEC_ROM_ICACHE_ENABLE_MASK 0x00000001 + +/* When set, enables DCache within the secure mode */ +#define SEC_ROM_DCACHE_ENABLE_MASK 0x00000002 + +/* When set, enables IRQ within the secure mode */ +#define SEC_ROM_IRQ_ENABLE_MASK 0x00000004 + +/* When set, enables FIQ within the secure mode */ +#define SEC_ROM_FIQ_ENABLE_MASK 0x00000008 + +/* When set, enables Unified L2 cache within the secure mode */ +#define SEC_ROM_UL2_CACHE_ENABLE_MASK 0x00000010 + +/* Broadcom Secure Service API Service IDs */ +#define SSAPI_DORMANT_ENTRY_SERV 0x01000000 +#define SSAPI_PUBLIC_OTP_SERV 0x01000001 +#define SSAPI_ENABLE_L2_CACHE 0x01000002 +#define SSAPI_DISABLE_L2_CACHE 0x01000003 +#define SSAPI_WRITE_SCU_STATUS 0x01000004 +#define SSAPI_WRITE_PWR_GATE 0x01000005 + +/* Broadcom Secure Service API Return Codes */ +#define SEC_ROM_RET_OK 0x00000001 +#define SEC_ROM_RET_FAIL 0x00000009 + +#define SSAPI_RET_FROM_INT_SERV 0x4 +#define SEC_EXIT_NORMAL 0x1 + +#define SSAPI_ROW_AES 0x0E000006 +#define SSAPI_BRCM_START_VC_CORE 0x0E000008 + +#ifndef __ASSEMBLY__ +extern void bcm_kona_smc_init(void); + +extern unsigned bcm_kona_smc(unsigned service_id, + unsigned arg0, + unsigned arg1, + unsigned arg2, + unsigned arg3); + +extern int bcm_kona_smc_asm(u32 service_id, + u32 buffer_addr); + +#endif /* __ASSEMBLY__ */ + +#endif /* BCM_KONA_SMC_H */ diff --git a/arch/arm/mach-bcm/bcm_kona_smc_asm.S b/arch/arm/mach-bcm/bcm_kona_smc_asm.S new file mode 100644 index 000000000000..a1608480d60d --- /dev/null +++ b/arch/arm/mach-bcm/bcm_kona_smc_asm.S @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/linkage.h> +#include "bcm_kona_smc.h" + +/* + * int bcm_kona_smc_asm(u32 service_id, u32 buffer_addr) + */ + +ENTRY(bcm_kona_smc_asm) + stmfd sp!, {r4-r12, lr} + mov r4, r0 @ service_id + mov r5, #3 @ Keep IRQ and FIQ off in SM + /* + * Since interrupts are disabled in the open mode, we must keep + * interrupts disabled in secure mode by setting R5=0x3. If interrupts + * are enabled in open mode, we can set R5=0x0 to allow interrupts in + * secure mode. If we did this, the secure monitor would return back + * control to the open mode to handle the interrupt prior to completing + * the secure service. If this happened, R12 would not be + * SEC_EXIT_NORMAL and we would need to call SMC again after resetting + * R5 (it gets clobbered by the secure monitor) and setting R4 to + * SSAPI_RET_FROM_INT_SERV to indicate that we want the secure monitor + * to finish up the previous uncompleted secure service. + */ + mov r6, r1 @ buffer_addr + smc #0 + /* Check r12 for SEC_EXIT_NORMAL here if interrupts are enabled */ + ldmfd sp!, {r4-r12, pc} +ENDPROC(bcm_kona_smc_asm) diff --git a/arch/arm/mach-bcm/board_bcm.c b/arch/arm/mach-bcm/board_bcm.c index 259593540477..22e8421b1df3 100644 --- a/arch/arm/mach-bcm/board_bcm.c +++ b/arch/arm/mach-bcm/board_bcm.c @@ -20,12 +20,35 @@ #include <asm/mach/arch.h> #include <asm/mach/time.h> +#include <asm/hardware/cache-l2x0.h> +#include "bcm_kona_smc.h" + +static int __init kona_l2_cache_init(void) +{ + if (!IS_ENABLED(CONFIG_CACHE_L2X0)) + return 0; + + bcm_kona_smc(SSAPI_ENABLE_L2_CACHE, 0, 0, 0, 0); + + /* + * The aux_val and aux_mask have no effect since L2 cache is already + * enabled. Pass 0s for aux_val and 1s for aux_mask for default value. + */ + l2x0_of_init(0, ~0); + + return 0; +} + static void __init board_init(void) { of_platform_populate(NULL, of_default_bus_match_table, NULL, &platform_bus); + + bcm_kona_smc_init(); + + kona_l2_cache_init(); } static const char * const bcm11351_dt_compat[] = { "bcm,bcm11351", NULL, }; diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index d2f6b362b6dd..b09b027178f3 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -24,6 +24,12 @@ obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +obj-$(CONFIG_ARCH_EXYNOS) += exynos-smc.o +obj-$(CONFIG_ARCH_EXYNOS) += firmware.o + +plus_sec := $(call as-instr,.arch_extension sec,+sec) +AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec) + # machine support obj-$(CONFIG_MACH_SMDKC210) += mach-smdkv310.o diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index b35c60059bb8..46089fe24705 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -233,6 +233,33 @@ static struct map_desc exynos4_iodesc1[] __initdata = { }, }; +static struct map_desc exynos4210_iodesc[] __initdata = { + { + .virtual = (unsigned long)S5P_VA_SYSRAM_NS, + .pfn = __phys_to_pfn(EXYNOS4210_PA_SYSRAM_NS), + .length = SZ_4K, + .type = MT_DEVICE, + }, +}; + +static struct map_desc exynos4x12_iodesc[] __initdata = { + { + .virtual = (unsigned long)S5P_VA_SYSRAM_NS, + .pfn = __phys_to_pfn(EXYNOS4x12_PA_SYSRAM_NS), + .length = SZ_4K, + .type = MT_DEVICE, + }, +}; + +static struct map_desc exynos5250_iodesc[] __initdata = { + { + .virtual = (unsigned long)S5P_VA_SYSRAM_NS, + .pfn = __phys_to_pfn(EXYNOS5250_PA_SYSRAM_NS), + .length = SZ_4K, + .type = MT_DEVICE, + }, +}; + static struct map_desc exynos5_iodesc[] __initdata = { { .virtual = (unsigned long)S3C_VA_SYS, @@ -361,6 +388,11 @@ static void __init exynos4_map_io(void) else iotable_init(exynos4_iodesc1, ARRAY_SIZE(exynos4_iodesc1)); + if (soc_is_exynos4210()) + iotable_init(exynos4210_iodesc, ARRAY_SIZE(exynos4210_iodesc)); + if (soc_is_exynos4212() || soc_is_exynos4412()) + iotable_init(exynos4x12_iodesc, ARRAY_SIZE(exynos4x12_iodesc)); + /* initialize device information early */ exynos4_default_sdhci0(); exynos4_default_sdhci1(); @@ -393,6 +425,9 @@ static void __init exynos4_map_io(void) static void __init exynos5_map_io(void) { iotable_init(exynos5_iodesc, ARRAY_SIZE(exynos5_iodesc)); + + if (soc_is_exynos5250()) + iotable_init(exynos5250_iodesc, ARRAY_SIZE(exynos5250_iodesc)); } static void __init exynos5440_map_io(void) diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index cb89ab886950..b17448c1a164 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -30,6 +30,8 @@ void exynos_init_late(void); void exynos4_clk_init(struct device_node *np); void exynos4_clk_register_fixed_ext(unsigned long, unsigned long); +void exynos_firmware_init(void); + #ifdef CONFIG_PM_GENERIC_DOMAINS int exynos_pm_late_initcall(void); #else diff --git a/arch/arm/mach-exynos/exynos-smc.S b/arch/arm/mach-exynos/exynos-smc.S new file mode 100644 index 000000000000..2e27aa3813fd --- /dev/null +++ b/arch/arm/mach-exynos/exynos-smc.S @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 Samsung Electronics. + * + * Copied from omap-smc.S Copyright (C) 2010 Texas Instruments, Inc. + * + * 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. + */ + +#include <linux/linkage.h> + +/* + * Function signature: void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3) + */ + +ENTRY(exynos_smc) + stmfd sp!, {r4-r11, lr} + dsb + smc #0 + ldmfd sp!, {r4-r11, pc} +ENDPROC(exynos_smc) diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c new file mode 100644 index 000000000000..ed11f100d479 --- /dev/null +++ b/arch/arm/mach-exynos/firmware.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012 Samsung Electronics. + * Kyungmin Park <kyungmin.park@samsung.com> + * Tomasz Figa <t.figa@samsung.com> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include <asm/firmware.h> + +#include <mach/map.h> + +#include "smc.h" + +static int exynos_do_idle(void) +{ + exynos_smc(SMC_CMD_SLEEP, 0, 0, 0); + return 0; +} + +static int exynos_cpu_boot(int cpu) +{ + exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0); + return 0; +} + +static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr) +{ + void __iomem *boot_reg = S5P_VA_SYSRAM_NS + 0x1c + 4*cpu; + + __raw_writel(boot_addr, boot_reg); + return 0; +} + +static const struct firmware_ops exynos_firmware_ops = { + .do_idle = exynos_do_idle, + .set_cpu_boot_addr = exynos_set_cpu_boot_addr, + .cpu_boot = exynos_cpu_boot, +}; + +void __init exynos_firmware_init(void) +{ + if (of_have_populated_dt()) { + struct device_node *nd; + const __be32 *addr; + + nd = of_find_compatible_node(NULL, NULL, + "samsung,secure-firmware"); + if (!nd) + return; + + addr = of_get_address(nd, 0, NULL, NULL); + if (!addr) { + pr_err("%s: No address specified.\n", __func__); + return; + } + } + + pr_info("Running under secure firmware.\n"); + + register_firmware_ops(&exynos_firmware_ops); +} diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h index 7f99b7b187d6..99e0a79f3b1f 100644 --- a/arch/arm/mach-exynos/include/mach/map.h +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -26,6 +26,9 @@ #define EXYNOS4_PA_SYSRAM0 0x02025000 #define EXYNOS4_PA_SYSRAM1 0x02020000 #define EXYNOS5_PA_SYSRAM 0x02020000 +#define EXYNOS4210_PA_SYSRAM_NS 0x0203F000 +#define EXYNOS4x12_PA_SYSRAM_NS 0x0204F000 +#define EXYNOS5250_PA_SYSRAM_NS 0x0204F000 #define EXYNOS4_PA_FIMC0 0x11800000 #define EXYNOS4_PA_FIMC1 0x11810000 diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c index ac27f3cd121f..b9ed834a7eee 100644 --- a/arch/arm/mach-exynos/mach-exynos4-dt.c +++ b/arch/arm/mach-exynos/mach-exynos4-dt.c @@ -57,6 +57,7 @@ DT_MACHINE_START(EXYNOS4210_DT, "Samsung Exynos4 (Flattened Device Tree)") .smp = smp_ops(exynos_smp_ops), .init_irq = exynos4_init_irq, .map_io = exynos4_dt_map_io, + .init_early = exynos_firmware_init, .init_machine = exynos4_dt_machine_init, .init_late = exynos_init_late, .init_time = exynos_init_time, diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 95e04bd5813f..a0e8ff7758a4 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -24,6 +24,7 @@ #include <asm/cacheflush.h> #include <asm/smp_plat.h> #include <asm/smp_scu.h> +#include <asm/firmware.h> #include <mach/hardware.h> #include <mach/regs-clock.h> @@ -137,10 +138,21 @@ static int __cpuinit exynos_boot_secondary(unsigned int cpu, struct task_struct timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { + unsigned long boot_addr; + smp_rmb(); - __raw_writel(virt_to_phys(exynos4_secondary_startup), - cpu_boot_reg(phys_cpu)); + boot_addr = virt_to_phys(exynos4_secondary_startup); + + /* + * Try to set boot address using firmware first + * and fall back to boot register if it fails. + */ + if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr)) + __raw_writel(boot_addr, cpu_boot_reg(phys_cpu)); + + call_firmware_op(cpu_boot, phys_cpu); + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); if (pen_release == -1) @@ -196,10 +208,20 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) * system-wide flags register. The boot monitor waits * until it receives a soft interrupt, and then the * secondary CPU branches to this address. + * + * Try using firmware operation first and fall back to + * boot register if it fails. */ - for (i = 1; i < max_cpus; ++i) - __raw_writel(virt_to_phys(exynos4_secondary_startup), - cpu_boot_reg(cpu_logical_map(i))); + for (i = 1; i < max_cpus; ++i) { + unsigned long phys_cpu; + unsigned long boot_addr; + + phys_cpu = cpu_logical_map(i); + boot_addr = virt_to_phys(exynos4_secondary_startup); + + if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr)) + __raw_writel(boot_addr, cpu_boot_reg(phys_cpu)); + } } struct smp_operations exynos_smp_ops __initdata = { diff --git a/arch/arm/mach-exynos/smc.h b/arch/arm/mach-exynos/smc.h new file mode 100644 index 000000000000..13a1dc8ecbf2 --- /dev/null +++ b/arch/arm/mach-exynos/smc.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2012 Samsung Electronics. + * + * EXYNOS - SMC Call + * + * 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. + */ + +#ifndef __ASM_ARCH_EXYNOS_SMC_H +#define __ASM_ARCH_EXYNOS_SMC_H + +#define SMC_CMD_INIT (-1) +#define SMC_CMD_INFO (-2) +/* For Power Management */ +#define SMC_CMD_SLEEP (-3) +#define SMC_CMD_CPU1BOOT (-4) +#define SMC_CMD_CPU0AFTR (-5) +/* For CP15 Access */ +#define SMC_CMD_C15RESUME (-11) +/* For L2 Cache Access */ +#define SMC_CMD_L2X0CTRL (-21) +#define SMC_CMD_L2X0SETUP1 (-22) +#define SMC_CMD_L2X0SETUP2 (-23) +#define SMC_CMD_L2X0INVALL (-24) +#define SMC_CMD_L2X0DEBUG (-25) + +extern void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3); + +#endif diff --git a/arch/arm/plat-samsung/include/plat/map-s5p.h b/arch/arm/plat-samsung/include/plat/map-s5p.h index c2d7bdae5891..c18678610bc0 100644 --- a/arch/arm/plat-samsung/include/plat/map-s5p.h +++ b/arch/arm/plat-samsung/include/plat/map-s5p.h @@ -22,6 +22,7 @@ #define S5P_VA_GPIO3 S3C_ADDR(0x02280000) #define S5P_VA_SYSRAM S3C_ADDR(0x02400000) +#define S5P_VA_SYSRAM_NS S3C_ADDR(0x02410000) #define S5P_VA_DMC0 S3C_ADDR(0x02440000) #define S5P_VA_DMC1 S3C_ADDR(0x02480000) #define S5P_VA_SROMC S3C_ADDR(0x024C0000) |