summaryrefslogtreecommitdiffstats
path: root/drivers/firmware/trusted_foundations.c
blob: 1389fa9418a7b963e3299b26c1bd27c684c94d45 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Trusted Foundations support for ARM CPUs
 *
 * Copyright (c) 2013, NVIDIA Corporation.
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>

#include <linux/firmware/trusted_foundations.h>

#include <asm/firmware.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/outercache.h>

#define TF_CACHE_MAINT		0xfffff100

#define TF_CACHE_ENABLE		1
#define TF_CACHE_DISABLE	2
#define TF_CACHE_REENABLE	4

#define TF_SET_CPU_BOOT_ADDR_SMC 0xfffff200

#define TF_CPU_PM		0xfffffffc
#define TF_CPU_PM_S3		0xffffffe3
#define TF_CPU_PM_S2		0xffffffe6
#define TF_CPU_PM_S2_NO_MC_CLK	0xffffffe5
#define TF_CPU_PM_S1		0xffffffe4
#define TF_CPU_PM_S1_NOFLUSH_L2	0xffffffe7

static unsigned long tf_idle_mode = TF_PM_MODE_NONE;
static unsigned long cpu_boot_addr;

static void tf_generic_smc(u32 type, u32 arg1, u32 arg2)
{
	register u32 r0 asm("r0") = type;
	register u32 r1 asm("r1") = arg1;
	register u32 r2 asm("r2") = arg2;

	asm volatile(
		".arch_extension	sec\n\t"
		"stmfd	sp!, {r4 - r11}\n\t"
		__asmeq("%0", "r0")
		__asmeq("%1", "r1")
		__asmeq("%2", "r2")
		"mov	r3, #0\n\t"
		"mov	r4, #0\n\t"
		"smc	#0\n\t"
		"ldmfd	sp!, {r4 - r11}\n\t"
		:
		: "r" (r0), "r" (r1), "r" (r2)
		: "memory", "r3", "r12", "lr");
}

static int tf_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
{
	cpu_boot_addr = boot_addr;
	tf_generic_smc(TF_SET_CPU_BOOT_ADDR_SMC, cpu_boot_addr, 0);

	return 0;
}

static int tf_prepare_idle(unsigned long mode)
{
	switch (mode) {
	case TF_PM_MODE_LP0:
		tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S3, cpu_boot_addr);
		break;

	case TF_PM_MODE_LP1:
		tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S2, cpu_boot_addr);
		break;

	case TF_PM_MODE_LP1_NO_MC_CLK:
		tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S2_NO_MC_CLK,
			       cpu_boot_addr);
		break;

	case TF_PM_MODE_LP2:
		tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1, cpu_boot_addr);
		break;

	case TF_PM_MODE_LP2_NOFLUSH_L2:
		tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1_NOFLUSH_L2,
			       cpu_boot_addr);
		break;

	case TF_PM_MODE_NONE:
		break;

	default:
		return -EINVAL;
	}

	tf_idle_mode = mode;

	return 0;
}

#ifdef CONFIG_CACHE_L2X0
static void tf_cache_write_sec(unsigned long val, unsigned int reg)
{
	u32 enable_op, l2x0_way_mask = 0xff;

	switch (reg) {
	case L2X0_CTRL:
		if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_ASSOCIATIVITY_16)
			l2x0_way_mask = 0xffff;

		switch (tf_idle_mode) {
		case TF_PM_MODE_LP2:
			enable_op = TF_CACHE_REENABLE;
			break;

		default:
			enable_op = TF_CACHE_ENABLE;
			break;
		}

		if (val == L2X0_CTRL_EN)
			tf_generic_smc(TF_CACHE_MAINT, enable_op,
				       l2x0_saved_regs.aux_ctrl);
		else
			tf_generic_smc(TF_CACHE_MAINT, TF_CACHE_DISABLE,
				       l2x0_way_mask);
		break;

	default:
		break;
	}
}

static int tf_init_cache(void)
{
	outer_cache.write_sec = tf_cache_write_sec;

	return 0;
}
#endif /* CONFIG_CACHE_L2X0 */

static const struct firmware_ops trusted_foundations_ops = {
	.set_cpu_boot_addr = tf_set_cpu_boot_addr,
	.prepare_idle = tf_prepare_idle,
#ifdef CONFIG_CACHE_L2X0
	.l2x0_init = tf_init_cache,
#endif
};

void register_trusted_foundations(struct trusted_foundations_platform_data *pd)
{
	/*
	 * we are not using version information for now since currently
	 * supported SMCs are compatible with all TF releases
	 */
	register_firmware_ops(&trusted_foundations_ops);
}

void of_register_trusted_foundations(void)
{
	struct device_node *node;
	struct trusted_foundations_platform_data pdata;
	int err;

	node = of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations");
	if (!node)
		return;

	err = of_property_read_u32(node, "tlm,version-major",
				   &pdata.version_major);
	if (err != 0)
		panic("Trusted Foundation: missing version-major property\n");
	err = of_property_read_u32(node, "tlm,version-minor",
				   &pdata.version_minor);
	if (err != 0)
		panic("Trusted Foundation: missing version-minor property\n");
	register_trusted_foundations(&pdata);
}

bool trusted_foundations_registered(void)
{
	return firmware_ops == &trusted_foundations_ops;
}