xref: /optee_os/core/drivers/atmel_tcb.c (revision 024af21c74ff20e008a57bc78d4728b2bd76f359)
1d922c314SClément Léger // SPDX-License-Identifier: BSD-2-Clause
2d922c314SClément Léger /*
3d922c314SClément Léger  * Copyright (c) 2022, Microchip
4d922c314SClément Léger  */
5d922c314SClément Léger 
6d922c314SClément Léger #include <assert.h>
7d922c314SClément Léger #include <drivers/clk.h>
8d922c314SClément Léger #include <drivers/clk_dt.h>
9d922c314SClément Léger #include <io.h>
10d922c314SClément Léger #include <kernel/boot.h>
11d922c314SClément Léger #include <kernel/time_source.h>
12d922c314SClément Léger #include <libfdt.h>
13d922c314SClément Léger #include <matrix.h>
146370f75dSTony Han #include <platform_config.h>
15d922c314SClément Léger #include <tee_api_defines.h>
16d922c314SClément Léger 
17d922c314SClément Léger #define TCB_CHAN(chan)		((chan) * 0x40)
18d922c314SClément Léger 
19d922c314SClément Léger #define TCB_CCR(chan)		(0x0 + TCB_CHAN(chan))
20d922c314SClément Léger #define  TCB_CCR_SWTRG		0x4
21d922c314SClément Léger #define  TCB_CCR_CLKEN		0x1
22d922c314SClément Léger 
23d922c314SClément Léger #define TCB_CMR(chan)		(0x4 + TCB_CHAN(chan))
24d922c314SClément Léger #define  TCB_CMR_WAVE		BIT32(15)
25d922c314SClément Léger #define  TCB_CMR_TIMER_CLOCK5	4
26d922c314SClément Léger #define  TCB_CMR_XC1		6
27d922c314SClément Léger #define  TCB_CMR_ACPA_SET	BIT32(16)
28d922c314SClément Léger #define  TCB_CMR_ACPC_CLEAR	SHIFT_U32(2, 18)
29d922c314SClément Léger 
30d922c314SClément Léger #define TCB_CV(chan)		(0x10 + TCB_CHAN(chan))
31d922c314SClément Léger 
32d922c314SClément Léger #define TCB_RA(chan)		(0x14 + TCB_CHAN(chan))
33d922c314SClément Léger #define TCB_RB(chan)		(0x18 + TCB_CHAN(chan))
34d922c314SClément Léger #define TCB_RC(chan)		(0x1c + TCB_CHAN(chan))
35d922c314SClément Léger 
36d922c314SClément Léger #define TCB_IER(chan)		(0x24 + TCB_CHAN(chan))
37d922c314SClément Léger #define  TCB_IER_COVFS		0x1
38d922c314SClément Léger 
39d922c314SClément Léger #define TCB_SR(chan)		(0x20 + TCB_CHAN(chan))
40d922c314SClément Léger #define  TCB_SR_COVFS		0x1
41d922c314SClément Léger 
42d922c314SClément Léger #define TCB_IDR(chan)		(0x28 + TCB_CHAN(chan))
43d922c314SClément Léger 
44d922c314SClément Léger #define TCB_BCR			0xc0
45d922c314SClément Léger #define  TCB_BCR_SYNC		0x1
46d922c314SClément Léger 
47d922c314SClément Léger #define TCB_BMR			0xc4
48d922c314SClément Léger #define  TCB_BMR_TC1XC1S_TIOA0	SHIFT_U32(2, 2)
49d922c314SClément Léger 
50d922c314SClément Léger #define TCB_WPMR		0xe4
51d922c314SClément Léger #define  TCB_WPMR_WAKEY		0x54494d
52d922c314SClément Léger 
53*024af21cSTony Han #ifdef CFG_SAMA7G5
54*024af21cSTony Han static const char * const tcb_clocks[] = {
55*024af21cSTony Han 	"t0_clk", "t1_clk", "t2_clk", "slow_clk"};
56*024af21cSTony Han #else
57d922c314SClément Léger static const char * const tcb_clocks[] = { "t0_clk", "gclk", "slow_clk" };
58*024af21cSTony Han #endif
59d922c314SClément Léger static vaddr_t tcb_base;
60d922c314SClément Léger static uint32_t tcb_rate;
61d922c314SClément Léger 
62d922c314SClément Léger static TEE_Result atmel_tcb_enable_clocks(const void *fdt, int node)
63d922c314SClément Léger {
64d922c314SClément Léger 	unsigned int i = 0;
65d922c314SClément Léger 	struct clk *clk = NULL;
66d922c314SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
67d922c314SClément Léger 
68d922c314SClément Léger 	for (i = 0; i < ARRAY_SIZE(tcb_clocks); i++) {
69d922c314SClément Léger 		res = clk_dt_get_by_name(fdt, node, tcb_clocks[i], &clk);
70d922c314SClément Léger 		if (res)
71d922c314SClément Léger 			return res;
72d922c314SClément Léger 
73d922c314SClément Léger 		clk_enable(clk);
74d922c314SClément Léger 	}
75d922c314SClément Léger 
76d922c314SClément Léger 	return TEE_SUCCESS;
77d922c314SClément Léger }
78d922c314SClément Léger 
79d922c314SClément Léger static TEE_Result atmel_tcb_get_sys_time(TEE_Time *time)
80d922c314SClément Léger {
81d922c314SClément Léger 	uint64_t cv0 = 0;
82d922c314SClément Léger 	uint64_t cv1 = 0;
83d922c314SClément Léger 
84d922c314SClément Léger 	if (!tcb_base)
85d922c314SClément Léger 		return TEE_ERROR_BAD_STATE;
86d922c314SClément Léger 
87d922c314SClément Léger 	do {
88d922c314SClément Léger 		cv1 = io_read32(tcb_base + TCB_CV(1));
89d922c314SClément Léger 		cv0 = io_read32(tcb_base + TCB_CV(0));
90d922c314SClément Léger 	} while (io_read32(tcb_base + TCB_CV(1)) != cv1);
91d922c314SClément Léger 
92d922c314SClément Léger 	cv0 |= cv1 << 32;
93d922c314SClément Léger 
94d922c314SClément Léger 	time->seconds = cv0 / tcb_rate;
95d922c314SClément Léger 	time->millis = (cv0 % tcb_rate) / (tcb_rate / TEE_TIME_MILLIS_BASE);
96d922c314SClément Léger 
97d922c314SClément Léger 	return TEE_SUCCESS;
98d922c314SClément Léger }
99d922c314SClément Léger 
100d922c314SClément Léger static const struct time_source atmel_tcb_time_source = {
101d922c314SClément Léger 	.name = "atmel_tcb",
102d922c314SClément Léger 	.protection_level = 1000,
103d922c314SClément Léger 	.get_sys_time = atmel_tcb_get_sys_time,
104d922c314SClément Léger };
105d922c314SClément Léger 
106d922c314SClément Léger REGISTER_TIME_SOURCE(atmel_tcb_time_source)
107d922c314SClément Léger 
108d922c314SClément Léger static void atmel_tcb_configure(void)
109d922c314SClément Léger {
110d922c314SClément Léger 	/* Disable write protection */
111d922c314SClément Léger 	io_write32(tcb_base + TCB_WPMR, TCB_WPMR_WAKEY);
112d922c314SClément Léger 
113d922c314SClément Léger 	/* Disable all irqs for both channel 0 & 1 */
114d922c314SClément Léger 	io_write32(tcb_base + TCB_IDR(0), 0xff);
115d922c314SClément Léger 	io_write32(tcb_base + TCB_IDR(1), 0xff);
116d922c314SClément Léger 
117d922c314SClément Léger 	/*
118d922c314SClément Léger 	 * In order to avoid wrapping, use a 64 bit counter by chaining
119d922c314SClément Léger 	 * two channels. We use the slow_clk which runs at 32K and is sufficient
120d922c314SClément Léger 	 * for the millisecond precision, this will wrap in approximately
121d922c314SClément Léger 	 * 17851025 years so no worries here.
122d922c314SClément Léger 	 *
123d922c314SClément Léger 	 * Channel 0 is configured to generate a clock on TIOA0 which is cleared
124d922c314SClément Léger 	 * when reaching 0x80000000 and set when reaching 0.
125d922c314SClément Léger 	 */
126d922c314SClément Léger 	io_write32(tcb_base + TCB_CMR(0),
127d922c314SClément Léger 		   TCB_CMR_TIMER_CLOCK5 | TCB_CMR_WAVE | TCB_CMR_ACPA_SET |
128d922c314SClément Léger 		   TCB_CMR_ACPC_CLEAR);
129d922c314SClément Léger 	io_write32(tcb_base + TCB_RC(0), 0x80000000);
130d922c314SClément Léger 	io_write32(tcb_base + TCB_RA(0), 0x1);
131d922c314SClément Léger 	io_write32(tcb_base + TCB_CCR(0), TCB_CCR_CLKEN);
132d922c314SClément Léger 
133d922c314SClément Léger 	/* Channel 1 is configured to use TIOA0 as input */
134d922c314SClément Léger 	io_write32(tcb_base + TCB_CMR(1), TCB_CMR_XC1 | TCB_CMR_WAVE);
135d922c314SClément Léger 	io_write32(tcb_base + TCB_CCR(1), TCB_CCR_CLKEN);
136d922c314SClément Léger 
137d922c314SClément Léger 	/* Set XC1 input to be TIOA0 (ie output of Channel 0) */
138d922c314SClément Léger 	io_write32(tcb_base + TCB_BMR, TCB_BMR_TC1XC1S_TIOA0);
139d922c314SClément Léger 
140d922c314SClément Léger 	/* Sync & start all timers */
141d922c314SClément Léger 	io_write32(tcb_base + TCB_BCR, TCB_BCR_SYNC);
142d922c314SClément Léger 
143d922c314SClément Léger 	/* Enable write protection */
144d922c314SClément Léger 	io_write32(tcb_base + TCB_WPMR, TCB_WPMR_WAKEY | 1);
145d922c314SClément Léger }
146d922c314SClément Léger 
147d922c314SClément Léger static TEE_Result atmel_tcb_check(void)
148d922c314SClément Léger {
149d922c314SClément Léger 	if (!tcb_base)
150d922c314SClément Léger 		panic("Missing TCB base ! Check the device-tree");
151d922c314SClément Léger 
152d922c314SClément Léger 	return TEE_SUCCESS;
153d922c314SClément Léger }
154d922c314SClément Léger 
155d922c314SClément Léger boot_final(atmel_tcb_check);
156d922c314SClément Léger 
157d922c314SClément Léger static TEE_Result atmel_tcb_probe(const void *fdt, int node,
158d922c314SClément Léger 				  const void *compat_data __unused)
159d922c314SClément Léger {
160d922c314SClément Léger 	size_t size = 0;
161d922c314SClément Léger 	struct clk *clk = NULL;
162d922c314SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
163d922c314SClément Léger 	unsigned int peri_id = AT91C_ID_TC0;
164d922c314SClément Léger 
165d922c314SClément Léger 	/* Enable all TCB clocks */
166d922c314SClément Léger 	res = atmel_tcb_enable_clocks(fdt, node);
167d922c314SClément Léger 	if (res)
168d922c314SClément Léger 		return res;
169d922c314SClément Léger 
170d922c314SClément Léger 	if (tcb_base)
171d922c314SClément Léger 		return TEE_SUCCESS;
172d922c314SClément Léger 
173f354a5d8SGatien Chevallier 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
174d922c314SClément Léger 		return TEE_SUCCESS;
175d922c314SClément Léger 
176d922c314SClément Léger 	res = clk_dt_get_by_name(fdt, node, "slow_clk", &clk);
177d922c314SClément Léger 	if (res)
178d922c314SClément Léger 		return res;
179d922c314SClément Léger 
180442c670aSClément Léger 	res = matrix_dt_get_id(fdt, node, &peri_id);
181442c670aSClément Léger 	if (res)
182442c670aSClément Léger 		return res;
183442c670aSClément Léger 
184a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, node, &tcb_base, &size, DT_MAP_AUTO) < 0)
185d922c314SClément Léger 		return TEE_ERROR_GENERIC;
186d922c314SClément Léger 
187d922c314SClément Léger 	matrix_configure_periph_secure(peri_id);
188d922c314SClément Léger 
189d922c314SClément Léger 	tcb_rate = clk_get_rate(clk);
190d922c314SClément Léger 	assert(tcb_rate);
191d922c314SClément Léger 
192d922c314SClément Léger 	atmel_tcb_configure();
193d922c314SClément Léger 
194d922c314SClément Léger 	return TEE_SUCCESS;
195d922c314SClément Léger }
196d922c314SClément Léger 
197d922c314SClément Léger static const struct dt_device_match atmel_tcb_match_table[] = {
198d922c314SClément Léger 	{ .compatible = "atmel,sama5d2-tcb" },
199d922c314SClément Léger 	{ }
200d922c314SClément Léger };
201d922c314SClément Léger 
202d922c314SClément Léger DEFINE_DT_DRIVER(atmel_tcb_dt_driver) = {
203d922c314SClément Léger 	.name = "atmel_tcb",
204d922c314SClément Léger 	.type = DT_DRIVER_NOTYPE,
205d922c314SClément Léger 	.match_table = atmel_tcb_match_table,
206d922c314SClément Léger 	.probe = atmel_tcb_probe,
207d922c314SClément Léger };
208