summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/arm/firmware.txt88
-rw-r--r--Documentation/devicetree/bindings/arm/samsung-boards.txt10
-rw-r--r--Documentation/devicetree/bindings/misc/smc.txt14
-rw-r--r--arch/arm/boot/dts/bcm11351.dtsi5
-rw-r--r--arch/arm/common/Makefile2
-rw-r--r--arch/arm/common/firmware.c18
-rw-r--r--arch/arm/include/asm/firmware.h66
-rw-r--r--arch/arm/mach-bcm/Makefile4
-rw-r--r--arch/arm/mach-bcm/bcm_kona_smc.c118
-rw-r--r--arch/arm/mach-bcm/bcm_kona_smc.h80
-rw-r--r--arch/arm/mach-bcm/bcm_kona_smc_asm.S41
-rw-r--r--arch/arm/mach-bcm/board_bcm.c23
-rw-r--r--arch/arm/mach-exynos/Makefile6
-rw-r--r--arch/arm/mach-exynos/common.c35
-rw-r--r--arch/arm/mach-exynos/common.h2
-rw-r--r--arch/arm/mach-exynos/exynos-smc.S22
-rw-r--r--arch/arm/mach-exynos/firmware.c70
-rw-r--r--arch/arm/mach-exynos/include/mach/map.h3
-rw-r--r--arch/arm/mach-exynos/mach-exynos4-dt.c1
-rw-r--r--arch/arm/mach-exynos/platsmp.c32
-rw-r--r--arch/arm/mach-exynos/smc.h31
-rw-r--r--arch/arm/plat-samsung/include/plat/map-s5p.h1
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)