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