mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 15:11:31 +00:00
7366b92a77
Some boards are running with secure firmware running in TrustZone secure world, which changes the way some things have to be initialized. This patch adds an interface for platforms to specify available firmware operations and call them. A wrapper macro, call_firmware_op(), checks if the operation is provided and calls it if so, otherwise returns -ENOSYS to allow fallback to legacy operation.. By default no operations are provided. Example of use: In code using firmware ops: __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); In board-/platform-specific code: 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 */ }; static void __init board_init_early(void) { register_firmware_ops(&platformX_firmware_ops); } Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Tomasz Figa <t.figa@samsung.com> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
89 lines
2.3 KiB
Plaintext
89 lines
2.3 KiB
Plaintext
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);
|