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