mirror_ubuntu-kernels/drivers/crypto/inside-secure/safexcel_ring.c

255 lines
6.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 Marvell
*
* Antoine Tenart <antoine.tenart@free-electrons.com>
*/
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include "safexcel.h"
int safexcel_init_ring_descriptors(struct safexcel_crypto_priv *priv,
struct safexcel_desc_ring *cdr,
struct safexcel_desc_ring *rdr)
{
int i;
struct safexcel_command_desc *cdesc;
dma_addr_t atok;
/* Actual command descriptor ring */
cdr->offset = priv->config.cd_offset;
cdr->base = dmam_alloc_coherent(priv->dev,
cdr->offset * EIP197_DEFAULT_RING_SIZE,
&cdr->base_dma, GFP_KERNEL);
if (!cdr->base)
return -ENOMEM;
cdr->write = cdr->base;
cdr->base_end = cdr->base + cdr->offset * (EIP197_DEFAULT_RING_SIZE - 1);
cdr->read = cdr->base;
/* Command descriptor shadow ring for storing additional token data */
cdr->shoffset = priv->config.cdsh_offset;
cdr->shbase = dmam_alloc_coherent(priv->dev,
cdr->shoffset *
EIP197_DEFAULT_RING_SIZE,
&cdr->shbase_dma, GFP_KERNEL);
if (!cdr->shbase)
return -ENOMEM;
cdr->shwrite = cdr->shbase;
cdr->shbase_end = cdr->shbase + cdr->shoffset *
(EIP197_DEFAULT_RING_SIZE - 1);
/*
* Populate command descriptors with physical pointers to shadow descs.
* Note that we only need to do this once if we don't overwrite them.
*/
cdesc = cdr->base;
atok = cdr->shbase_dma;
for (i = 0; i < EIP197_DEFAULT_RING_SIZE; i++) {
cdesc->atok_lo = lower_32_bits(atok);
cdesc->atok_hi = upper_32_bits(atok);
cdesc = (void *)cdesc + cdr->offset;
atok += cdr->shoffset;
}
rdr->offset = priv->config.rd_offset;
/* Use shoffset for result token offset here */
rdr->shoffset = priv->config.res_offset;
rdr->base = dmam_alloc_coherent(priv->dev,
rdr->offset * EIP197_DEFAULT_RING_SIZE,
&rdr->base_dma, GFP_KERNEL);
if (!rdr->base)
return -ENOMEM;
rdr->write = rdr->base;
rdr->base_end = rdr->base + rdr->offset * (EIP197_DEFAULT_RING_SIZE - 1);
rdr->read = rdr->base;
return 0;
}
inline int safexcel_select_ring(struct safexcel_crypto_priv *priv)
{
return (atomic_inc_return(&priv->ring_used) % priv->config.rings);
}
static void *safexcel_ring_next_cwptr(struct safexcel_crypto_priv *priv,
struct safexcel_desc_ring *ring,
bool first,
struct safexcel_token **atoken)
{
void *ptr = ring->write;
if (first)
*atoken = ring->shwrite;
if ((ring->write == ring->read - ring->offset) ||
(ring->read == ring->base && ring->write == ring->base_end))
return ERR_PTR(-ENOMEM);
if (ring->write == ring->base_end) {
ring->write = ring->base;
ring->shwrite = ring->shbase;
} else {
ring->write += ring->offset;
ring->shwrite += ring->shoffset;
}
return ptr;
}
static void *safexcel_ring_next_rwptr(struct safexcel_crypto_priv *priv,
struct safexcel_desc_ring *ring,
struct result_data_desc **rtoken)
{
void *ptr = ring->write;
/* Result token at relative offset shoffset */
*rtoken = ring->write + ring->shoffset;
if ((ring->write == ring->read - ring->offset) ||
(ring->read == ring->base && ring->write == ring->base_end))
return ERR_PTR(-ENOMEM);
if (ring->write == ring->base_end)
ring->write = ring->base;
else
ring->write += ring->offset;
return ptr;
}
void *safexcel_ring_next_rptr(struct safexcel_crypto_priv *priv,
struct safexcel_desc_ring *ring)
{
void *ptr = ring->read;
if (ring->write == ring->read)
return ERR_PTR(-ENOENT);
if (ring->read == ring->base_end)
ring->read = ring->base;
else
ring->read += ring->offset;
return ptr;
}
inline void *safexcel_ring_curr_rptr(struct safexcel_crypto_priv *priv,
int ring)
{
struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr;
return rdr->read;
}
inline int safexcel_ring_first_rdr_index(struct safexcel_crypto_priv *priv,
int ring)
{
struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr;
return (rdr->read - rdr->base) / rdr->offset;
}
inline int safexcel_ring_rdr_rdesc_index(struct safexcel_crypto_priv *priv,
int ring,
struct safexcel_result_desc *rdesc)
{
struct safexcel_desc_ring *rdr = &priv->ring[ring].rdr;
return ((void *)rdesc - rdr->base) / rdr->offset;
}
void safexcel_ring_rollback_wptr(struct safexcel_crypto_priv *priv,
struct safexcel_desc_ring *ring)
{
if (ring->write == ring->read)
return;
if (ring->write == ring->base) {
ring->write = ring->base_end;
ring->shwrite = ring->shbase_end;
} else {
ring->write -= ring->offset;
ring->shwrite -= ring->shoffset;
}
}
struct safexcel_command_desc *safexcel_add_cdesc(struct safexcel_crypto_priv *priv,
int ring_id,
bool first, bool last,
dma_addr_t data, u32 data_len,
u32 full_data_len,
dma_addr_t context,
struct safexcel_token **atoken)
{
struct safexcel_command_desc *cdesc;
cdesc = safexcel_ring_next_cwptr(priv, &priv->ring[ring_id].cdr,
first, atoken);
if (IS_ERR(cdesc))
return cdesc;
cdesc->particle_size = data_len;
cdesc->rsvd0 = 0;
cdesc->last_seg = last;
cdesc->first_seg = first;
cdesc->additional_cdata_size = 0;
cdesc->rsvd1 = 0;
cdesc->data_lo = lower_32_bits(data);
cdesc->data_hi = upper_32_bits(data);
if (first) {
/*
* Note that the length here MUST be >0 or else the EIP(1)97
* may hang. Newer EIP197 firmware actually incorporates this
* fix already, but that doesn't help the EIP97 and we may
* also be running older firmware.
*/
cdesc->control_data.packet_length = full_data_len ?: 1;
cdesc->control_data.options = EIP197_OPTION_MAGIC_VALUE |
EIP197_OPTION_64BIT_CTX |
EIP197_OPTION_CTX_CTRL_IN_CMD |
EIP197_OPTION_RC_AUTO;
cdesc->control_data.type = EIP197_TYPE_BCLA;
cdesc->control_data.context_lo = lower_32_bits(context) |
EIP197_CONTEXT_SMALL;
cdesc->control_data.context_hi = upper_32_bits(context);
}
return cdesc;
}
struct safexcel_result_desc *safexcel_add_rdesc(struct safexcel_crypto_priv *priv,
int ring_id,
bool first, bool last,
dma_addr_t data, u32 len)
{
struct safexcel_result_desc *rdesc;
struct result_data_desc *rtoken;
rdesc = safexcel_ring_next_rwptr(priv, &priv->ring[ring_id].rdr,
&rtoken);
if (IS_ERR(rdesc))
return rdesc;
rdesc->particle_size = len;
rdesc->rsvd0 = 0;
rdesc->descriptor_overflow = 1; /* assume error */
rdesc->buffer_overflow = 1; /* assume error */
rdesc->last_seg = last;
rdesc->first_seg = first;
rdesc->result_size = EIP197_RD64_RESULT_SIZE;
rdesc->rsvd1 = 0;
rdesc->data_lo = lower_32_bits(data);
rdesc->data_hi = upper_32_bits(data);
/* Clear length in result token */
rtoken->packet_length = 0;
/* Assume errors - HW will clear if not the case */
rtoken->error_code = 0x7fff;
return rdesc;
}