1*766b2d72SQunqin Zhao // SPDX-License-Identifier: GPL-2.0 2*766b2d72SQunqin Zhao /* Copyright (c) 2019 HiSilicon Limited. */ 3*766b2d72SQunqin Zhao /* Copyright (c) 2025 Loongson Technology Corporation Limited. */ 4*766b2d72SQunqin Zhao 5*766b2d72SQunqin Zhao #include <linux/crypto.h> 6*766b2d72SQunqin Zhao #include <linux/err.h> 7*766b2d72SQunqin Zhao #include <linux/hw_random.h> 8*766b2d72SQunqin Zhao #include <linux/io.h> 9*766b2d72SQunqin Zhao #include <linux/iopoll.h> 10*766b2d72SQunqin Zhao #include <linux/kernel.h> 11*766b2d72SQunqin Zhao #include <linux/list.h> 12*766b2d72SQunqin Zhao #include <linux/mfd/loongson-se.h> 13*766b2d72SQunqin Zhao #include <linux/module.h> 14*766b2d72SQunqin Zhao #include <linux/mutex.h> 15*766b2d72SQunqin Zhao #include <linux/platform_device.h> 16*766b2d72SQunqin Zhao #include <linux/random.h> 17*766b2d72SQunqin Zhao #include <crypto/internal/rng.h> 18*766b2d72SQunqin Zhao 19*766b2d72SQunqin Zhao #define SE_SEED_SIZE 32 20*766b2d72SQunqin Zhao 21*766b2d72SQunqin Zhao struct loongson_rng_list { 22*766b2d72SQunqin Zhao struct mutex lock; 23*766b2d72SQunqin Zhao struct list_head list; 24*766b2d72SQunqin Zhao int registered; 25*766b2d72SQunqin Zhao }; 26*766b2d72SQunqin Zhao 27*766b2d72SQunqin Zhao struct loongson_rng { 28*766b2d72SQunqin Zhao u32 used; 29*766b2d72SQunqin Zhao struct loongson_se_engine *engine; 30*766b2d72SQunqin Zhao struct list_head list; 31*766b2d72SQunqin Zhao struct mutex lock; 32*766b2d72SQunqin Zhao }; 33*766b2d72SQunqin Zhao 34*766b2d72SQunqin Zhao struct loongson_rng_ctx { 35*766b2d72SQunqin Zhao struct loongson_rng *rng; 36*766b2d72SQunqin Zhao }; 37*766b2d72SQunqin Zhao 38*766b2d72SQunqin Zhao struct loongson_rng_cmd { 39*766b2d72SQunqin Zhao u32 cmd_id; 40*766b2d72SQunqin Zhao union { 41*766b2d72SQunqin Zhao u32 len; 42*766b2d72SQunqin Zhao u32 ret; 43*766b2d72SQunqin Zhao } u; 44*766b2d72SQunqin Zhao u32 seed_off; 45*766b2d72SQunqin Zhao u32 out_off; 46*766b2d72SQunqin Zhao u32 pad[4]; 47*766b2d72SQunqin Zhao }; 48*766b2d72SQunqin Zhao 49*766b2d72SQunqin Zhao static struct loongson_rng_list rng_devices = { 50*766b2d72SQunqin Zhao .lock = __MUTEX_INITIALIZER(rng_devices.lock), 51*766b2d72SQunqin Zhao .list = LIST_HEAD_INIT(rng_devices.list), 52*766b2d72SQunqin Zhao }; 53*766b2d72SQunqin Zhao 54*766b2d72SQunqin Zhao static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src, 55*766b2d72SQunqin Zhao unsigned int slen, u8 *dstn, unsigned int dlen) 56*766b2d72SQunqin Zhao { 57*766b2d72SQunqin Zhao struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); 58*766b2d72SQunqin Zhao struct loongson_rng *rng = ctx->rng; 59*766b2d72SQunqin Zhao struct loongson_rng_cmd *cmd = rng->engine->command; 60*766b2d72SQunqin Zhao int err, len; 61*766b2d72SQunqin Zhao 62*766b2d72SQunqin Zhao mutex_lock(&rng->lock); 63*766b2d72SQunqin Zhao cmd->seed_off = 0; 64*766b2d72SQunqin Zhao do { 65*766b2d72SQunqin Zhao len = min(dlen, rng->engine->buffer_size); 66*766b2d72SQunqin Zhao cmd = rng->engine->command; 67*766b2d72SQunqin Zhao cmd->u.len = len; 68*766b2d72SQunqin Zhao err = loongson_se_send_engine_cmd(rng->engine); 69*766b2d72SQunqin Zhao if (err) 70*766b2d72SQunqin Zhao break; 71*766b2d72SQunqin Zhao 72*766b2d72SQunqin Zhao cmd = rng->engine->command_ret; 73*766b2d72SQunqin Zhao if (cmd->u.ret) { 74*766b2d72SQunqin Zhao err = -EIO; 75*766b2d72SQunqin Zhao break; 76*766b2d72SQunqin Zhao } 77*766b2d72SQunqin Zhao 78*766b2d72SQunqin Zhao memcpy(dstn, rng->engine->data_buffer, len); 79*766b2d72SQunqin Zhao dlen -= len; 80*766b2d72SQunqin Zhao dstn += len; 81*766b2d72SQunqin Zhao } while (dlen > 0); 82*766b2d72SQunqin Zhao mutex_unlock(&rng->lock); 83*766b2d72SQunqin Zhao 84*766b2d72SQunqin Zhao return err; 85*766b2d72SQunqin Zhao } 86*766b2d72SQunqin Zhao 87*766b2d72SQunqin Zhao static int loongson_rng_init(struct crypto_tfm *tfm) 88*766b2d72SQunqin Zhao { 89*766b2d72SQunqin Zhao struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); 90*766b2d72SQunqin Zhao struct loongson_rng *rng; 91*766b2d72SQunqin Zhao u32 min_used = U32_MAX; 92*766b2d72SQunqin Zhao 93*766b2d72SQunqin Zhao mutex_lock(&rng_devices.lock); 94*766b2d72SQunqin Zhao list_for_each_entry(rng, &rng_devices.list, list) { 95*766b2d72SQunqin Zhao if (rng->used < min_used) { 96*766b2d72SQunqin Zhao ctx->rng = rng; 97*766b2d72SQunqin Zhao min_used = rng->used; 98*766b2d72SQunqin Zhao } 99*766b2d72SQunqin Zhao } 100*766b2d72SQunqin Zhao ctx->rng->used++; 101*766b2d72SQunqin Zhao mutex_unlock(&rng_devices.lock); 102*766b2d72SQunqin Zhao 103*766b2d72SQunqin Zhao return 0; 104*766b2d72SQunqin Zhao } 105*766b2d72SQunqin Zhao 106*766b2d72SQunqin Zhao static void loongson_rng_exit(struct crypto_tfm *tfm) 107*766b2d72SQunqin Zhao { 108*766b2d72SQunqin Zhao struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); 109*766b2d72SQunqin Zhao 110*766b2d72SQunqin Zhao mutex_lock(&rng_devices.lock); 111*766b2d72SQunqin Zhao ctx->rng->used--; 112*766b2d72SQunqin Zhao mutex_unlock(&rng_devices.lock); 113*766b2d72SQunqin Zhao } 114*766b2d72SQunqin Zhao 115*766b2d72SQunqin Zhao static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed, 116*766b2d72SQunqin Zhao unsigned int slen) 117*766b2d72SQunqin Zhao { 118*766b2d72SQunqin Zhao struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); 119*766b2d72SQunqin Zhao struct loongson_rng *rng = ctx->rng; 120*766b2d72SQunqin Zhao struct loongson_rng_cmd *cmd; 121*766b2d72SQunqin Zhao int err; 122*766b2d72SQunqin Zhao 123*766b2d72SQunqin Zhao if (slen < SE_SEED_SIZE) 124*766b2d72SQunqin Zhao return -EINVAL; 125*766b2d72SQunqin Zhao 126*766b2d72SQunqin Zhao slen = min(slen, rng->engine->buffer_size); 127*766b2d72SQunqin Zhao 128*766b2d72SQunqin Zhao mutex_lock(&rng->lock); 129*766b2d72SQunqin Zhao cmd = rng->engine->command; 130*766b2d72SQunqin Zhao cmd->u.len = slen; 131*766b2d72SQunqin Zhao cmd->seed_off = rng->engine->buffer_off; 132*766b2d72SQunqin Zhao memcpy(rng->engine->data_buffer, seed, slen); 133*766b2d72SQunqin Zhao err = loongson_se_send_engine_cmd(rng->engine); 134*766b2d72SQunqin Zhao if (err) 135*766b2d72SQunqin Zhao goto out; 136*766b2d72SQunqin Zhao 137*766b2d72SQunqin Zhao cmd = rng->engine->command_ret; 138*766b2d72SQunqin Zhao if (cmd->u.ret) 139*766b2d72SQunqin Zhao err = -EIO; 140*766b2d72SQunqin Zhao out: 141*766b2d72SQunqin Zhao mutex_unlock(&rng->lock); 142*766b2d72SQunqin Zhao 143*766b2d72SQunqin Zhao return err; 144*766b2d72SQunqin Zhao } 145*766b2d72SQunqin Zhao 146*766b2d72SQunqin Zhao static struct rng_alg loongson_rng_alg = { 147*766b2d72SQunqin Zhao .generate = loongson_rng_generate, 148*766b2d72SQunqin Zhao .seed = loongson_rng_seed, 149*766b2d72SQunqin Zhao .seedsize = SE_SEED_SIZE, 150*766b2d72SQunqin Zhao .base = { 151*766b2d72SQunqin Zhao .cra_name = "stdrng", 152*766b2d72SQunqin Zhao .cra_driver_name = "loongson_stdrng", 153*766b2d72SQunqin Zhao .cra_priority = 300, 154*766b2d72SQunqin Zhao .cra_ctxsize = sizeof(struct loongson_rng_ctx), 155*766b2d72SQunqin Zhao .cra_module = THIS_MODULE, 156*766b2d72SQunqin Zhao .cra_init = loongson_rng_init, 157*766b2d72SQunqin Zhao .cra_exit = loongson_rng_exit, 158*766b2d72SQunqin Zhao }, 159*766b2d72SQunqin Zhao }; 160*766b2d72SQunqin Zhao 161*766b2d72SQunqin Zhao static int loongson_rng_probe(struct platform_device *pdev) 162*766b2d72SQunqin Zhao { 163*766b2d72SQunqin Zhao struct loongson_rng_cmd *cmd; 164*766b2d72SQunqin Zhao struct loongson_rng *rng; 165*766b2d72SQunqin Zhao int ret = 0; 166*766b2d72SQunqin Zhao 167*766b2d72SQunqin Zhao rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 168*766b2d72SQunqin Zhao if (!rng) 169*766b2d72SQunqin Zhao return -ENOMEM; 170*766b2d72SQunqin Zhao 171*766b2d72SQunqin Zhao rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG); 172*766b2d72SQunqin Zhao if (!rng->engine) 173*766b2d72SQunqin Zhao return -ENODEV; 174*766b2d72SQunqin Zhao cmd = rng->engine->command; 175*766b2d72SQunqin Zhao cmd->cmd_id = SE_CMD_RNG; 176*766b2d72SQunqin Zhao cmd->out_off = rng->engine->buffer_off; 177*766b2d72SQunqin Zhao mutex_init(&rng->lock); 178*766b2d72SQunqin Zhao 179*766b2d72SQunqin Zhao mutex_lock(&rng_devices.lock); 180*766b2d72SQunqin Zhao 181*766b2d72SQunqin Zhao if (!rng_devices.registered) { 182*766b2d72SQunqin Zhao ret = crypto_register_rng(&loongson_rng_alg); 183*766b2d72SQunqin Zhao if (ret) { 184*766b2d72SQunqin Zhao dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); 185*766b2d72SQunqin Zhao goto out; 186*766b2d72SQunqin Zhao } 187*766b2d72SQunqin Zhao rng_devices.registered = 1; 188*766b2d72SQunqin Zhao } 189*766b2d72SQunqin Zhao 190*766b2d72SQunqin Zhao list_add_tail(&rng->list, &rng_devices.list); 191*766b2d72SQunqin Zhao out: 192*766b2d72SQunqin Zhao mutex_unlock(&rng_devices.lock); 193*766b2d72SQunqin Zhao 194*766b2d72SQunqin Zhao return ret; 195*766b2d72SQunqin Zhao } 196*766b2d72SQunqin Zhao 197*766b2d72SQunqin Zhao static struct platform_driver loongson_rng_driver = { 198*766b2d72SQunqin Zhao .probe = loongson_rng_probe, 199*766b2d72SQunqin Zhao .driver = { 200*766b2d72SQunqin Zhao .name = "loongson-rng", 201*766b2d72SQunqin Zhao }, 202*766b2d72SQunqin Zhao }; 203*766b2d72SQunqin Zhao module_platform_driver(loongson_rng_driver); 204*766b2d72SQunqin Zhao 205*766b2d72SQunqin Zhao MODULE_ALIAS("platform:loongson-rng"); 206*766b2d72SQunqin Zhao MODULE_LICENSE("GPL"); 207*766b2d72SQunqin Zhao MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>"); 208*766b2d72SQunqin Zhao MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>"); 209*766b2d72SQunqin Zhao MODULE_DESCRIPTION("Loongson Random Number Generator driver"); 210