162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sun8i-ce-trng.c - hardware cryptographic offloader for 462306a36Sopenharmony_ci * Allwinner H3/A64/H5/H2+/H6/R40 SoC 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2015-2020 Corentin Labbe <clabbe@baylibre.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file handle the TRNG 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include "sun8i-ce.h" 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <linux/hw_random.h> 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * Note that according to the algorithm ID, 2 versions of the TRNG exists, 1862306a36Sopenharmony_ci * The first present in H3/H5/R40/A64 and the second present in H6. 1962306a36Sopenharmony_ci * This file adds support for both, but only the second is working 2062306a36Sopenharmony_ci * reliabily according to rngtest. 2162306a36Sopenharmony_ci **/ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int sun8i_ce_trng_read(struct hwrng *rng, void *data, size_t max, bool wait) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct sun8i_ce_dev *ce; 2662306a36Sopenharmony_ci dma_addr_t dma_dst; 2762306a36Sopenharmony_ci int err = 0; 2862306a36Sopenharmony_ci int flow = 3; 2962306a36Sopenharmony_ci unsigned int todo; 3062306a36Sopenharmony_ci struct sun8i_ce_flow *chan; 3162306a36Sopenharmony_ci struct ce_task *cet; 3262306a36Sopenharmony_ci u32 common; 3362306a36Sopenharmony_ci void *d; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci ce = container_of(rng, struct sun8i_ce_dev, trng); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* round the data length to a multiple of 32*/ 3862306a36Sopenharmony_ci todo = max + 32; 3962306a36Sopenharmony_ci todo -= todo % 32; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci d = kzalloc(todo, GFP_KERNEL | GFP_DMA); 4262306a36Sopenharmony_ci if (!d) 4362306a36Sopenharmony_ci return -ENOMEM; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG 4662306a36Sopenharmony_ci ce->hwrng_stat_req++; 4762306a36Sopenharmony_ci ce->hwrng_stat_bytes += todo; 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci dma_dst = dma_map_single(ce->dev, d, todo, DMA_FROM_DEVICE); 5162306a36Sopenharmony_ci if (dma_mapping_error(ce->dev, dma_dst)) { 5262306a36Sopenharmony_ci dev_err(ce->dev, "Cannot DMA MAP DST\n"); 5362306a36Sopenharmony_ci err = -EFAULT; 5462306a36Sopenharmony_ci goto err_dst; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci err = pm_runtime_resume_and_get(ce->dev); 5862306a36Sopenharmony_ci if (err < 0) 5962306a36Sopenharmony_ci goto err_pm; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci mutex_lock(&ce->rnglock); 6262306a36Sopenharmony_ci chan = &ce->chanlist[flow]; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci cet = &chan->tl[0]; 6562306a36Sopenharmony_ci memset(cet, 0, sizeof(struct ce_task)); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci cet->t_id = cpu_to_le32(flow); 6862306a36Sopenharmony_ci common = ce->variant->trng | CE_COMM_INT; 6962306a36Sopenharmony_ci cet->t_common_ctl = cpu_to_le32(common); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* recent CE (H6) need length in bytes, in word otherwise */ 7262306a36Sopenharmony_ci if (ce->variant->trng_t_dlen_in_bytes) 7362306a36Sopenharmony_ci cet->t_dlen = cpu_to_le32(todo); 7462306a36Sopenharmony_ci else 7562306a36Sopenharmony_ci cet->t_dlen = cpu_to_le32(todo / 4); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci cet->t_sym_ctl = 0; 7862306a36Sopenharmony_ci cet->t_asym_ctl = 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci cet->t_dst[0].addr = cpu_to_le32(dma_dst); 8162306a36Sopenharmony_ci cet->t_dst[0].len = cpu_to_le32(todo / 4); 8262306a36Sopenharmony_ci ce->chanlist[flow].timeout = todo; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci err = sun8i_ce_run_task(ce, 3, "TRNG"); 8562306a36Sopenharmony_ci mutex_unlock(&ce->rnglock); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci pm_runtime_put(ce->dev); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cierr_pm: 9062306a36Sopenharmony_ci dma_unmap_single(ce->dev, dma_dst, todo, DMA_FROM_DEVICE); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!err) { 9362306a36Sopenharmony_ci memcpy(data, d, max); 9462306a36Sopenharmony_ci err = max; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_cierr_dst: 9762306a36Sopenharmony_ci kfree_sensitive(d); 9862306a36Sopenharmony_ci return err; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciint sun8i_ce_hwrng_register(struct sun8i_ce_dev *ce) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (ce->variant->trng == CE_ID_NOTSUPP) { 10662306a36Sopenharmony_ci dev_info(ce->dev, "TRNG not supported\n"); 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci ce->trng.name = "sun8i Crypto Engine TRNG"; 11062306a36Sopenharmony_ci ce->trng.read = sun8i_ce_trng_read; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = hwrng_register(&ce->trng); 11362306a36Sopenharmony_ci if (ret) 11462306a36Sopenharmony_ci dev_err(ce->dev, "Fail to register the TRNG\n"); 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_civoid sun8i_ce_hwrng_unregister(struct sun8i_ce_dev *ce) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci if (ce->variant->trng == CE_ID_NOTSUPP) 12162306a36Sopenharmony_ci return; 12262306a36Sopenharmony_ci hwrng_unregister(&ce->trng); 12362306a36Sopenharmony_ci} 124