+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (c) 2019 HiSilicon Limited. */
-/* Copyright (c) 2025 Loongson Technology Corporation Limited. */
-
-#include <linux/crypto.h>
-#include <linux/err.h>
-#include <linux/hw_random.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/mfd/loongson-se.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/platform_device.h>
-#include <linux/random.h>
-#include <crypto/internal/rng.h>
-
-#define SE_SEED_SIZE 32
-
-struct loongson_rng_list {
- struct mutex lock;
- struct list_head list;
- int registered;
-};
-
-struct loongson_rng {
- u32 used;
- struct loongson_se_engine *engine;
- struct list_head list;
- struct mutex lock;
-};
-
-struct loongson_rng_ctx {
- struct loongson_rng *rng;
-};
-
-struct loongson_rng_cmd {
- u32 cmd_id;
- union {
- u32 len;
- u32 ret;
- } u;
- u32 seed_off;
- u32 out_off;
- u32 pad[4];
-};
-
-static struct loongson_rng_list rng_devices = {
- .lock = __MUTEX_INITIALIZER(rng_devices.lock),
- .list = LIST_HEAD_INIT(rng_devices.list),
-};
-
-static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src,
- unsigned int slen, u8 *dstn, unsigned int dlen)
-{
- struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm);
- struct loongson_rng *rng = ctx->rng;
- struct loongson_rng_cmd *cmd = rng->engine->command;
- int err, len;
-
- mutex_lock(&rng->lock);
- cmd->seed_off = 0;
- do {
- len = min(dlen, rng->engine->buffer_size);
- cmd = rng->engine->command;
- cmd->u.len = len;
- err = loongson_se_send_engine_cmd(rng->engine);
- if (err)
- break;
-
- cmd = rng->engine->command_ret;
- if (cmd->u.ret) {
- err = -EIO;
- break;
- }
-
- memcpy(dstn, rng->engine->data_buffer, len);
- dlen -= len;
- dstn += len;
- } while (dlen > 0);
- mutex_unlock(&rng->lock);
-
- return err;
-}
-
-static int loongson_rng_init(struct crypto_tfm *tfm)
-{
- struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm);
- struct loongson_rng *rng;
- u32 min_used = U32_MAX;
-
- mutex_lock(&rng_devices.lock);
- list_for_each_entry(rng, &rng_devices.list, list) {
- if (rng->used < min_used) {
- ctx->rng = rng;
- min_used = rng->used;
- }
- }
- ctx->rng->used++;
- mutex_unlock(&rng_devices.lock);
-
- return 0;
-}
-
-static void loongson_rng_exit(struct crypto_tfm *tfm)
-{
- struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm);
-
- mutex_lock(&rng_devices.lock);
- ctx->rng->used--;
- mutex_unlock(&rng_devices.lock);
-}
-
-static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed,
- unsigned int slen)
-{
- struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm);
- struct loongson_rng *rng = ctx->rng;
- struct loongson_rng_cmd *cmd;
- int err;
-
- if (slen < SE_SEED_SIZE)
- return -EINVAL;
-
- slen = min(slen, rng->engine->buffer_size);
-
- mutex_lock(&rng->lock);
- cmd = rng->engine->command;
- cmd->u.len = slen;
- cmd->seed_off = rng->engine->buffer_off;
- memcpy(rng->engine->data_buffer, seed, slen);
- err = loongson_se_send_engine_cmd(rng->engine);
- if (err)
- goto out;
-
- cmd = rng->engine->command_ret;
- if (cmd->u.ret)
- err = -EIO;
-out:
- mutex_unlock(&rng->lock);
-
- return err;
-}
-
-static struct rng_alg loongson_rng_alg = {
- .generate = loongson_rng_generate,
- .seed = loongson_rng_seed,
- .seedsize = SE_SEED_SIZE,
- .base = {
- .cra_name = "stdrng",
- .cra_driver_name = "loongson_stdrng",
- .cra_priority = 300,
- .cra_ctxsize = sizeof(struct loongson_rng_ctx),
- .cra_module = THIS_MODULE,
- .cra_init = loongson_rng_init,
- .cra_exit = loongson_rng_exit,
- },
-};
-
-static int loongson_rng_probe(struct platform_device *pdev)
-{
- struct loongson_rng_cmd *cmd;
- struct loongson_rng *rng;
- int ret = 0;
-
- rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
- if (!rng)
- return -ENOMEM;
-
- rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG);
- if (!rng->engine)
- return -ENODEV;
- cmd = rng->engine->command;
- cmd->cmd_id = SE_CMD_RNG;
- cmd->out_off = rng->engine->buffer_off;
- mutex_init(&rng->lock);
-
- mutex_lock(&rng_devices.lock);
-
- if (!rng_devices.registered) {
- ret = crypto_register_rng(&loongson_rng_alg);
- if (ret) {
- dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret);
- goto out;
- }
- rng_devices.registered = 1;
- }
-
- list_add_tail(&rng->list, &rng_devices.list);
-out:
- mutex_unlock(&rng_devices.lock);
-
- return ret;
-}
-
-static struct platform_driver loongson_rng_driver = {
- .probe = loongson_rng_probe,
- .driver = {
- .name = "loongson-rng",
- },
-};
-module_platform_driver(loongson_rng_driver);
-
-MODULE_ALIAS("platform:loongson-rng");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>");
-MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>");
-MODULE_DESCRIPTION("Loongson Random Number Generator driver");