18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * caam - Freescale FSL CAAM support for hw_random 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci * Copyright 2018-2019 NXP 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on caamalg.c crypto API driver. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 138c2ecf20Sopenharmony_ci#include <linux/completion.h> 148c2ecf20Sopenharmony_ci#include <linux/atomic.h> 158c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "compat.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "regs.h" 208c2ecf20Sopenharmony_ci#include "intern.h" 218c2ecf20Sopenharmony_ci#include "desc_constr.h" 228c2ecf20Sopenharmony_ci#include "jr.h" 238c2ecf20Sopenharmony_ci#include "error.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define CAAM_RNG_MAX_FIFO_STORE_SIZE 16 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * Length of used descriptors, see caam_init_desc() 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ + \ 318c2ecf20Sopenharmony_ci CAAM_CMD_SZ + \ 328c2ecf20Sopenharmony_ci CAAM_CMD_SZ + CAAM_PTR_SZ_MAX) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* rng per-device context */ 358c2ecf20Sopenharmony_cistruct caam_rng_ctx { 368c2ecf20Sopenharmony_ci struct hwrng rng; 378c2ecf20Sopenharmony_ci struct device *jrdev; 388c2ecf20Sopenharmony_ci struct device *ctrldev; 398c2ecf20Sopenharmony_ci void *desc_async; 408c2ecf20Sopenharmony_ci void *desc_sync; 418c2ecf20Sopenharmony_ci struct work_struct worker; 428c2ecf20Sopenharmony_ci struct kfifo fifo; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct caam_rng_job_ctx { 468c2ecf20Sopenharmony_ci struct completion *done; 478c2ecf20Sopenharmony_ci int *err; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return (struct caam_rng_ctx *)r->priv; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void caam_rng_done(struct device *jrdev, u32 *desc, u32 err, 568c2ecf20Sopenharmony_ci void *context) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct caam_rng_job_ctx *jctx = context; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (err) 618c2ecf20Sopenharmony_ci *jctx->err = caam_jr_strstatus(jrdev, err); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci complete(jctx->done); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci init_job_desc(desc, 0); /* + 1 cmd_sz */ 698c2ecf20Sopenharmony_ci /* Generate random bytes: + 1 cmd_sz */ 708c2ecf20Sopenharmony_ci append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG | 718c2ecf20Sopenharmony_ci OP_ALG_PR_ON); 728c2ecf20Sopenharmony_ci /* Store bytes: + 1 cmd_sz + caam_ptr_sz */ 738c2ecf20Sopenharmony_ci append_fifo_store(desc, dst_dma, 748c2ecf20Sopenharmony_ci CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS, 778c2ecf20Sopenharmony_ci 16, 4, desc, desc_bytes(desc), 1); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return desc; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int caam_rng_read_one(struct device *jrdev, 838c2ecf20Sopenharmony_ci void *dst, int len, 848c2ecf20Sopenharmony_ci void *desc, 858c2ecf20Sopenharmony_ci struct completion *done) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci dma_addr_t dst_dma; 888c2ecf20Sopenharmony_ci int err, ret = 0; 898c2ecf20Sopenharmony_ci struct caam_rng_job_ctx jctx = { 908c2ecf20Sopenharmony_ci .done = done, 918c2ecf20Sopenharmony_ci .err = &ret, 928c2ecf20Sopenharmony_ci }; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci len = CAAM_RNG_MAX_FIFO_STORE_SIZE; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE); 978c2ecf20Sopenharmony_ci if (dma_mapping_error(jrdev, dst_dma)) { 988c2ecf20Sopenharmony_ci dev_err(jrdev, "unable to map destination memory\n"); 998c2ecf20Sopenharmony_ci return -ENOMEM; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci init_completion(done); 1038c2ecf20Sopenharmony_ci err = caam_jr_enqueue(jrdev, 1048c2ecf20Sopenharmony_ci caam_init_desc(desc, dst_dma), 1058c2ecf20Sopenharmony_ci caam_rng_done, &jctx); 1068c2ecf20Sopenharmony_ci if (err == -EINPROGRESS) { 1078c2ecf20Sopenharmony_ci wait_for_completion(done); 1088c2ecf20Sopenharmony_ci err = 0; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return err ?: (ret ?: len); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void caam_rng_fill_async(struct caam_rng_ctx *ctx) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct scatterlist sg[1]; 1198c2ecf20Sopenharmony_ci struct completion done; 1208c2ecf20Sopenharmony_ci int len, nents; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci sg_init_table(sg, ARRAY_SIZE(sg)); 1238c2ecf20Sopenharmony_ci nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg), 1248c2ecf20Sopenharmony_ci CAAM_RNG_MAX_FIFO_STORE_SIZE); 1258c2ecf20Sopenharmony_ci if (!nents) 1268c2ecf20Sopenharmony_ci return; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci len = caam_rng_read_one(ctx->jrdev, sg_virt(&sg[0]), 1298c2ecf20Sopenharmony_ci sg[0].length, 1308c2ecf20Sopenharmony_ci ctx->desc_async, 1318c2ecf20Sopenharmony_ci &done); 1328c2ecf20Sopenharmony_ci if (len < 0) 1338c2ecf20Sopenharmony_ci return; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci kfifo_dma_in_finish(&ctx->fifo, len); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void caam_rng_worker(struct work_struct *work) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx, 1418c2ecf20Sopenharmony_ci worker); 1428c2ecf20Sopenharmony_ci caam_rng_fill_async(ctx); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng); 1488c2ecf20Sopenharmony_ci int out; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (wait) { 1518c2ecf20Sopenharmony_ci struct completion done; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return caam_rng_read_one(ctx->jrdev, dst, max, 1548c2ecf20Sopenharmony_ci ctx->desc_sync, &done); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci out = kfifo_out(&ctx->fifo, dst, max); 1588c2ecf20Sopenharmony_ci if (kfifo_is_empty(&ctx->fifo)) 1598c2ecf20Sopenharmony_ci schedule_work(&ctx->worker); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return out; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void caam_cleanup(struct hwrng *rng) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci flush_work(&ctx->worker); 1698c2ecf20Sopenharmony_ci caam_jr_free(ctx->jrdev); 1708c2ecf20Sopenharmony_ci kfifo_free(&ctx->fifo); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int caam_init(struct hwrng *rng) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng); 1768c2ecf20Sopenharmony_ci int err; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ctx->desc_sync = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN, 1798c2ecf20Sopenharmony_ci GFP_DMA | GFP_KERNEL); 1808c2ecf20Sopenharmony_ci if (!ctx->desc_sync) 1818c2ecf20Sopenharmony_ci return -ENOMEM; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ctx->desc_async = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN, 1848c2ecf20Sopenharmony_ci GFP_DMA | GFP_KERNEL); 1858c2ecf20Sopenharmony_ci if (!ctx->desc_async) 1868c2ecf20Sopenharmony_ci return -ENOMEM; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (kfifo_alloc(&ctx->fifo, CAAM_RNG_MAX_FIFO_STORE_SIZE, 1898c2ecf20Sopenharmony_ci GFP_DMA | GFP_KERNEL)) 1908c2ecf20Sopenharmony_ci return -ENOMEM; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci INIT_WORK(&ctx->worker, caam_rng_worker); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ctx->jrdev = caam_jr_alloc(); 1958c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(ctx->jrdev); 1968c2ecf20Sopenharmony_ci if (err) { 1978c2ecf20Sopenharmony_ci kfifo_free(&ctx->fifo); 1988c2ecf20Sopenharmony_ci pr_err("Job Ring Device allocation for transform failed\n"); 1998c2ecf20Sopenharmony_ci return err; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * Fill async buffer to have early randomness data for 2048c2ecf20Sopenharmony_ci * hw_random 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci caam_rng_fill_async(ctx); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciint caam_rng_init(struct device *ctrldev); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_civoid caam_rng_exit(struct device *ctrldev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci devres_release_group(ctrldev, caam_rng_init); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ciint caam_rng_init(struct device *ctrldev) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct caam_rng_ctx *ctx; 2218c2ecf20Sopenharmony_ci u32 rng_inst; 2228c2ecf20Sopenharmony_ci struct caam_drv_private *priv = dev_get_drvdata(ctrldev); 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Check for an instantiated RNG before registration */ 2268c2ecf20Sopenharmony_ci if (priv->era < 10) 2278c2ecf20Sopenharmony_ci rng_inst = (rd_reg32(&priv->ctrl->perfmon.cha_num_ls) & 2288c2ecf20Sopenharmony_ci CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT; 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci rng_inst = rd_reg32(&priv->ctrl->vreg.rng) & CHA_VER_NUM_MASK; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (!rng_inst) 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (!devres_open_group(ctrldev, caam_rng_init, GFP_KERNEL)) 2368c2ecf20Sopenharmony_ci return -ENOMEM; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci ctx = devm_kzalloc(ctrldev, sizeof(*ctx), GFP_KERNEL); 2398c2ecf20Sopenharmony_ci if (!ctx) 2408c2ecf20Sopenharmony_ci return -ENOMEM; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci ctx->ctrldev = ctrldev; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ctx->rng.name = "rng-caam"; 2458c2ecf20Sopenharmony_ci ctx->rng.init = caam_init; 2468c2ecf20Sopenharmony_ci ctx->rng.cleanup = caam_cleanup; 2478c2ecf20Sopenharmony_ci ctx->rng.read = caam_read; 2488c2ecf20Sopenharmony_ci ctx->rng.priv = (unsigned long)ctx; 2498c2ecf20Sopenharmony_ci ctx->rng.quality = 1024; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dev_info(ctrldev, "registering rng-caam\n"); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ret = devm_hwrng_register(ctrldev, &ctx->rng); 2548c2ecf20Sopenharmony_ci if (ret) { 2558c2ecf20Sopenharmony_ci caam_rng_exit(ctrldev); 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci devres_close_group(ctrldev, caam_rng_init); 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 262