// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 Marvell * * Antoine Tenart */ #include #include #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; }