diff options
-rw-r--r-- | Documentation/arm/firmware.txt | 88 | ||||
-rw-r--r-- | arch/arm/common/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/common/firmware.c | 18 | ||||
-rw-r--r-- | arch/arm/include/asm/firmware.h | 66 |
4 files changed, 174 insertions, 0 deletions
diff --git a/Documentation/arm/firmware.txt b/Documentation/arm/firmware.txt new file mode 100644 index 000000000000..c2e468fe7b0b --- /dev/null +++ b/Documentation/arm/firmware.txt | |||
@@ -0,0 +1,88 @@ | |||
1 | Interface for registering and calling firmware-specific operations for ARM. | ||
2 | ---- | ||
3 | Written by Tomasz Figa <t.figa@samsung.com> | ||
4 | |||
5 | Some boards are running with secure firmware running in TrustZone secure | ||
6 | world, which changes the way some things have to be initialized. This makes | ||
7 | a need to provide an interface for such platforms to specify available firmware | ||
8 | operations and call them when needed. | ||
9 | |||
10 | Firmware operations can be specified using struct firmware_ops | ||
11 | |||
12 | struct firmware_ops { | ||
13 | /* | ||
14 | * Enters CPU idle mode | ||
15 | */ | ||
16 | int (*do_idle)(void); | ||
17 | /* | ||
18 | * Sets boot address of specified physical CPU | ||
19 | */ | ||
20 | int (*set_cpu_boot_addr)(int cpu, unsigned long boot_addr); | ||
21 | /* | ||
22 | * Boots specified physical CPU | ||
23 | */ | ||
24 | int (*cpu_boot)(int cpu); | ||
25 | /* | ||
26 | * Initializes L2 cache | ||
27 | */ | ||
28 | int (*l2x0_init)(void); | ||
29 | }; | ||
30 | |||
31 | and then registered with register_firmware_ops function | ||
32 | |||
33 | void register_firmware_ops(const struct firmware_ops *ops) | ||
34 | |||
35 | the ops pointer must be non-NULL. | ||
36 | |||
37 | There is a default, empty set of operations provided, so there is no need to | ||
38 | set anything if platform does not require firmware operations. | ||
39 | |||
40 | To call a firmware operation, a helper macro is provided | ||
41 | |||
42 | #define call_firmware_op(op, ...) \ | ||
43 | ((firmware_ops->op) ? firmware_ops->op(__VA_ARGS__) : (-ENOSYS)) | ||
44 | |||
45 | the macro checks if the operation is provided and calls it or otherwise returns | ||
46 | -ENOSYS to signal that given operation is not available (for example, to allow | ||
47 | fallback to legacy operation). | ||
48 | |||
49 | Example of registering firmware operations: | ||
50 | |||
51 | /* board file */ | ||
52 | |||
53 | static int platformX_do_idle(void) | ||
54 | { | ||
55 | /* tell platformX firmware to enter idle */ | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static int platformX_cpu_boot(int i) | ||
60 | { | ||
61 | /* tell platformX firmware to boot CPU i */ | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static const struct firmware_ops platformX_firmware_ops = { | ||
66 | .do_idle = exynos_do_idle, | ||
67 | .cpu_boot = exynos_cpu_boot, | ||
68 | /* other operations not available on platformX */ | ||
69 | }; | ||
70 | |||
71 | /* init_early callback of machine descriptor */ | ||
72 | static void __init board_init_early(void) | ||
73 | { | ||
74 | register_firmware_ops(&platformX_firmware_ops); | ||
75 | } | ||
76 | |||
77 | Example of using a firmware operation: | ||
78 | |||
79 | /* some platform code, e.g. SMP initialization */ | ||
80 | |||
81 | __raw_writel(virt_to_phys(exynos4_secondary_startup), | ||
82 | CPU1_BOOT_REG); | ||
83 | |||
84 | /* Call Exynos specific smc call */ | ||
85 | if (call_firmware_op(cpu_boot, cpu) == -ENOSYS) | ||
86 | cpu_boot_legacy(...); /* Try legacy way */ | ||
87 | |||
88 | gic_raise_softirq(cpumask_of(cpu), 1); | ||
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index dc8dd0de5c0f..45f7eae93ae0 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile | |||
@@ -2,6 +2,8 @@ | |||
2 | # Makefile for the linux kernel. | 2 | # Makefile for the linux kernel. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += firmware.o | ||
6 | |||
5 | obj-$(CONFIG_ICST) += icst.o | 7 | obj-$(CONFIG_ICST) += icst.o |
6 | obj-$(CONFIG_SA1111) += sa1111.o | 8 | obj-$(CONFIG_SA1111) += sa1111.o |
7 | obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o | 9 | obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o |
diff --git a/arch/arm/common/firmware.c b/arch/arm/common/firmware.c new file mode 100644 index 000000000000..27ddccb1131f --- /dev/null +++ b/arch/arm/common/firmware.c | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Samsung Electronics. | ||
3 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
4 | * Tomasz Figa <t.figa@samsung.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/suspend.h> | ||
13 | |||
14 | #include <asm/firmware.h> | ||
15 | |||
16 | static const struct firmware_ops default_firmware_ops; | ||
17 | |||
18 | const struct firmware_ops *firmware_ops = &default_firmware_ops; | ||
diff --git a/arch/arm/include/asm/firmware.h b/arch/arm/include/asm/firmware.h new file mode 100644 index 000000000000..15631300c238 --- /dev/null +++ b/arch/arm/include/asm/firmware.h | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Samsung Electronics. | ||
3 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
4 | * Tomasz Figa <t.figa@samsung.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef __ASM_ARM_FIRMWARE_H | ||
12 | #define __ASM_ARM_FIRMWARE_H | ||
13 | |||
14 | #include <linux/bug.h> | ||
15 | |||
16 | /* | ||
17 | * struct firmware_ops | ||
18 | * | ||
19 | * A structure to specify available firmware operations. | ||
20 | * | ||
21 | * A filled up structure can be registered with register_firmware_ops(). | ||
22 | */ | ||
23 | struct firmware_ops { | ||
24 | /* | ||
25 | * Enters CPU idle mode | ||
26 | */ | ||
27 | int (*do_idle)(void); | ||
28 | /* | ||
29 | * Sets boot address of specified physical CPU | ||
30 | */ | ||
31 | int (*set_cpu_boot_addr)(int cpu, unsigned long boot_addr); | ||
32 | /* | ||
33 | * Boots specified physical CPU | ||
34 | */ | ||
35 | int (*cpu_boot)(int cpu); | ||
36 | /* | ||
37 | * Initializes L2 cache | ||
38 | */ | ||
39 | int (*l2x0_init)(void); | ||
40 | }; | ||
41 | |||
42 | /* Global pointer for current firmware_ops structure, can't be NULL. */ | ||
43 | extern const struct firmware_ops *firmware_ops; | ||
44 | |||
45 | /* | ||
46 | * call_firmware_op(op, ...) | ||
47 | * | ||
48 | * Checks if firmware operation is present and calls it, | ||
49 | * otherwise returns -ENOSYS | ||
50 | */ | ||
51 | #define call_firmware_op(op, ...) \ | ||
52 | ((firmware_ops->op) ? firmware_ops->op(__VA_ARGS__) : (-ENOSYS)) | ||
53 | |||
54 | /* | ||
55 | * register_firmware_ops(ops) | ||
56 | * | ||
57 | * A function to register platform firmware_ops struct. | ||
58 | */ | ||
59 | static inline void register_firmware_ops(const struct firmware_ops *ops) | ||
60 | { | ||
61 | BUG_ON(!ops); | ||
62 | |||
63 | firmware_ops = ops; | ||
64 | } | ||
65 | |||
66 | #endif | ||