1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Trusted Foundations support for ARM CPUs |
4 | * |
5 | * Copyright (c) 2013, NVIDIA Corporation. |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/init.h> |
10 | #include <linux/of.h> |
11 | |
12 | #include <linux/firmware/trusted_foundations.h> |
13 | |
14 | #include <asm/firmware.h> |
15 | #include <asm/hardware/cache-l2x0.h> |
16 | #include <asm/outercache.h> |
17 | |
18 | #define TF_CACHE_MAINT 0xfffff100 |
19 | |
20 | #define TF_CACHE_ENABLE 1 |
21 | #define TF_CACHE_DISABLE 2 |
22 | #define TF_CACHE_REENABLE 4 |
23 | |
24 | #define TF_SET_CPU_BOOT_ADDR_SMC 0xfffff200 |
25 | |
26 | #define TF_CPU_PM 0xfffffffc |
27 | #define TF_CPU_PM_S3 0xffffffe3 |
28 | #define TF_CPU_PM_S2 0xffffffe6 |
29 | #define TF_CPU_PM_S2_NO_MC_CLK 0xffffffe5 |
30 | #define TF_CPU_PM_S1 0xffffffe4 |
31 | #define TF_CPU_PM_S1_NOFLUSH_L2 0xffffffe7 |
32 | |
33 | static unsigned long tf_idle_mode = TF_PM_MODE_NONE; |
34 | static unsigned long cpu_boot_addr; |
35 | |
36 | static void tf_generic_smc(u32 type, u32 arg1, u32 arg2) |
37 | { |
38 | register u32 r0 asm("r0" ) = type; |
39 | register u32 r1 asm("r1" ) = arg1; |
40 | register u32 r2 asm("r2" ) = arg2; |
41 | |
42 | asm volatile( |
43 | ".arch_extension sec\n\t" |
44 | "stmfd sp!, {r4 - r11}\n\t" |
45 | __asmeq("%0" , "r0" ) |
46 | __asmeq("%1" , "r1" ) |
47 | __asmeq("%2" , "r2" ) |
48 | "mov r3, #0\n\t" |
49 | "mov r4, #0\n\t" |
50 | "smc #0\n\t" |
51 | "ldmfd sp!, {r4 - r11}\n\t" |
52 | : |
53 | : "r" (r0), "r" (r1), "r" (r2) |
54 | : "memory" , "r3" , "r12" , "lr" ); |
55 | } |
56 | |
57 | static int tf_set_cpu_boot_addr(int cpu, unsigned long boot_addr) |
58 | { |
59 | cpu_boot_addr = boot_addr; |
60 | tf_generic_smc(TF_SET_CPU_BOOT_ADDR_SMC, arg1: cpu_boot_addr, arg2: 0); |
61 | |
62 | return 0; |
63 | } |
64 | |
65 | static int tf_prepare_idle(unsigned long mode) |
66 | { |
67 | switch (mode) { |
68 | case TF_PM_MODE_LP0: |
69 | tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S3, arg2: cpu_boot_addr); |
70 | break; |
71 | |
72 | case TF_PM_MODE_LP1: |
73 | tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S2, arg2: cpu_boot_addr); |
74 | break; |
75 | |
76 | case TF_PM_MODE_LP1_NO_MC_CLK: |
77 | tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S2_NO_MC_CLK, |
78 | arg2: cpu_boot_addr); |
79 | break; |
80 | |
81 | case TF_PM_MODE_LP2: |
82 | tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1, arg2: cpu_boot_addr); |
83 | break; |
84 | |
85 | case TF_PM_MODE_LP2_NOFLUSH_L2: |
86 | tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1_NOFLUSH_L2, |
87 | arg2: cpu_boot_addr); |
88 | break; |
89 | |
90 | case TF_PM_MODE_NONE: |
91 | break; |
92 | |
93 | default: |
94 | return -EINVAL; |
95 | } |
96 | |
97 | tf_idle_mode = mode; |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | #ifdef CONFIG_CACHE_L2X0 |
103 | static void tf_cache_write_sec(unsigned long val, unsigned int reg) |
104 | { |
105 | u32 enable_op, l2x0_way_mask = 0xff; |
106 | |
107 | switch (reg) { |
108 | case L2X0_CTRL: |
109 | if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_ASSOCIATIVITY_16) |
110 | l2x0_way_mask = 0xffff; |
111 | |
112 | switch (tf_idle_mode) { |
113 | case TF_PM_MODE_LP2: |
114 | enable_op = TF_CACHE_REENABLE; |
115 | break; |
116 | |
117 | default: |
118 | enable_op = TF_CACHE_ENABLE; |
119 | break; |
120 | } |
121 | |
122 | if (val == L2X0_CTRL_EN) |
123 | tf_generic_smc(TF_CACHE_MAINT, enable_op, |
124 | l2x0_saved_regs.aux_ctrl); |
125 | else |
126 | tf_generic_smc(TF_CACHE_MAINT, TF_CACHE_DISABLE, |
127 | l2x0_way_mask); |
128 | break; |
129 | |
130 | default: |
131 | break; |
132 | } |
133 | } |
134 | |
135 | static int tf_init_cache(void) |
136 | { |
137 | outer_cache.write_sec = tf_cache_write_sec; |
138 | |
139 | return 0; |
140 | } |
141 | #endif /* CONFIG_CACHE_L2X0 */ |
142 | |
143 | static const struct firmware_ops trusted_foundations_ops = { |
144 | .set_cpu_boot_addr = tf_set_cpu_boot_addr, |
145 | .prepare_idle = tf_prepare_idle, |
146 | #ifdef CONFIG_CACHE_L2X0 |
147 | .l2x0_init = tf_init_cache, |
148 | #endif |
149 | }; |
150 | |
151 | void register_trusted_foundations(struct trusted_foundations_platform_data *pd) |
152 | { |
153 | /* |
154 | * we are not using version information for now since currently |
155 | * supported SMCs are compatible with all TF releases |
156 | */ |
157 | register_firmware_ops(&trusted_foundations_ops); |
158 | } |
159 | |
160 | void of_register_trusted_foundations(void) |
161 | { |
162 | struct device_node *node; |
163 | struct trusted_foundations_platform_data pdata; |
164 | int err; |
165 | |
166 | node = of_find_compatible_node(NULL, NULL, compat: "tlm,trusted-foundations" ); |
167 | if (!node) |
168 | return; |
169 | |
170 | err = of_property_read_u32(np: node, propname: "tlm,version-major" , |
171 | out_value: &pdata.version_major); |
172 | if (err != 0) |
173 | panic(fmt: "Trusted Foundation: missing version-major property\n" ); |
174 | err = of_property_read_u32(np: node, propname: "tlm,version-minor" , |
175 | out_value: &pdata.version_minor); |
176 | if (err != 0) |
177 | panic(fmt: "Trusted Foundation: missing version-minor property\n" ); |
178 | register_trusted_foundations(&pdata); |
179 | } |
180 | |
181 | bool trusted_foundations_registered(void) |
182 | { |
183 | return firmware_ops == &trusted_foundations_ops; |
184 | } |
185 | |