162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Marvell 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Antoine Tenart <antoine.tenart@free-electrons.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 962306a36Sopenharmony_ci#include <linux/spinlock.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "safexcel.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciint safexcel_init_ring_descriptors(struct safexcel_crypto_priv *priv, 1462306a36Sopenharmony_ci struct safexcel_desc_ring *cdr, 1562306a36Sopenharmony_ci struct safexcel_desc_ring *rdr) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci int i; 1862306a36Sopenharmony_ci struct safexcel_command_desc *cdesc; 1962306a36Sopenharmony_ci dma_addr_t atok; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci /* Actual command descriptor ring */ 2262306a36Sopenharmony_ci cdr->offset = priv->config.cd_offset; 2362306a36Sopenharmony_ci cdr->base = dmam_alloc_coherent(priv->dev, 2462306a36Sopenharmony_ci cdr->offset * EIP197_DEFAULT_RING_SIZE, 2562306a36Sopenharmony_ci &cdr->base_dma, GFP_KERNEL); 2662306a36Sopenharmony_ci if (!cdr->base) 2762306a36Sopenharmony_ci return -ENOMEM; 2862306a36Sopenharmony_ci cdr->write = cdr->base; 2962306a36Sopenharmony_ci cdr->base_end = cdr->base + cdr->offset * (EIP197_DEFAULT_RING_SIZE - 1); 3062306a36Sopenharmony_ci cdr->read = cdr->base; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* Command descriptor shadow ring for storing additional token data */ 3362306a36Sopenharmony_ci cdr->shoffset = priv->config.cdsh_offset; 3462306a36Sopenharmony_ci cdr->shbase = dmam_alloc_coherent(priv->dev, 3562306a36Sopenharmony_ci cdr->shoffset * 3662306a36Sopenharmony_ci EIP197_DEFAULT_RING_SIZE, 3762306a36Sopenharmony_ci &cdr->shbase_dma, GFP_KERNEL); 3862306a36Sopenharmony_ci if (!cdr->shbase) 3962306a36Sopenharmony_ci return -ENOMEM; 4062306a36Sopenharmony_ci cdr->shwrite = cdr->shbase; 4162306a36Sopenharmony_ci cdr->shbase_end = cdr->shbase + cdr->shoffset * 4262306a36Sopenharmony_ci (EIP197_DEFAULT_RING_SIZE - 1); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* 4562306a36Sopenharmony_ci * Populate command descriptors with physical pointers to shadow descs. 4662306a36Sopenharmony_ci * Note that we only need to do this once if we don't overwrite them. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci cdesc = cdr->base; 4962306a36Sopenharmony_ci atok = cdr->shbase_dma; 5062306a36Sopenharmony_ci for (i = 0; i < EIP197_DEFAULT_RING_SIZE; i++) { 5162306a36Sopenharmony_ci cdesc->atok_lo = lower_32_bits(atok); 5262306a36Sopenharmony_ci cdesc->atok_hi = upper_32_bits(atok); 5362306a36Sopenharmony_ci cdesc = (void *)cdesc + cdr->offset; 5462306a36Sopenharmony_ci atok += cdr->shoffset; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci rdr->offset = priv->config.rd_offset; 5862306a36Sopenharmony_ci /* Use shoffset for result token offset here */ 5962306a36Sopenharmony_ci rdr->shoffset = priv->config.res_offset; 6062306a36Sopenharmony_ci rdr->base = dmam_alloc_coherent(priv->dev, 6162306a36Sopenharmony_ci rdr->offset * EIP197_DEFAULT_RING_SIZE, 6262306a36Sopenharmony_ci &rdr->base_dma, GFP_KERNEL); 6362306a36Sopenharmony_ci if (!rdr->base) 6462306a36Sopenharmony_ci return -ENOMEM; 6562306a36Sopenharmony_ci rdr->write = rdr->base; 6662306a36Sopenharmony_ci rdr->base_end = rdr->base + rdr->offset * (EIP197_DEFAULT_RING_SIZE - 1); 6762306a36Sopenharmony_ci rdr->read = rdr->base; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciinline int safexcel_select_ring(struct safexcel_crypto_priv *priv) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci return (atomic_inc_return(&priv->ring_used) % priv->config.rings); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void *safexcel_ring_next_cwptr(struct safexcel_crypto_priv *priv, 7862306a36Sopenharmony_ci struct safexcel_desc_ring *ring, 7962306a36Sopenharmony_ci bool first, 8062306a36Sopenharmony_ci struct safexcel_token **atoken) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci void *ptr = ring->write; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (first) 8562306a36Sopenharmony_ci *atoken = ring->shwrite; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if ((ring->write == ring->read - ring->offset) || 8862306a36Sopenharmony_ci (ring->read == ring->base && ring->write == ring->base_end)) 8962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (ring->write == ring->base_end) { 9262306a36Sopenharmony_ci ring->write = ring->base; 9362306a36Sopenharmony_ci ring->shwrite = ring->shbase; 9462306a36Sopenharmony_ci } else { 9562306a36Sopenharmony_ci ring->write += ring->offset; 9662306a36Sopenharmony_ci ring->shwrite += ring->shoffset; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return ptr; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void *safexcel_ring_next_rwptr(struct safexcel_crypto_priv *priv, 10362306a36Sopenharmony_ci struct safexcel_desc_ring *ring, 10462306a36Sopenharmony_ci struct result_data_desc **rtoken) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci void *ptr = ring->write; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Result token at relative offset shoffset */ 10962306a36Sopenharmony_ci *rtoken = ring->write + ring->shoffset; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if ((ring->write == ring->read - ring->offset) || 11262306a36Sopenharmony_ci (ring->read == ring->base && ring->write == ring->base_end)) 11362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (ring->write == ring->base_end) 11662306a36Sopenharmony_ci ring->write = ring->base; 11762306a36Sopenharmony_ci else 11862306a36Sopenharmony_ci ring->write += ring->offset; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return ptr; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_civoid *safexcel_ring_next_rptr(struct safexcel_crypto_priv *priv, 12462306a36Sopenharmony_ci struct safexcel_desc_ring *ring) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci void *ptr = ring->read; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (ring->write == ring->read) 12962306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (ring->read == ring->base_end) 13262306a36Sopenharmony_ci ring->read = ring->base; 13362306a36Sopenharmony_ci else 13462306a36Sopenharmony_ci ring->read += ring->offset; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return ptr; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciinline void *safexcel_ring_curr_rptr(struct safexcel_crypto_priv *priv, 14062306a36Sopenharmony_ci int ring) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return rdr->read; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciinline int safexcel_ring_first_rdr_index(struct safexcel_crypto_priv *priv, 14862306a36Sopenharmony_ci int ring) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return (rdr->read - rdr->base) / rdr->offset; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciinline int safexcel_ring_rdr_rdesc_index(struct safexcel_crypto_priv *priv, 15662306a36Sopenharmony_ci int ring, 15762306a36Sopenharmony_ci struct safexcel_result_desc *rdesc) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return ((void *)rdesc - rdr->base) / rdr->offset; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_civoid safexcel_ring_rollback_wptr(struct safexcel_crypto_priv *priv, 16562306a36Sopenharmony_ci struct safexcel_desc_ring *ring) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (ring->write == ring->read) 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (ring->write == ring->base) { 17162306a36Sopenharmony_ci ring->write = ring->base_end; 17262306a36Sopenharmony_ci ring->shwrite = ring->shbase_end; 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci ring->write -= ring->offset; 17562306a36Sopenharmony_ci ring->shwrite -= ring->shoffset; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistruct safexcel_command_desc *safexcel_add_cdesc(struct safexcel_crypto_priv *priv, 18062306a36Sopenharmony_ci int ring_id, 18162306a36Sopenharmony_ci bool first, bool last, 18262306a36Sopenharmony_ci dma_addr_t data, u32 data_len, 18362306a36Sopenharmony_ci u32 full_data_len, 18462306a36Sopenharmony_ci dma_addr_t context, 18562306a36Sopenharmony_ci struct safexcel_token **atoken) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct safexcel_command_desc *cdesc; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci cdesc = safexcel_ring_next_cwptr(priv, &priv->ring[ring_id].cdr, 19062306a36Sopenharmony_ci first, atoken); 19162306a36Sopenharmony_ci if (IS_ERR(cdesc)) 19262306a36Sopenharmony_ci return cdesc; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci cdesc->particle_size = data_len; 19562306a36Sopenharmony_ci cdesc->rsvd0 = 0; 19662306a36Sopenharmony_ci cdesc->last_seg = last; 19762306a36Sopenharmony_ci cdesc->first_seg = first; 19862306a36Sopenharmony_ci cdesc->additional_cdata_size = 0; 19962306a36Sopenharmony_ci cdesc->rsvd1 = 0; 20062306a36Sopenharmony_ci cdesc->data_lo = lower_32_bits(data); 20162306a36Sopenharmony_ci cdesc->data_hi = upper_32_bits(data); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (first) { 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * Note that the length here MUST be >0 or else the EIP(1)97 20662306a36Sopenharmony_ci * may hang. Newer EIP197 firmware actually incorporates this 20762306a36Sopenharmony_ci * fix already, but that doesn't help the EIP97 and we may 20862306a36Sopenharmony_ci * also be running older firmware. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci cdesc->control_data.packet_length = full_data_len ?: 1; 21162306a36Sopenharmony_ci cdesc->control_data.options = EIP197_OPTION_MAGIC_VALUE | 21262306a36Sopenharmony_ci EIP197_OPTION_64BIT_CTX | 21362306a36Sopenharmony_ci EIP197_OPTION_CTX_CTRL_IN_CMD | 21462306a36Sopenharmony_ci EIP197_OPTION_RC_AUTO; 21562306a36Sopenharmony_ci cdesc->control_data.type = EIP197_TYPE_BCLA; 21662306a36Sopenharmony_ci cdesc->control_data.context_lo = lower_32_bits(context) | 21762306a36Sopenharmony_ci EIP197_CONTEXT_SMALL; 21862306a36Sopenharmony_ci cdesc->control_data.context_hi = upper_32_bits(context); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return cdesc; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistruct safexcel_result_desc *safexcel_add_rdesc(struct safexcel_crypto_priv *priv, 22562306a36Sopenharmony_ci int ring_id, 22662306a36Sopenharmony_ci bool first, bool last, 22762306a36Sopenharmony_ci dma_addr_t data, u32 len) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct safexcel_result_desc *rdesc; 23062306a36Sopenharmony_ci struct result_data_desc *rtoken; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci rdesc = safexcel_ring_next_rwptr(priv, &priv->ring[ring_id].rdr, 23362306a36Sopenharmony_ci &rtoken); 23462306a36Sopenharmony_ci if (IS_ERR(rdesc)) 23562306a36Sopenharmony_ci return rdesc; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci rdesc->particle_size = len; 23862306a36Sopenharmony_ci rdesc->rsvd0 = 0; 23962306a36Sopenharmony_ci rdesc->descriptor_overflow = 1; /* assume error */ 24062306a36Sopenharmony_ci rdesc->buffer_overflow = 1; /* assume error */ 24162306a36Sopenharmony_ci rdesc->last_seg = last; 24262306a36Sopenharmony_ci rdesc->first_seg = first; 24362306a36Sopenharmony_ci rdesc->result_size = EIP197_RD64_RESULT_SIZE; 24462306a36Sopenharmony_ci rdesc->rsvd1 = 0; 24562306a36Sopenharmony_ci rdesc->data_lo = lower_32_bits(data); 24662306a36Sopenharmony_ci rdesc->data_hi = upper_32_bits(data); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Clear length in result token */ 24962306a36Sopenharmony_ci rtoken->packet_length = 0; 25062306a36Sopenharmony_ci /* Assume errors - HW will clear if not the case */ 25162306a36Sopenharmony_ci rtoken->error_code = 0x7fff; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return rdesc; 25462306a36Sopenharmony_ci} 255