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