forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
6.6 KiB
254 lines
6.6 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) 2017 Marvell |
|
* |
|
* Antoine Tenart <[email protected]> |
|
*/ |
|
|
|
#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; |
|
}
|
|
|