提交 a1faef96 编写于 作者: A Arnd Bergmann

Merge tag 'secure-exynos-for-v3.10' of...

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: NArnd Bergmann <arnd@arndb.de>
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
......
/*
* 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;
/*
* 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
......
/*
* 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)
/*
* 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 = {
......
/*
* 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)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册