mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-02 08:32:55 +00:00

The Allwinner H616 (and later) SoCs support more than 32 bits worth of physical addresses. To accommodate the larger address space, the CE task descriptor fields holding addresses are now encoded as "word addresses", so take the actual address divided by four. This is true for the fields within the descriptor, but also for the descriptor base address, in the CE_TDA register. Wrap all accesses to those fields in a function, which will do the required division if needed. For now this in unused, so there should be no change in behaviour. Signed-off-by: Andre Przywara <andre.przywara@arm.com> Reviewed-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
124 lines
2.8 KiB
C
124 lines
2.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* sun8i-ce-trng.c - hardware cryptographic offloader for
|
|
* Allwinner H3/A64/H5/H2+/H6/R40 SoC
|
|
*
|
|
* Copyright (C) 2015-2020 Corentin Labbe <clabbe@baylibre.com>
|
|
*
|
|
* This file handle the TRNG
|
|
*
|
|
* You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst
|
|
*/
|
|
#include "sun8i-ce.h"
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/hw_random.h>
|
|
/*
|
|
* Note that according to the algorithm ID, 2 versions of the TRNG exists,
|
|
* The first present in H3/H5/R40/A64 and the second present in H6.
|
|
* This file adds support for both, but only the second is working
|
|
* reliabily according to rngtest.
|
|
**/
|
|
|
|
static int sun8i_ce_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
|
{
|
|
struct sun8i_ce_dev *ce;
|
|
dma_addr_t dma_dst;
|
|
int err = 0;
|
|
int flow = 3;
|
|
unsigned int todo;
|
|
struct sun8i_ce_flow *chan;
|
|
struct ce_task *cet;
|
|
u32 common;
|
|
void *d;
|
|
|
|
ce = container_of(rng, struct sun8i_ce_dev, trng);
|
|
|
|
/* round the data length to a multiple of 32*/
|
|
todo = max + 32;
|
|
todo -= todo % 32;
|
|
|
|
d = kzalloc(todo, GFP_KERNEL | GFP_DMA);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
|
|
#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
|
|
ce->hwrng_stat_req++;
|
|
ce->hwrng_stat_bytes += todo;
|
|
#endif
|
|
|
|
dma_dst = dma_map_single(ce->dev, d, todo, DMA_FROM_DEVICE);
|
|
if (dma_mapping_error(ce->dev, dma_dst)) {
|
|
dev_err(ce->dev, "Cannot DMA MAP DST\n");
|
|
err = -EFAULT;
|
|
goto err_dst;
|
|
}
|
|
|
|
err = pm_runtime_resume_and_get(ce->dev);
|
|
if (err < 0)
|
|
goto err_pm;
|
|
|
|
mutex_lock(&ce->rnglock);
|
|
chan = &ce->chanlist[flow];
|
|
|
|
cet = &chan->tl[0];
|
|
memset(cet, 0, sizeof(struct ce_task));
|
|
|
|
cet->t_id = cpu_to_le32(flow);
|
|
common = ce->variant->trng | CE_COMM_INT;
|
|
cet->t_common_ctl = cpu_to_le32(common);
|
|
|
|
/* recent CE (H6) need length in bytes, in word otherwise */
|
|
if (ce->variant->trng_t_dlen_in_bytes)
|
|
cet->t_dlen = cpu_to_le32(todo);
|
|
else
|
|
cet->t_dlen = cpu_to_le32(todo / 4);
|
|
|
|
cet->t_sym_ctl = 0;
|
|
cet->t_asym_ctl = 0;
|
|
|
|
cet->t_dst[0].addr = desc_addr_val_le32(ce, dma_dst);
|
|
cet->t_dst[0].len = cpu_to_le32(todo / 4);
|
|
ce->chanlist[flow].timeout = todo;
|
|
|
|
err = sun8i_ce_run_task(ce, 3, "TRNG");
|
|
mutex_unlock(&ce->rnglock);
|
|
|
|
pm_runtime_put(ce->dev);
|
|
|
|
err_pm:
|
|
dma_unmap_single(ce->dev, dma_dst, todo, DMA_FROM_DEVICE);
|
|
|
|
if (!err) {
|
|
memcpy(data, d, max);
|
|
err = max;
|
|
}
|
|
err_dst:
|
|
kfree_sensitive(d);
|
|
return err;
|
|
}
|
|
|
|
int sun8i_ce_hwrng_register(struct sun8i_ce_dev *ce)
|
|
{
|
|
int ret;
|
|
|
|
if (ce->variant->trng == CE_ID_NOTSUPP) {
|
|
dev_info(ce->dev, "TRNG not supported\n");
|
|
return 0;
|
|
}
|
|
ce->trng.name = "sun8i Crypto Engine TRNG";
|
|
ce->trng.read = sun8i_ce_trng_read;
|
|
|
|
ret = hwrng_register(&ce->trng);
|
|
if (ret)
|
|
dev_err(ce->dev, "Fail to register the TRNG\n");
|
|
return ret;
|
|
}
|
|
|
|
void sun8i_ce_hwrng_unregister(struct sun8i_ce_dev *ce)
|
|
{
|
|
if (ce->variant->trng == CE_ID_NOTSUPP)
|
|
return;
|
|
hwrng_unregister(&ce->trng);
|
|
}
|