Pankaj Gupta | a0edacb | 2020-12-09 14:02:39 +0530 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021 NXP |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | * |
| 6 | */ |
| 7 | |
| 8 | #include <errno.h> |
| 9 | #include <stdbool.h> |
| 10 | #include <stdint.h> |
| 11 | #include <stdio.h> |
| 12 | #include <stdlib.h> |
| 13 | #include <string.h> |
| 14 | |
| 15 | #include <arch_helpers.h> |
| 16 | #include "caam.h" |
| 17 | #include <common/debug.h> |
| 18 | #include "jobdesc.h" |
| 19 | #include "nxp_timer.h" |
| 20 | #include "sec_hw_specific.h" |
| 21 | #include "sec_jr_driver.h" |
| 22 | |
| 23 | |
| 24 | /* Job rings used for communication with SEC HW */ |
| 25 | struct sec_job_ring_t g_job_rings[MAX_SEC_JOB_RINGS]; |
| 26 | |
| 27 | /* The current state of SEC user space driver */ |
| 28 | volatile sec_driver_state_t g_driver_state = SEC_DRIVER_STATE_IDLE; |
| 29 | |
| 30 | int g_job_rings_no; |
| 31 | |
| 32 | uint8_t ip_ring[SEC_DMA_MEM_INPUT_RING_SIZE] __aligned(CACHE_WRITEBACK_GRANULE); |
| 33 | uint8_t op_ring[SEC_DMA_MEM_OUTPUT_RING_SIZE] __aligned(CACHE_WRITEBACK_GRANULE); |
| 34 | |
| 35 | void *init_job_ring(uint8_t jr_mode, |
| 36 | uint16_t irq_coalescing_timer, |
| 37 | uint8_t irq_coalescing_count, |
| 38 | void *reg_base_addr, uint32_t irq_id) |
| 39 | { |
| 40 | struct sec_job_ring_t *job_ring = &g_job_rings[g_job_rings_no++]; |
| 41 | int ret = 0; |
| 42 | |
| 43 | job_ring->register_base_addr = reg_base_addr; |
| 44 | job_ring->jr_mode = jr_mode; |
| 45 | job_ring->irq_fd = irq_id; |
| 46 | |
| 47 | job_ring->input_ring = vtop(ip_ring); |
| 48 | memset(job_ring->input_ring, 0, SEC_DMA_MEM_INPUT_RING_SIZE); |
| 49 | |
| 50 | job_ring->output_ring = (struct sec_outring_entry *)vtop(op_ring); |
| 51 | memset(job_ring->output_ring, 0, SEC_DMA_MEM_OUTPUT_RING_SIZE); |
| 52 | |
| 53 | dsb(); |
| 54 | |
| 55 | #if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) |
| 56 | flush_dcache_range((uintptr_t)(job_ring->input_ring), |
| 57 | SEC_DMA_MEM_INPUT_RING_SIZE), |
| 58 | flush_dcache_range((uintptr_t)(job_ring->output_ring), |
| 59 | SEC_DMA_MEM_OUTPUT_RING_SIZE), |
| 60 | |
| 61 | dmbsy(); |
| 62 | #endif |
| 63 | /* Reset job ring in SEC hw and configure job ring registers */ |
| 64 | ret = hw_reset_job_ring(job_ring); |
| 65 | if (ret != 0) { |
| 66 | ERROR("Failed to reset hardware job ring\n"); |
| 67 | return NULL; |
| 68 | } |
| 69 | |
| 70 | if (jr_mode == SEC_NOTIFICATION_TYPE_IRQ) { |
| 71 | /* Enable IRQ if driver work sin interrupt mode */ |
| 72 | ERROR("Enabling DONE IRQ generation on job ring\n"); |
| 73 | ret = jr_enable_irqs(job_ring); |
| 74 | if (ret != 0) { |
| 75 | ERROR("Failed to enable irqs for job ring\n"); |
| 76 | return NULL; |
| 77 | } |
| 78 | } |
| 79 | if ((irq_coalescing_timer != 0) || (irq_coalescing_count != 0)) { |
| 80 | hw_job_ring_set_coalescing_param(job_ring, |
| 81 | irq_coalescing_timer, |
| 82 | irq_coalescing_count); |
| 83 | |
| 84 | hw_job_ring_enable_coalescing(job_ring); |
| 85 | job_ring->coalescing_en = 1; |
| 86 | } |
| 87 | |
| 88 | job_ring->jr_state = SEC_JOB_RING_STATE_STARTED; |
| 89 | |
| 90 | return job_ring; |
| 91 | } |
| 92 | |
| 93 | int sec_release(void) |
| 94 | { |
| 95 | int i; |
| 96 | |
| 97 | /* Validate driver state */ |
| 98 | if (g_driver_state == SEC_DRIVER_STATE_RELEASE) { |
| 99 | ERROR("Driver release is already in progress"); |
| 100 | return SEC_DRIVER_RELEASE_IN_PROGRESS; |
| 101 | } |
| 102 | /* Update driver state */ |
| 103 | g_driver_state = SEC_DRIVER_STATE_RELEASE; |
| 104 | |
| 105 | /* If any descriptors in flight , poll and wait |
| 106 | * until all descriptors are received and silently discarded. |
| 107 | */ |
| 108 | |
| 109 | flush_job_rings(); |
| 110 | |
| 111 | for (i = 0; i < g_job_rings_no; i++) { |
| 112 | shutdown_job_ring(&g_job_rings[i]); |
| 113 | } |
| 114 | g_job_rings_no = 0; |
| 115 | g_driver_state = SEC_DRIVER_STATE_IDLE; |
| 116 | |
| 117 | return SEC_SUCCESS; |
| 118 | } |
| 119 | |
| 120 | int sec_jr_lib_init(void) |
| 121 | { |
| 122 | /* Validate driver state */ |
| 123 | if (g_driver_state != SEC_DRIVER_STATE_IDLE) { |
| 124 | ERROR("Driver already initialized\n"); |
| 125 | return 0; |
| 126 | } |
| 127 | |
| 128 | memset(g_job_rings, 0, sizeof(g_job_rings)); |
| 129 | g_job_rings_no = 0; |
| 130 | |
| 131 | /* Update driver state */ |
| 132 | g_driver_state = SEC_DRIVER_STATE_STARTED; |
| 133 | return 0; |
| 134 | } |
| 135 | |
| 136 | int dequeue_jr(void *job_ring_handle, int32_t limit) |
| 137 | { |
| 138 | int ret = 0; |
| 139 | int notified_descs_no = 0; |
| 140 | struct sec_job_ring_t *job_ring = (sec_job_ring_t *) job_ring_handle; |
| 141 | uint64_t start_time; |
| 142 | |
| 143 | /* Validate driver state */ |
| 144 | if (g_driver_state != SEC_DRIVER_STATE_STARTED) { |
| 145 | ERROR("Driver release in progress or driver not initialized\n"); |
| 146 | return -1; |
| 147 | } |
| 148 | |
| 149 | /* Validate input arguments */ |
| 150 | if (job_ring == NULL) { |
| 151 | ERROR("job_ring_handle is NULL\n"); |
| 152 | return -1; |
| 153 | } |
| 154 | if (((limit == 0) || (limit > SEC_JOB_RING_SIZE))) { |
| 155 | ERROR("Invalid limit parameter configuration\n"); |
| 156 | return -1; |
| 157 | } |
| 158 | |
| 159 | VERBOSE("JR Polling limit[%d]\n", limit); |
| 160 | |
| 161 | /* Poll job ring |
| 162 | * If limit < 0 -> poll JR until no more notifications are available. |
| 163 | * If limit > 0 -> poll JR until limit is reached. |
| 164 | */ |
| 165 | |
| 166 | start_time = get_timer_val(0); |
| 167 | |
| 168 | while (notified_descs_no == 0) { |
| 169 | /* Run hw poll job ring */ |
| 170 | notified_descs_no = hw_poll_job_ring(job_ring, limit); |
| 171 | if (notified_descs_no < 0) { |
| 172 | ERROR("Error polling SEC engine job ring "); |
| 173 | return notified_descs_no; |
| 174 | } |
| 175 | VERBOSE("Jobs notified[%d]. ", notified_descs_no); |
| 176 | |
| 177 | if (get_timer_val(start_time) >= CAAM_TIMEOUT) { |
| 178 | break; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | if (job_ring->jr_mode == SEC_NOTIFICATION_TYPE_IRQ) { |
| 183 | |
| 184 | /* Always enable IRQ generation when in pure IRQ mode */ |
| 185 | ret = jr_enable_irqs(job_ring); |
| 186 | if (ret != 0) { |
| 187 | ERROR("Failed to enable irqs for job ring"); |
| 188 | return ret; |
| 189 | } |
| 190 | } |
| 191 | return notified_descs_no; |
| 192 | } |
| 193 | |
| 194 | int enq_jr_desc(void *job_ring_handle, struct job_descriptor *jobdescr) |
| 195 | { |
| 196 | struct sec_job_ring_t *job_ring; |
| 197 | |
| 198 | job_ring = (struct sec_job_ring_t *)job_ring_handle; |
| 199 | |
| 200 | /* Validate driver state */ |
| 201 | if (g_driver_state != SEC_DRIVER_STATE_STARTED) { |
| 202 | ERROR("Driver release in progress or driver not initialized\n"); |
| 203 | return -1; |
| 204 | } |
| 205 | |
| 206 | /* Check job ring state */ |
| 207 | if (job_ring->jr_state != SEC_JOB_RING_STATE_STARTED) { |
| 208 | ERROR("Job ring is currently resetting\n"); |
| 209 | return -1; |
| 210 | } |
| 211 | |
| 212 | if (SEC_JOB_RING_IS_FULL(job_ring->pidx, job_ring->cidx, |
| 213 | SEC_JOB_RING_SIZE, SEC_JOB_RING_SIZE)) { |
| 214 | ERROR("Job ring is full\n"); |
| 215 | return -1; |
| 216 | } |
| 217 | |
| 218 | /* Set ptr in input ring to current descriptor */ |
| 219 | sec_write_addr(&job_ring->input_ring[job_ring->pidx], |
| 220 | (phys_addr_t) vtop(jobdescr->desc)); |
| 221 | |
| 222 | dsb(); |
| 223 | |
| 224 | #if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) |
| 225 | flush_dcache_range((uintptr_t)(&job_ring->input_ring[job_ring->pidx]), |
| 226 | sizeof(phys_addr_t)); |
| 227 | |
| 228 | inv_dcache_range((uintptr_t)(&job_ring->output_ring[job_ring->cidx]), |
| 229 | sizeof(struct sec_outring_entry)); |
| 230 | dmbsy(); |
| 231 | #endif |
| 232 | /* Notify HW that a new job is enqueued */ |
| 233 | hw_enqueue_desc_on_job_ring( |
| 234 | (struct jobring_regs *)job_ring->register_base_addr, 1); |
| 235 | |
| 236 | /* increment the producer index for the current job ring */ |
| 237 | job_ring->pidx = SEC_CIRCULAR_COUNTER(job_ring->pidx, |
| 238 | SEC_JOB_RING_SIZE); |
| 239 | |
| 240 | return 0; |
| 241 | } |