18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Marvell 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Antoine Tenart <antoine.tenart@free-electrons.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 98c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "safexcel.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ciint safexcel_init_ring_descriptors(struct safexcel_crypto_priv *priv, 148c2ecf20Sopenharmony_ci struct safexcel_desc_ring *cdr, 158c2ecf20Sopenharmony_ci struct safexcel_desc_ring *rdr) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci int i; 188c2ecf20Sopenharmony_ci struct safexcel_command_desc *cdesc; 198c2ecf20Sopenharmony_ci dma_addr_t atok; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci /* Actual command descriptor ring */ 228c2ecf20Sopenharmony_ci cdr->offset = priv->config.cd_offset; 238c2ecf20Sopenharmony_ci cdr->base = dmam_alloc_coherent(priv->dev, 248c2ecf20Sopenharmony_ci cdr->offset * EIP197_DEFAULT_RING_SIZE, 258c2ecf20Sopenharmony_ci &cdr->base_dma, GFP_KERNEL); 268c2ecf20Sopenharmony_ci if (!cdr->base) 278c2ecf20Sopenharmony_ci return -ENOMEM; 288c2ecf20Sopenharmony_ci cdr->write = cdr->base; 298c2ecf20Sopenharmony_ci cdr->base_end = cdr->base + cdr->offset * (EIP197_DEFAULT_RING_SIZE - 1); 308c2ecf20Sopenharmony_ci cdr->read = cdr->base; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci /* Command descriptor shadow ring for storing additional token data */ 338c2ecf20Sopenharmony_ci cdr->shoffset = priv->config.cdsh_offset; 348c2ecf20Sopenharmony_ci cdr->shbase = dmam_alloc_coherent(priv->dev, 358c2ecf20Sopenharmony_ci cdr->shoffset * 368c2ecf20Sopenharmony_ci EIP197_DEFAULT_RING_SIZE, 378c2ecf20Sopenharmony_ci &cdr->shbase_dma, GFP_KERNEL); 388c2ecf20Sopenharmony_ci if (!cdr->shbase) 398c2ecf20Sopenharmony_ci return -ENOMEM; 408c2ecf20Sopenharmony_ci cdr->shwrite = cdr->shbase; 418c2ecf20Sopenharmony_ci cdr->shbase_end = cdr->shbase + cdr->shoffset * 428c2ecf20Sopenharmony_ci (EIP197_DEFAULT_RING_SIZE - 1); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* 458c2ecf20Sopenharmony_ci * Populate command descriptors with physical pointers to shadow descs. 468c2ecf20Sopenharmony_ci * Note that we only need to do this once if we don't overwrite them. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci cdesc = cdr->base; 498c2ecf20Sopenharmony_ci atok = cdr->shbase_dma; 508c2ecf20Sopenharmony_ci for (i = 0; i < EIP197_DEFAULT_RING_SIZE; i++) { 518c2ecf20Sopenharmony_ci cdesc->atok_lo = lower_32_bits(atok); 528c2ecf20Sopenharmony_ci cdesc->atok_hi = upper_32_bits(atok); 538c2ecf20Sopenharmony_ci cdesc = (void *)cdesc + cdr->offset; 548c2ecf20Sopenharmony_ci atok += cdr->shoffset; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci rdr->offset = priv->config.rd_offset; 588c2ecf20Sopenharmony_ci /* Use shoffset for result token offset here */ 598c2ecf20Sopenharmony_ci rdr->shoffset = priv->config.res_offset; 608c2ecf20Sopenharmony_ci rdr->base = dmam_alloc_coherent(priv->dev, 618c2ecf20Sopenharmony_ci rdr->offset * EIP197_DEFAULT_RING_SIZE, 628c2ecf20Sopenharmony_ci &rdr->base_dma, GFP_KERNEL); 638c2ecf20Sopenharmony_ci if (!rdr->base) 648c2ecf20Sopenharmony_ci return -ENOMEM; 658c2ecf20Sopenharmony_ci rdr->write = rdr->base; 668c2ecf20Sopenharmony_ci rdr->base_end = rdr->base + rdr->offset * (EIP197_DEFAULT_RING_SIZE - 1); 678c2ecf20Sopenharmony_ci rdr->read = rdr->base; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciinline int safexcel_select_ring(struct safexcel_crypto_priv *priv) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return (atomic_inc_return(&priv->ring_used) % priv->config.rings); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void *safexcel_ring_next_cwptr(struct safexcel_crypto_priv *priv, 788c2ecf20Sopenharmony_ci struct safexcel_desc_ring *ring, 798c2ecf20Sopenharmony_ci bool first, 808c2ecf20Sopenharmony_ci struct safexcel_token **atoken) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci void *ptr = ring->write; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (first) 858c2ecf20Sopenharmony_ci *atoken = ring->shwrite; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if ((ring->write == ring->read - ring->offset) || 888c2ecf20Sopenharmony_ci (ring->read == ring->base && ring->write == ring->base_end)) 898c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (ring->write == ring->base_end) { 928c2ecf20Sopenharmony_ci ring->write = ring->base; 938c2ecf20Sopenharmony_ci ring->shwrite = ring->shbase; 948c2ecf20Sopenharmony_ci } else { 958c2ecf20Sopenharmony_ci ring->write += ring->offset; 968c2ecf20Sopenharmony_ci ring->shwrite += ring->shoffset; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return ptr; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void *safexcel_ring_next_rwptr(struct safexcel_crypto_priv *priv, 1038c2ecf20Sopenharmony_ci struct safexcel_desc_ring *ring, 1048c2ecf20Sopenharmony_ci struct result_data_desc **rtoken) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci void *ptr = ring->write; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Result token at relative offset shoffset */ 1098c2ecf20Sopenharmony_ci *rtoken = ring->write + ring->shoffset; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if ((ring->write == ring->read - ring->offset) || 1128c2ecf20Sopenharmony_ci (ring->read == ring->base && ring->write == ring->base_end)) 1138c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (ring->write == ring->base_end) 1168c2ecf20Sopenharmony_ci ring->write = ring->base; 1178c2ecf20Sopenharmony_ci else 1188c2ecf20Sopenharmony_ci ring->write += ring->offset; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return ptr; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_civoid *safexcel_ring_next_rptr(struct safexcel_crypto_priv *priv, 1248c2ecf20Sopenharmony_ci struct safexcel_desc_ring *ring) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci void *ptr = ring->read; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (ring->write == ring->read) 1298c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (ring->read == ring->base_end) 1328c2ecf20Sopenharmony_ci ring->read = ring->base; 1338c2ecf20Sopenharmony_ci else 1348c2ecf20Sopenharmony_ci ring->read += ring->offset; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return ptr; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciinline void *safexcel_ring_curr_rptr(struct safexcel_crypto_priv *priv, 1408c2ecf20Sopenharmony_ci int ring) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return rdr->read; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ciinline int safexcel_ring_first_rdr_index(struct safexcel_crypto_priv *priv, 1488c2ecf20Sopenharmony_ci int ring) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return (rdr->read - rdr->base) / rdr->offset; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciinline int safexcel_ring_rdr_rdesc_index(struct safexcel_crypto_priv *priv, 1568c2ecf20Sopenharmony_ci int ring, 1578c2ecf20Sopenharmony_ci struct safexcel_result_desc *rdesc) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return ((void *)rdesc - rdr->base) / rdr->offset; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_civoid safexcel_ring_rollback_wptr(struct safexcel_crypto_priv *priv, 1658c2ecf20Sopenharmony_ci struct safexcel_desc_ring *ring) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci if (ring->write == ring->read) 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (ring->write == ring->base) { 1718c2ecf20Sopenharmony_ci ring->write = ring->base_end; 1728c2ecf20Sopenharmony_ci ring->shwrite = ring->shbase_end; 1738c2ecf20Sopenharmony_ci } else { 1748c2ecf20Sopenharmony_ci ring->write -= ring->offset; 1758c2ecf20Sopenharmony_ci ring->shwrite -= ring->shoffset; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistruct safexcel_command_desc *safexcel_add_cdesc(struct safexcel_crypto_priv *priv, 1808c2ecf20Sopenharmony_ci int ring_id, 1818c2ecf20Sopenharmony_ci bool first, bool last, 1828c2ecf20Sopenharmony_ci dma_addr_t data, u32 data_len, 1838c2ecf20Sopenharmony_ci u32 full_data_len, 1848c2ecf20Sopenharmony_ci dma_addr_t context, 1858c2ecf20Sopenharmony_ci struct safexcel_token **atoken) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct safexcel_command_desc *cdesc; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci cdesc = safexcel_ring_next_cwptr(priv, &priv->ring[ring_id].cdr, 1908c2ecf20Sopenharmony_ci first, atoken); 1918c2ecf20Sopenharmony_ci if (IS_ERR(cdesc)) 1928c2ecf20Sopenharmony_ci return cdesc; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci cdesc->particle_size = data_len; 1958c2ecf20Sopenharmony_ci cdesc->rsvd0 = 0; 1968c2ecf20Sopenharmony_ci cdesc->last_seg = last; 1978c2ecf20Sopenharmony_ci cdesc->first_seg = first; 1988c2ecf20Sopenharmony_ci cdesc->additional_cdata_size = 0; 1998c2ecf20Sopenharmony_ci cdesc->rsvd1 = 0; 2008c2ecf20Sopenharmony_ci cdesc->data_lo = lower_32_bits(data); 2018c2ecf20Sopenharmony_ci cdesc->data_hi = upper_32_bits(data); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (first) { 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * Note that the length here MUST be >0 or else the EIP(1)97 2068c2ecf20Sopenharmony_ci * may hang. Newer EIP197 firmware actually incorporates this 2078c2ecf20Sopenharmony_ci * fix already, but that doesn't help the EIP97 and we may 2088c2ecf20Sopenharmony_ci * also be running older firmware. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci cdesc->control_data.packet_length = full_data_len ?: 1; 2118c2ecf20Sopenharmony_ci cdesc->control_data.options = EIP197_OPTION_MAGIC_VALUE | 2128c2ecf20Sopenharmony_ci EIP197_OPTION_64BIT_CTX | 2138c2ecf20Sopenharmony_ci EIP197_OPTION_CTX_CTRL_IN_CMD | 2148c2ecf20Sopenharmony_ci EIP197_OPTION_RC_AUTO; 2158c2ecf20Sopenharmony_ci cdesc->control_data.type = EIP197_TYPE_BCLA; 2168c2ecf20Sopenharmony_ci cdesc->control_data.context_lo = lower_32_bits(context) | 2178c2ecf20Sopenharmony_ci EIP197_CONTEXT_SMALL; 2188c2ecf20Sopenharmony_ci cdesc->control_data.context_hi = upper_32_bits(context); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return cdesc; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistruct safexcel_result_desc *safexcel_add_rdesc(struct safexcel_crypto_priv *priv, 2258c2ecf20Sopenharmony_ci int ring_id, 2268c2ecf20Sopenharmony_ci bool first, bool last, 2278c2ecf20Sopenharmony_ci dma_addr_t data, u32 len) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct safexcel_result_desc *rdesc; 2308c2ecf20Sopenharmony_ci struct result_data_desc *rtoken; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci rdesc = safexcel_ring_next_rwptr(priv, &priv->ring[ring_id].rdr, 2338c2ecf20Sopenharmony_ci &rtoken); 2348c2ecf20Sopenharmony_ci if (IS_ERR(rdesc)) 2358c2ecf20Sopenharmony_ci return rdesc; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci rdesc->particle_size = len; 2388c2ecf20Sopenharmony_ci rdesc->rsvd0 = 0; 2398c2ecf20Sopenharmony_ci rdesc->descriptor_overflow = 1; /* assume error */ 2408c2ecf20Sopenharmony_ci rdesc->buffer_overflow = 1; /* assume error */ 2418c2ecf20Sopenharmony_ci rdesc->last_seg = last; 2428c2ecf20Sopenharmony_ci rdesc->first_seg = first; 2438c2ecf20Sopenharmony_ci rdesc->result_size = EIP197_RD64_RESULT_SIZE; 2448c2ecf20Sopenharmony_ci rdesc->rsvd1 = 0; 2458c2ecf20Sopenharmony_ci rdesc->data_lo = lower_32_bits(data); 2468c2ecf20Sopenharmony_ci rdesc->data_hi = upper_32_bits(data); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Clear length in result token */ 2498c2ecf20Sopenharmony_ci rtoken->packet_length = 0; 2508c2ecf20Sopenharmony_ci /* Assume errors - HW will clear if not the case */ 2518c2ecf20Sopenharmony_ci rtoken->error_code = 0x7fff; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return rdesc; 2548c2ecf20Sopenharmony_ci} 255