add support secure firmware for exynos
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJRYvieAAoJEA0Cl+kVi2xq75IP/0yWplek6QrI6Xf1pWu2QJbb VLHzk1d/dQ7ufebVLyWwtSKmoC9NiBOknr2xFXv9acpY0L/LkorQixTwGxaej65V xf1ye6Ph8HOVPPOtMGBXkP8wURfVNwlcsGAVp9T8TagIPQvbunLr9y7jD052tgVL 6+GkV8Zk0wNzrKHa2yoCoXFN80UHlbOEEfIMWWPUEnA8a/KpK5qX7t6dVXJARJPG mWJF27RR0q47+ZqwtENOrwylIAvrlZ5/ZsrWeWE0dg6luZtGYLfbbFLjFz+yT+Zm 4n75EAXOtLtzCydGKIZUonr0HpKYc2Ihv5IngkxKY94uT+djO6vZMLgjf7auAIZR GKieMfYrsRae1EZTsbxe+Ps5Btlby6nrL4xfNrqV2WYgR70D7+0qih90QfzdEoN5 oCRU4jkoX1Rszb4Eh277Ke8UMaZYCW9MvaYeVXSIiXQIyo745UjXcuFYL+PwnKH0 SDih2sE35JpAwKvQt/dMdUNdsOXJKQDGtqg+4FSgLDzaPcsEsS5p3f0TXMfrfygR xYbTttmwQXasatWAo7jgT0dqhm8d7JFiBmVWNIvJ01MMzmmAlTHwFzaqJddwsji4 iTemAEDvQdfJ7UNvJZ7Fd5k55XEBWVBMAECMyfM1MEpbyyKavr7nUGsPpEITsLnM g7ALbXtbnpPtxRjKRFfL =OIUa -----END PGP SIGNATURE----- Merge tag 'secure-exynos-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into next/firmware From Kukjin Kim <kgene.kim@samsung.com>: add support secure firmware for exynos * tag 'secure-exynos-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung: ARM: EXYNOS: Add secure firmware support to secondary CPU bring-up ARM: EXYNOS: Add IO mapping for non-secure SYSRAM. ARM: EXYNOS: Add support for Exynos secure firmware ARM: EXYNOS: Add support for secure monitor calls ARM: Add interface for registering and calling firmware-specific operations Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
a1faef961b
|
@ -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);
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -232,6 +232,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,
|
||||
|
@ -360,6 +387,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();
|
||||
|
@ -392,6 +424,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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -25,6 +25,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>
|
||||
|
@ -145,10 +146,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)
|
||||
|
@ -204,10 +216,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 = {
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue