xref: /optee_os/core/drivers/amd/asu/asu_main.c (revision 7f2d4e10736f698f6b7739a9cd39e64d96c98a0a)
1*7f2d4e10SAkshay Belsare // SPDX-License-Identifier: BSD-2-Clause
2*7f2d4e10SAkshay Belsare /*
3*7f2d4e10SAkshay Belsare  * Copyright (c) 2025-2026, Advanced Micro Devices, Inc. All rights reserved.
4*7f2d4e10SAkshay Belsare  *
5*7f2d4e10SAkshay Belsare  */
6*7f2d4e10SAkshay Belsare 
7*7f2d4e10SAkshay Belsare #include <assert.h>
8*7f2d4e10SAkshay Belsare #include <drivers/amd/asu_client.h>
9*7f2d4e10SAkshay Belsare #include <initcall.h>
10*7f2d4e10SAkshay Belsare #include <io.h>
11*7f2d4e10SAkshay Belsare #include <kernel/delay.h>
12*7f2d4e10SAkshay Belsare #include <kernel/dt.h>
13*7f2d4e10SAkshay Belsare #include <kernel/interrupt.h>
14*7f2d4e10SAkshay Belsare #include <kernel/panic.h>
15*7f2d4e10SAkshay Belsare #include <kernel/spinlock.h>
16*7f2d4e10SAkshay Belsare #include <libfdt.h>
17*7f2d4e10SAkshay Belsare #include <mm/core_mmu.h>
18*7f2d4e10SAkshay Belsare #include <stdbool.h>
19*7f2d4e10SAkshay Belsare #include <stdint.h>
20*7f2d4e10SAkshay Belsare #include <string.h>
21*7f2d4e10SAkshay Belsare #include <trace.h>
22*7f2d4e10SAkshay Belsare #include <util.h>
23*7f2d4e10SAkshay Belsare 
24*7f2d4e10SAkshay Belsare #include "asu_doorbell.h"
25*7f2d4e10SAkshay Belsare 
26*7f2d4e10SAkshay Belsare #define ASU_QUEUE_BUFFER_FULL		0xFFU
27*7f2d4e10SAkshay Belsare #define ASU_CLIENT_READY		0xFFFFFFFFU
28*7f2d4e10SAkshay Belsare #define ASU_TARGET_IPI_INT_MASK		1U
29*7f2d4e10SAkshay Belsare 
30*7f2d4e10SAkshay Belsare #define ASU_BASEADDR			0xEBF80000U
31*7f2d4e10SAkshay Belsare #define ASU_GLOBAL_CNTRL		(ASU_BASEADDR + 0x00000000U)
32*7f2d4e10SAkshay Belsare 
33*7f2d4e10SAkshay Belsare #define ASU_BASEADDR_SIZE		0x10000U
34*7f2d4e10SAkshay Belsare #define ASU_GLOBAL_ADDR_LIMIT		0x1000U
35*7f2d4e10SAkshay Belsare 
36*7f2d4e10SAkshay Belsare #define ASU_GLOBAL_CNTRL_FW_IS_PRESENT_MASK	0x10U
37*7f2d4e10SAkshay Belsare #define ASU_ASUFW_BIT_CHECK_TIMEOUT_VALUE	0xFFFFFU
38*7f2d4e10SAkshay Belsare 
39*7f2d4e10SAkshay Belsare #define ASU_CHNL_IPI_BITMASK		GENMASK_32(31, 16)
40*7f2d4e10SAkshay Belsare 
41*7f2d4e10SAkshay Belsare struct asu_client {
42*7f2d4e10SAkshay Belsare 	struct asu_channel_memory *chnl_memptr;
43*7f2d4e10SAkshay Belsare 	uint32_t is_ready;
44*7f2d4e10SAkshay Belsare 	unsigned int slock;          /* chnl_memptr spin lock */
45*7f2d4e10SAkshay Belsare 	void *global_ctrl;
46*7f2d4e10SAkshay Belsare 	void *doorbell;
47*7f2d4e10SAkshay Belsare };
48*7f2d4e10SAkshay Belsare 
49*7f2d4e10SAkshay Belsare struct asu_ids {
50*7f2d4e10SAkshay Belsare 	uint8_t ids[ASU_UNIQUE_ID_MAX];
51*7f2d4e10SAkshay Belsare 	unsigned int slock;          /* id array spin lock */
52*7f2d4e10SAkshay Belsare };
53*7f2d4e10SAkshay Belsare 
54*7f2d4e10SAkshay Belsare static struct asu_ids asuid;
55*7f2d4e10SAkshay Belsare static struct asu_client *asu;
56*7f2d4e10SAkshay Belsare 
57*7f2d4e10SAkshay Belsare /*
58*7f2d4e10SAkshay Belsare  * asu_fwcheck() - Check if ASU firmware is present and ready
59*7f2d4e10SAkshay Belsare  * Polls the ASU global control register to verify if the HSM firmware
60*7f2d4e10SAkshay Belsare  * is present and ready for interaction. Uses a timeout of approximately
61*7f2d4e10SAkshay Belsare  * 1 second for the check.
62*7f2d4e10SAkshay Belsare  *
63*7f2d4e10SAkshay Belsare  * Return: TEE_SUCCESS if firmware is ready, TEE_ERROR_BAD_STATE if not present
64*7f2d4e10SAkshay Belsare  */
65*7f2d4e10SAkshay Belsare static TEE_Result asu_fwcheck(void)
66*7f2d4e10SAkshay Belsare {
67*7f2d4e10SAkshay Belsare 	uint64_t timeout = 0;
68*7f2d4e10SAkshay Belsare 
69*7f2d4e10SAkshay Belsare 	/*
70*7f2d4e10SAkshay Belsare 	 * Timeout is set to ~1sec.
71*7f2d4e10SAkshay Belsare 	 * This is the worst case time within which ASUFW ready for interaction
72*7f2d4e10SAkshay Belsare 	 * with components requests.
73*7f2d4e10SAkshay Belsare 	 */
74*7f2d4e10SAkshay Belsare 	timeout = timeout_init_us(ASU_ASUFW_BIT_CHECK_TIMEOUT_VALUE);
75*7f2d4e10SAkshay Belsare 
76*7f2d4e10SAkshay Belsare 	do {
77*7f2d4e10SAkshay Belsare 		if (io_read32((vaddr_t)asu->global_ctrl) &
78*7f2d4e10SAkshay Belsare 		    ASU_GLOBAL_CNTRL_FW_IS_PRESENT_MASK) {
79*7f2d4e10SAkshay Belsare 			DMSG("ASU FW is ready!");
80*7f2d4e10SAkshay Belsare 			return TEE_SUCCESS;
81*7f2d4e10SAkshay Belsare 		}
82*7f2d4e10SAkshay Belsare 	} while (!timeout_elapsed(timeout));
83*7f2d4e10SAkshay Belsare 
84*7f2d4e10SAkshay Belsare 	EMSG("ASU FW is not present!");
85*7f2d4e10SAkshay Belsare 
86*7f2d4e10SAkshay Belsare 	return TEE_ERROR_BAD_STATE;
87*7f2d4e10SAkshay Belsare }
88*7f2d4e10SAkshay Belsare 
89*7f2d4e10SAkshay Belsare /*
90*7f2d4e10SAkshay Belsare  * asu_get_channelID() - Determine the ASU channel ID for APU communication
91*7f2d4e10SAkshay Belsare  *
92*7f2d4e10SAkshay Belsare  * Maps the Runtime Configuration Area (RTCA) to find which ASU channel
93*7f2d4e10SAkshay Belsare  * should be used by the APU for communication. Searches through available
94*7f2d4e10SAkshay Belsare  * channels to find one matching the APU's local IPI ID.
95*7f2d4e10SAkshay Belsare  *
96*7f2d4e10SAkshay Belsare  * Return: Channel ID (0 to ASU_MAX_IPI_CHANNELS-1) or ASU_MAX_IPI_CHANNELS on
97*7f2d4e10SAkshay Belsare  *	   failure
98*7f2d4e10SAkshay Belsare  */
99*7f2d4e10SAkshay Belsare static uint32_t asu_get_channelID(void)
100*7f2d4e10SAkshay Belsare {
101*7f2d4e10SAkshay Belsare 	TEE_Result ret = TEE_ERROR_GENERIC;
102*7f2d4e10SAkshay Belsare 	void *comm_chnl_info = NULL;
103*7f2d4e10SAkshay Belsare 	uint32_t channel_id = ASU_MAX_IPI_CHANNELS;
104*7f2d4e10SAkshay Belsare 	vaddr_t membase = 0;
105*7f2d4e10SAkshay Belsare 	uint32_t id = 0;
106*7f2d4e10SAkshay Belsare 
107*7f2d4e10SAkshay Belsare 	/*
108*7f2d4e10SAkshay Belsare 	 * RTCA is mapped only to find the ASU Channel ID
109*7f2d4e10SAkshay Belsare 	 * to be used by APU.
110*7f2d4e10SAkshay Belsare 	 * After reading the information RTCA region is unmapped.
111*7f2d4e10SAkshay Belsare 	 */
112*7f2d4e10SAkshay Belsare 	comm_chnl_info = core_mmu_add_mapping(MEM_AREA_IO_SEC,
113*7f2d4e10SAkshay Belsare 					      ASU_RTCA_BASEADDR,
114*7f2d4e10SAkshay Belsare 					      ASU_GLOBAL_ADDR_LIMIT);
115*7f2d4e10SAkshay Belsare 	if (!comm_chnl_info) {
116*7f2d4e10SAkshay Belsare 		EMSG("Failed to map runtime config area");
117*7f2d4e10SAkshay Belsare 		return channel_id;
118*7f2d4e10SAkshay Belsare 	}
119*7f2d4e10SAkshay Belsare 
120*7f2d4e10SAkshay Belsare 	for (id = 0; id < ASU_MAX_IPI_CHANNELS; id++) {
121*7f2d4e10SAkshay Belsare 		membase = (vaddr_t)comm_chnl_info +
122*7f2d4e10SAkshay Belsare 			  ASU_RTCA_CHANNEL_BASE_OFFSET +
123*7f2d4e10SAkshay Belsare 			  ASU_RTCA_CHANNEL_INFO_LEN * id;
124*7f2d4e10SAkshay Belsare 		if ((io_read32(membase) & ASU_CHNL_IPI_BITMASK) ==
125*7f2d4e10SAkshay Belsare 		    (CFG_AMD_APU_LCL_IPI_ID << 16)) {
126*7f2d4e10SAkshay Belsare 			channel_id = id;
127*7f2d4e10SAkshay Belsare 			DMSG("Use ASU channel ID %"PRIu32, channel_id);
128*7f2d4e10SAkshay Belsare 			break;
129*7f2d4e10SAkshay Belsare 		}
130*7f2d4e10SAkshay Belsare 	}
131*7f2d4e10SAkshay Belsare 
132*7f2d4e10SAkshay Belsare 	if (channel_id == ASU_MAX_IPI_CHANNELS)
133*7f2d4e10SAkshay Belsare 		EMSG("Failed to identify ASU channel ID for APU");
134*7f2d4e10SAkshay Belsare 
135*7f2d4e10SAkshay Belsare 	ret = core_mmu_remove_mapping(MEM_AREA_IO_SEC,
136*7f2d4e10SAkshay Belsare 				      comm_chnl_info, ASU_GLOBAL_ADDR_LIMIT);
137*7f2d4e10SAkshay Belsare 	if (ret)
138*7f2d4e10SAkshay Belsare 		EMSG("Failed to unmap RTCA");
139*7f2d4e10SAkshay Belsare 
140*7f2d4e10SAkshay Belsare 	return channel_id;
141*7f2d4e10SAkshay Belsare }
142*7f2d4e10SAkshay Belsare 
143*7f2d4e10SAkshay Belsare /*
144*7f2d4e10SAkshay Belsare  * asu_alloc_unique_id() - Generate a unique identifier for ASU operations
145*7f2d4e10SAkshay Belsare  *
146*7f2d4e10SAkshay Belsare  * Creates a unique ID by cycling through available IDs in the callback
147*7f2d4e10SAkshay Belsare  * reference array. Ensures no ID collision by checking if the slot is
148*7f2d4e10SAkshay Belsare  * already in use.
149*7f2d4e10SAkshay Belsare  *
150*7f2d4e10SAkshay Belsare  * Return: Unique ID (1 to ASU_UNIQUE_ID_MAX-1) or ASU_UNIQUE_ID_MAX if none
151*7f2d4e10SAkshay Belsare  *	   available
152*7f2d4e10SAkshay Belsare  */
153*7f2d4e10SAkshay Belsare uint8_t asu_alloc_unique_id(void)
154*7f2d4e10SAkshay Belsare {
155*7f2d4e10SAkshay Belsare 	uint8_t unqid = 0;
156*7f2d4e10SAkshay Belsare 	uint32_t state = 0;
157*7f2d4e10SAkshay Belsare 
158*7f2d4e10SAkshay Belsare 	state = cpu_spin_lock_xsave(&asuid.slock);
159*7f2d4e10SAkshay Belsare 	while (unqid < ASU_UNIQUE_ID_MAX) {
160*7f2d4e10SAkshay Belsare 		if (asuid.ids[unqid] == ASU_UNIQUE_ID_MAX) {
161*7f2d4e10SAkshay Belsare 			asuid.ids[unqid] = unqid;
162*7f2d4e10SAkshay Belsare 			DMSG("Got unique ID %"PRIu8, unqid);
163*7f2d4e10SAkshay Belsare 			break;
164*7f2d4e10SAkshay Belsare 		}
165*7f2d4e10SAkshay Belsare 		unqid++;
166*7f2d4e10SAkshay Belsare 	};
167*7f2d4e10SAkshay Belsare 	cpu_spin_unlock_xrestore(&asuid.slock, state);
168*7f2d4e10SAkshay Belsare 
169*7f2d4e10SAkshay Belsare 	return unqid;
170*7f2d4e10SAkshay Belsare }
171*7f2d4e10SAkshay Belsare 
172*7f2d4e10SAkshay Belsare /**
173*7f2d4e10SAkshay Belsare  * asu_free_unique_id() - Release a previously allocated unique ID
174*7f2d4e10SAkshay Belsare  * @uniqueid: The unique ID to be freed
175*7f2d4e10SAkshay Belsare  *
176*7f2d4e10SAkshay Belsare  * Marks the specified unique ID as available for reuse.
177*7f2d4e10SAkshay Belsare  * The released ID is set to ASU_UNIQUE_ID_MAX
178*7f2d4e10SAkshay Belsare  * to indicate its availability. Intended to be used in environments where
179*7f2d4e10SAkshay Belsare  * concurrent access to the unique ID pool occurs.
180*7f2d4e10SAkshay Belsare  */
181*7f2d4e10SAkshay Belsare void asu_free_unique_id(uint8_t uniqueid)
182*7f2d4e10SAkshay Belsare {
183*7f2d4e10SAkshay Belsare 	uint32_t state = 0;
184*7f2d4e10SAkshay Belsare 
185*7f2d4e10SAkshay Belsare 	state = cpu_spin_lock_xsave(&asuid.slock);
186*7f2d4e10SAkshay Belsare 	asuid.ids[uniqueid] = ASU_UNIQUE_ID_MAX;
187*7f2d4e10SAkshay Belsare 	cpu_spin_unlock_xrestore(&asuid.slock, state);
188*7f2d4e10SAkshay Belsare }
189*7f2d4e10SAkshay Belsare 
190*7f2d4e10SAkshay Belsare /*
191*7f2d4e10SAkshay Belsare  * get_free_index() - Find a free buffer index in the specified priority queue
192*7f2d4e10SAkshay Belsare  * @priority: Priority level (ASU_PRIORITY_HIGH or ASU_PRIORITY_LOW)
193*7f2d4e10SAkshay Belsare  *
194*7f2d4e10SAkshay Belsare  * Searches for an available buffer slot in either the high priority (P0) or
195*7f2d4e10SAkshay Belsare  * low priority (P1) channel queue. Updates the next free index pointer.
196*7f2d4e10SAkshay Belsare  *
197*7f2d4e10SAkshay Belsare  * Return: Buffer index (0 to ASU_MAX_BUFFERS-1) or ASU_MAX_BUFFERS if queue
198*7f2d4e10SAkshay Belsare  *	   is full
199*7f2d4e10SAkshay Belsare  */
200*7f2d4e10SAkshay Belsare static uint8_t get_free_index(uint8_t priority)
201*7f2d4e10SAkshay Belsare {
202*7f2d4e10SAkshay Belsare 	struct asu_channel_queue *qptr = NULL;
203*7f2d4e10SAkshay Belsare 	uint8_t index = 0;
204*7f2d4e10SAkshay Belsare 	uint32_t state = 0;
205*7f2d4e10SAkshay Belsare 
206*7f2d4e10SAkshay Belsare 	state = cpu_spin_lock_xsave(&asu->slock);
207*7f2d4e10SAkshay Belsare 
208*7f2d4e10SAkshay Belsare 	if (priority == ASU_PRIORITY_HIGH)
209*7f2d4e10SAkshay Belsare 		qptr = &asu->chnl_memptr->p0_chnl_q;
210*7f2d4e10SAkshay Belsare 	else
211*7f2d4e10SAkshay Belsare 		qptr = &asu->chnl_memptr->p1_chnl_q;
212*7f2d4e10SAkshay Belsare 
213*7f2d4e10SAkshay Belsare 	while (index < ASU_MAX_BUFFERS) {
214*7f2d4e10SAkshay Belsare 		if (qptr->queue_bufs[index].reqbufstatus == 0U ||
215*7f2d4e10SAkshay Belsare 		    qptr->queue_bufs[index].respbufstatus == 0U)
216*7f2d4e10SAkshay Belsare 			break;
217*7f2d4e10SAkshay Belsare 
218*7f2d4e10SAkshay Belsare 		index++;
219*7f2d4e10SAkshay Belsare 	}
220*7f2d4e10SAkshay Belsare 
221*7f2d4e10SAkshay Belsare 	if (index < ASU_MAX_BUFFERS)
222*7f2d4e10SAkshay Belsare 		qptr->queue_bufs[index].reqbufstatus = ASU_COMMAND_IS_PRESENT;
223*7f2d4e10SAkshay Belsare 
224*7f2d4e10SAkshay Belsare 	cpu_spin_unlock_xrestore(&asu->slock, state);
225*7f2d4e10SAkshay Belsare 
226*7f2d4e10SAkshay Belsare 	return index;
227*7f2d4e10SAkshay Belsare }
228*7f2d4e10SAkshay Belsare 
229*7f2d4e10SAkshay Belsare static void put_free_index(struct asu_channel_queue_buf *bufptr)
230*7f2d4e10SAkshay Belsare {
231*7f2d4e10SAkshay Belsare 	uint32_t state = 0;
232*7f2d4e10SAkshay Belsare 
233*7f2d4e10SAkshay Belsare 	state = cpu_spin_lock_xsave(&asu->slock);
234*7f2d4e10SAkshay Belsare 	bufptr->reqbufstatus = 0;
235*7f2d4e10SAkshay Belsare 	bufptr->respbufstatus = 0;
236*7f2d4e10SAkshay Belsare 	cpu_spin_unlock_xrestore(&asu->slock, state);
237*7f2d4e10SAkshay Belsare }
238*7f2d4e10SAkshay Belsare 
239*7f2d4e10SAkshay Belsare /*
240*7f2d4e10SAkshay Belsare  * send_doorbell() - Send IPI doorbell interrupt to ASU
241*7f2d4e10SAkshay Belsare  *
242*7f2d4e10SAkshay Belsare  * Triggers an Inter-Processor Interrupt (IPI) to notify the ASU
243*7f2d4e10SAkshay Belsare  * that a new command is available in the shared memory queue.
244*7f2d4e10SAkshay Belsare  *
245*7f2d4e10SAkshay Belsare  * Return: TEE_SUCCESS on successful doorbell trigger
246*7f2d4e10SAkshay Belsare  */
247*7f2d4e10SAkshay Belsare static TEE_Result send_doorbell(void)
248*7f2d4e10SAkshay Belsare {
249*7f2d4e10SAkshay Belsare 	io_write32((vaddr_t)asu->doorbell + IPIPSU_TRIG_OFFSET,
250*7f2d4e10SAkshay Belsare 		   ASU_TARGET_IPI_INT_MASK);
251*7f2d4e10SAkshay Belsare 
252*7f2d4e10SAkshay Belsare 	return TEE_SUCCESS;
253*7f2d4e10SAkshay Belsare }
254*7f2d4e10SAkshay Belsare 
255*7f2d4e10SAkshay Belsare /*
256*7f2d4e10SAkshay Belsare  * asu_update_queue_buffer_n_send_ipi() - Queue command and send IPI
257*7f2d4e10SAkshay Belsare  * @param: Client parameters including priority
258*7f2d4e10SAkshay Belsare  * @req_buffer: Request buffer containing command data
259*7f2d4e10SAkshay Belsare  * @size: Size of request data
260*7f2d4e10SAkshay Belsare  * @header: Command header information
261*7f2d4e10SAkshay Belsare  * @status: FW return status
262*7f2d4e10SAkshay Belsare  *
263*7f2d4e10SAkshay Belsare  * Places a command in the appropriate priority queue buffer, updates
264*7f2d4e10SAkshay Belsare  * queue status, and sends an IPI to notify ASU of the pending command.
265*7f2d4e10SAkshay Belsare  *
266*7f2d4e10SAkshay Belsare  * Return: TEE_SUCCESS on success, appropriate error code on failure
267*7f2d4e10SAkshay Belsare  */
268*7f2d4e10SAkshay Belsare TEE_Result asu_update_queue_buffer_n_send_ipi(struct asu_client_params *param,
269*7f2d4e10SAkshay Belsare 					      void *req_buffer,
270*7f2d4e10SAkshay Belsare 					      uint32_t size,
271*7f2d4e10SAkshay Belsare 					      uint32_t header,
272*7f2d4e10SAkshay Belsare 					      uint32_t *status)
273*7f2d4e10SAkshay Belsare {
274*7f2d4e10SAkshay Belsare 	TEE_Result ret = TEE_ERROR_GENERIC;
275*7f2d4e10SAkshay Belsare 	uint8_t freeindex = 0;
276*7f2d4e10SAkshay Belsare 	struct asu_channel_queue_buf *bufptr = NULL;
277*7f2d4e10SAkshay Belsare 	struct asu_channel_queue *qptr = NULL;
278*7f2d4e10SAkshay Belsare 
279*7f2d4e10SAkshay Belsare 	if (!param) {
280*7f2d4e10SAkshay Belsare 		EMSG("Invalid parameters provided");
281*7f2d4e10SAkshay Belsare 		return TEE_ERROR_BAD_PARAMETERS;
282*7f2d4e10SAkshay Belsare 	}
283*7f2d4e10SAkshay Belsare 
284*7f2d4e10SAkshay Belsare 	if (asu->is_ready != ASU_CLIENT_READY) {
285*7f2d4e10SAkshay Belsare 		EMSG("ASU client is not ready");
286*7f2d4e10SAkshay Belsare 		return TEE_ERROR_BAD_STATE;
287*7f2d4e10SAkshay Belsare 	}
288*7f2d4e10SAkshay Belsare 
289*7f2d4e10SAkshay Belsare 	freeindex = get_free_index(param->priority);
290*7f2d4e10SAkshay Belsare 	if (freeindex == ASU_MAX_BUFFERS) {
291*7f2d4e10SAkshay Belsare 		EMSG("ASU buffers full");
292*7f2d4e10SAkshay Belsare 		return TEE_ERROR_SHORT_BUFFER;
293*7f2d4e10SAkshay Belsare 	}
294*7f2d4e10SAkshay Belsare 
295*7f2d4e10SAkshay Belsare 	if (param->priority == ASU_PRIORITY_HIGH) {
296*7f2d4e10SAkshay Belsare 		bufptr = &asu->chnl_memptr->p0_chnl_q.queue_bufs[freeindex];
297*7f2d4e10SAkshay Belsare 		qptr = &asu->chnl_memptr->p0_chnl_q;
298*7f2d4e10SAkshay Belsare 	} else {
299*7f2d4e10SAkshay Belsare 		bufptr = &asu->chnl_memptr->p1_chnl_q.queue_bufs[freeindex];
300*7f2d4e10SAkshay Belsare 		qptr = &asu->chnl_memptr->p1_chnl_q;
301*7f2d4e10SAkshay Belsare 	}
302*7f2d4e10SAkshay Belsare 
303*7f2d4e10SAkshay Belsare 	bufptr->req.header = header;
304*7f2d4e10SAkshay Belsare 	if (req_buffer && size != 0U)
305*7f2d4e10SAkshay Belsare 		memcpy(bufptr->req.arg, req_buffer, size);
306*7f2d4e10SAkshay Belsare 
307*7f2d4e10SAkshay Belsare 	bufptr->respbufstatus = 0;
308*7f2d4e10SAkshay Belsare 
309*7f2d4e10SAkshay Belsare 	qptr->cmd_is_present = true;
310*7f2d4e10SAkshay Belsare 	qptr->req_sent++;
311*7f2d4e10SAkshay Belsare 	ret = send_doorbell();
312*7f2d4e10SAkshay Belsare 	if (ret != TEE_SUCCESS) {
313*7f2d4e10SAkshay Belsare 		EMSG("Failed to communicate to ASU");
314*7f2d4e10SAkshay Belsare 		return TEE_ERROR_COMMUNICATION;
315*7f2d4e10SAkshay Belsare 	}
316*7f2d4e10SAkshay Belsare 	while (io_read8((vaddr_t)&bufptr->respbufstatus) !=
317*7f2d4e10SAkshay Belsare 	       ASU_RESPONSE_IS_PRESENT) {
318*7f2d4e10SAkshay Belsare 		/*
319*7f2d4e10SAkshay Belsare 		 * WFE will return on SEV generated by the
320*7f2d4e10SAkshay Belsare 		 * interrupt handler or by a spin_unlock
321*7f2d4e10SAkshay Belsare 		 */
322*7f2d4e10SAkshay Belsare 		wfe();
323*7f2d4e10SAkshay Belsare 	}
324*7f2d4e10SAkshay Belsare 
325*7f2d4e10SAkshay Belsare 	*status = bufptr->resp.arg[ASU_RESPONSE_STATUS_INDEX];
326*7f2d4e10SAkshay Belsare 	if (param->cbhandler && !*status)
327*7f2d4e10SAkshay Belsare 		ret = param->cbhandler(param->cbptr, &bufptr->resp);
328*7f2d4e10SAkshay Belsare 	put_free_index(bufptr);
329*7f2d4e10SAkshay Belsare 
330*7f2d4e10SAkshay Belsare 	return ret;
331*7f2d4e10SAkshay Belsare }
332*7f2d4e10SAkshay Belsare 
333*7f2d4e10SAkshay Belsare static void asu_clear_intr(void)
334*7f2d4e10SAkshay Belsare {
335*7f2d4e10SAkshay Belsare 	uint32_t status = 0;
336*7f2d4e10SAkshay Belsare 
337*7f2d4e10SAkshay Belsare 	status = io_read32((vaddr_t)asu->doorbell + IPIPSU_ISR_OFFSET);
338*7f2d4e10SAkshay Belsare 	io_write32((vaddr_t)asu->doorbell + IPIPSU_ISR_OFFSET,
339*7f2d4e10SAkshay Belsare 		   status & IPIPSU_ALL_MASK);
340*7f2d4e10SAkshay Belsare }
341*7f2d4e10SAkshay Belsare 
342*7f2d4e10SAkshay Belsare /*
343*7f2d4e10SAkshay Belsare  * asu_resp_handler() - Interrupt handler for ASU responses
344*7f2d4e10SAkshay Belsare  * @handler: Interrupt handler structure (unused)
345*7f2d4e10SAkshay Belsare  *
346*7f2d4e10SAkshay Belsare  * Interrupt service routine that processes responses from both high
347*7f2d4e10SAkshay Belsare  * priority (P0) and low priority (P1) queues when ASU completes
348*7f2d4e10SAkshay Belsare  * command processing.
349*7f2d4e10SAkshay Belsare  *
350*7f2d4e10SAkshay Belsare  * Return: ITRR_HANDLED indicating interrupt was handled
351*7f2d4e10SAkshay Belsare  */
352*7f2d4e10SAkshay Belsare static enum itr_return asu_resp_handler(struct itr_handler *handler __unused)
353*7f2d4e10SAkshay Belsare {
354*7f2d4e10SAkshay Belsare 	sev();
355*7f2d4e10SAkshay Belsare 	asu_clear_intr();
356*7f2d4e10SAkshay Belsare 
357*7f2d4e10SAkshay Belsare 	return ITRR_HANDLED;
358*7f2d4e10SAkshay Belsare }
359*7f2d4e10SAkshay Belsare 
360*7f2d4e10SAkshay Belsare static struct itr_handler doorbell_handler = {
361*7f2d4e10SAkshay Belsare 	.it = PAR_IPIPSU_0_INT_ID,
362*7f2d4e10SAkshay Belsare 	.handler = asu_resp_handler,
363*7f2d4e10SAkshay Belsare };
364*7f2d4e10SAkshay Belsare 
365*7f2d4e10SAkshay Belsare /*
366*7f2d4e10SAkshay Belsare  * setup_doorbell() - Initialize doorbell interrupt handling
367*7f2d4e10SAkshay Belsare  *
368*7f2d4e10SAkshay Belsare  * Maps the doorbell register region, configures interrupt settings,
369*7f2d4e10SAkshay Belsare  * registers the interrupt handler, and enables the interrupt for
370*7f2d4e10SAkshay Belsare  * receiving ASU response notifications.
371*7f2d4e10SAkshay Belsare  *
372*7f2d4e10SAkshay Belsare  * Return: Pointer to mapped doorbell region or NULL on failure
373*7f2d4e10SAkshay Belsare  */
374*7f2d4e10SAkshay Belsare static void *setup_doorbell(void)
375*7f2d4e10SAkshay Belsare {
376*7f2d4e10SAkshay Belsare 	void *dbell = NULL;
377*7f2d4e10SAkshay Belsare 	TEE_Result res = TEE_ERROR_GENERIC;
378*7f2d4e10SAkshay Belsare 
379*7f2d4e10SAkshay Belsare 	dbell = core_mmu_add_mapping(MEM_AREA_IO_SEC,
380*7f2d4e10SAkshay Belsare 				     asu_configtable.baseaddr,
381*7f2d4e10SAkshay Belsare 				     ASU_BASEADDR_SIZE);
382*7f2d4e10SAkshay Belsare 	if (!dbell) {
383*7f2d4e10SAkshay Belsare 		EMSG("Failed to map doorbell register");
384*7f2d4e10SAkshay Belsare 		return dbell;
385*7f2d4e10SAkshay Belsare 	}
386*7f2d4e10SAkshay Belsare 
387*7f2d4e10SAkshay Belsare 	io_write32((vaddr_t)dbell + IPIPSU_IER_OFFSET, IPIPSU_ALL_MASK);
388*7f2d4e10SAkshay Belsare 	io_write32((vaddr_t)dbell + IPIPSU_ISR_OFFSET, IPIPSU_ALL_MASK);
389*7f2d4e10SAkshay Belsare 
390*7f2d4e10SAkshay Belsare 	doorbell_handler.chip = interrupt_get_main_chip();
391*7f2d4e10SAkshay Belsare 
392*7f2d4e10SAkshay Belsare 	res = interrupt_add_configure_handler(&doorbell_handler,
393*7f2d4e10SAkshay Belsare 					      IRQ_TYPE_LEVEL_HIGH, 7);
394*7f2d4e10SAkshay Belsare 	if (res)
395*7f2d4e10SAkshay Belsare 		panic();
396*7f2d4e10SAkshay Belsare 
397*7f2d4e10SAkshay Belsare 	interrupt_enable(doorbell_handler.chip, doorbell_handler.it);
398*7f2d4e10SAkshay Belsare 
399*7f2d4e10SAkshay Belsare 	return dbell;
400*7f2d4e10SAkshay Belsare }
401*7f2d4e10SAkshay Belsare 
402*7f2d4e10SAkshay Belsare static void asu_init_unique_id(void)
403*7f2d4e10SAkshay Belsare {
404*7f2d4e10SAkshay Belsare 	uint32_t idx = 0;
405*7f2d4e10SAkshay Belsare 
406*7f2d4e10SAkshay Belsare 	asuid.slock = SPINLOCK_UNLOCK;
407*7f2d4e10SAkshay Belsare 	for (idx = 0; idx < ARRAY_SIZE(asuid.ids); idx++)
408*7f2d4e10SAkshay Belsare 		asuid.ids[idx] = ASU_UNIQUE_ID_MAX;
409*7f2d4e10SAkshay Belsare }
410*7f2d4e10SAkshay Belsare 
411*7f2d4e10SAkshay Belsare /*
412*7f2d4e10SAkshay Belsare  * asu_init() - Initialize the ASU driver and communication channel
413*7f2d4e10SAkshay Belsare  *
414*7f2d4e10SAkshay Belsare  * Performs complete ASU driver initialization including memory allocation,
415*7f2d4e10SAkshay Belsare  * firmware readiness check, channel ID discovery, shared memory mapping,
416*7f2d4e10SAkshay Belsare  * doorbell setup, and marking the client as ready for operation.
417*7f2d4e10SAkshay Belsare  *
418*7f2d4e10SAkshay Belsare  * Return: TEE_SUCCESS on successful initialization, appropriate error code on
419*7f2d4e10SAkshay Belsare  *	   failure
420*7f2d4e10SAkshay Belsare  */
421*7f2d4e10SAkshay Belsare static TEE_Result asu_init(void)
422*7f2d4e10SAkshay Belsare {
423*7f2d4e10SAkshay Belsare 	uint32_t channel_id = 0;
424*7f2d4e10SAkshay Belsare 	void *asu_shmem = NULL;
425*7f2d4e10SAkshay Belsare 	uint64_t membase = 0;
426*7f2d4e10SAkshay Belsare 
427*7f2d4e10SAkshay Belsare 	asu = calloc(1, sizeof(struct asu_client));
428*7f2d4e10SAkshay Belsare 	if (!asu) {
429*7f2d4e10SAkshay Belsare 		EMSG("Failed to allocate memory for ASU");
430*7f2d4e10SAkshay Belsare 		return TEE_ERROR_OUT_OF_MEMORY;
431*7f2d4e10SAkshay Belsare 	}
432*7f2d4e10SAkshay Belsare 
433*7f2d4e10SAkshay Belsare 	asu->global_ctrl = core_mmu_add_mapping(MEM_AREA_IO_SEC,
434*7f2d4e10SAkshay Belsare 						ASU_BASEADDR,
435*7f2d4e10SAkshay Belsare 						ASU_BASEADDR_SIZE);
436*7f2d4e10SAkshay Belsare 	if (!asu->global_ctrl) {
437*7f2d4e10SAkshay Belsare 		EMSG("Failed to initialize ASU");
438*7f2d4e10SAkshay Belsare 		goto free;
439*7f2d4e10SAkshay Belsare 	}
440*7f2d4e10SAkshay Belsare 
441*7f2d4e10SAkshay Belsare 	if (asu_fwcheck() != TEE_SUCCESS) {
442*7f2d4e10SAkshay Belsare 		EMSG("ASU FW check failed");
443*7f2d4e10SAkshay Belsare 		goto global_unmap;
444*7f2d4e10SAkshay Belsare 	}
445*7f2d4e10SAkshay Belsare 
446*7f2d4e10SAkshay Belsare 	channel_id = asu_get_channelID();
447*7f2d4e10SAkshay Belsare 
448*7f2d4e10SAkshay Belsare 	if (channel_id == ASU_MAX_IPI_CHANNELS) {
449*7f2d4e10SAkshay Belsare 		EMSG("ASU channel for APU not configured");
450*7f2d4e10SAkshay Belsare 		goto global_unmap;
451*7f2d4e10SAkshay Belsare 	}
452*7f2d4e10SAkshay Belsare 
453*7f2d4e10SAkshay Belsare 	membase = ASU_CHANNEL_MEMORY_BASEADDR +
454*7f2d4e10SAkshay Belsare 			ASU_GLOBAL_ADDR_LIMIT * channel_id;
455*7f2d4e10SAkshay Belsare 	asu_shmem = core_mmu_add_mapping(MEM_AREA_IO_SEC,
456*7f2d4e10SAkshay Belsare 					 membase,
457*7f2d4e10SAkshay Belsare 					 ASU_GLOBAL_ADDR_LIMIT);
458*7f2d4e10SAkshay Belsare 	if (!asu_shmem) {
459*7f2d4e10SAkshay Belsare 		EMSG("Failed to map ASU SHM");
460*7f2d4e10SAkshay Belsare 		goto global_unmap;
461*7f2d4e10SAkshay Belsare 	}
462*7f2d4e10SAkshay Belsare 	asu_init_unique_id();
463*7f2d4e10SAkshay Belsare 	asu->doorbell = setup_doorbell();
464*7f2d4e10SAkshay Belsare 	if (!asu->doorbell) {
465*7f2d4e10SAkshay Belsare 		EMSG("Failed to set up ASU doorbell");
466*7f2d4e10SAkshay Belsare 		goto sh_unmap;
467*7f2d4e10SAkshay Belsare 	}
468*7f2d4e10SAkshay Belsare 
469*7f2d4e10SAkshay Belsare 	asu->chnl_memptr = asu_shmem;
470*7f2d4e10SAkshay Belsare 	asu->is_ready = ASU_CLIENT_READY;
471*7f2d4e10SAkshay Belsare 	asu->slock = SPINLOCK_UNLOCK;
472*7f2d4e10SAkshay Belsare 
473*7f2d4e10SAkshay Belsare 	IMSG("ASU initialization complete");
474*7f2d4e10SAkshay Belsare 
475*7f2d4e10SAkshay Belsare 	return TEE_SUCCESS;
476*7f2d4e10SAkshay Belsare 
477*7f2d4e10SAkshay Belsare sh_unmap:
478*7f2d4e10SAkshay Belsare 	core_mmu_remove_mapping(MEM_AREA_IO_SEC, asu_shmem,
479*7f2d4e10SAkshay Belsare 				ASU_GLOBAL_ADDR_LIMIT);
480*7f2d4e10SAkshay Belsare global_unmap:
481*7f2d4e10SAkshay Belsare 	core_mmu_remove_mapping(MEM_AREA_IO_SEC, asu->global_ctrl,
482*7f2d4e10SAkshay Belsare 				ASU_BASEADDR_SIZE);
483*7f2d4e10SAkshay Belsare free:
484*7f2d4e10SAkshay Belsare 	free(asu);
485*7f2d4e10SAkshay Belsare 
486*7f2d4e10SAkshay Belsare 	EMSG("Failed to initialize ASU");
487*7f2d4e10SAkshay Belsare 
488*7f2d4e10SAkshay Belsare 	return TEE_ERROR_GENERIC;
489*7f2d4e10SAkshay Belsare }
490*7f2d4e10SAkshay Belsare 
491*7f2d4e10SAkshay Belsare service_init(asu_init);
492