Skip to content

Commit

Permalink
Merge tag 'secure-exynos-for-v3.10' of git://git.kernel.org/pub/scm/l…
Browse files Browse the repository at this point in the history
…inux/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>
  • Loading branch information
Arnd Bergmann committed Apr 9, 2013
2 parents cb63c02 + beddf63 commit a1faef9
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 5 deletions.
88 changes: 88 additions & 0 deletions Documentation/arm/firmware.txt
Original file line number Diff line number Diff line change
@@ -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);
10 changes: 10 additions & 0 deletions Documentation/devicetree/bindings/arm/samsung-boards.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 2 additions & 0 deletions arch/arm/common/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions arch/arm/common/firmware.c
Original file line number Diff line number Diff line change
@@ -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;
66 changes: 66 additions & 0 deletions arch/arm/include/asm/firmware.h
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions arch/arm/mach-exynos/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions arch/arm/mach-exynos/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions arch/arm/mach-exynos/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions arch/arm/mach-exynos/exynos-smc.S
Original file line number Diff line number Diff line change
@@ -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)
70 changes: 70 additions & 0 deletions arch/arm/mach-exynos/firmware.c
Original file line number Diff line number Diff line change
@@ -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);
}
3 changes: 3 additions & 0 deletions arch/arm/mach-exynos/include/mach/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions arch/arm/mach-exynos/mach-exynos4-dt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit a1faef9

Please sign in to comment.