Skip to content

Commit

Permalink
Merge tag 'firmware-for-linus' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/arm/arm-soc

Pull ARM platform specific firmware interfaces from Olof Johansson:
 "Two platforms, bcm and exynos have their own firmware interfaces using
  the "secure monitor call", this adds support for those.

  We had originally planned to have a third set of patches in here,
  which would extend support for the existing generic "psci" call that
  is used on multiple platforms as well as Xen and KVM guests, but that
  ended up getting dropped because the patches were not ready in time."

* tag 'firmware-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc:
  ARM: bcm: mark bcm_kona_smc_init as __init
  ARM: bcm281xx: Add DT support for SMC handler
  ARM: bcm281xx: Add L2 cache enable code
  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
  • Loading branch information
torvalds committed May 4, 2013
2 parents 22b1543 + 721e020 commit e3d9884
Show file tree
Hide file tree
Showing 22 changed files with 666 additions and 6 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 <[email protected]>

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>;
};
14 changes: 14 additions & 0 deletions Documentation/devicetree/bindings/misc/smc.txt
Original file line number Diff line number Diff line change
@@ -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
};
5 changes: 5 additions & 0 deletions arch/arm/boot/dts/bcm11351.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
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 <[email protected]>
* Tomasz Figa <[email protected]>
*
* 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 <[email protected]>
* Tomasz Figa <[email protected]>
*
* 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
4 changes: 3 additions & 1 deletion arch/arm/mach-bcm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
118 changes: 118 additions & 0 deletions arch/arm/mach-bcm/bcm_kona_smc.c
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit e3d9884

Please sign in to comment.