| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * linux/drivers/char/hw_random/asr-rng.c - Random Number Generator driver |
| * |
| * Copyright (C) 2023 ASR Micro Limited |
| * |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/platform_device.h> |
| #include <linux/of.h> |
| #include <linux/clk.h> |
| #include <linux/hw_random.h> |
| #include <linux/io.h> |
| #include <linux/slab.h> |
| #include <linux/sched.h> |
| #include <linux/fs.h> |
| #ifdef CONFIG_TEE |
| #include <linux/tee_drv.h> |
| #endif |
| |
| #include "asr-rng-optee.h" |
| #include "asr-geu-optee.h" |
| |
| static struct teec_uuid pta_rng_uuid = ASR_RNG_ACCESS_UUID; |
| |
| static int asr_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) |
| { |
| int ret = 0; |
| size_t readsize; |
| size_t size = max < 4096 ? max : 4096; |
| |
| (void)wait; |
| |
| ret = asrgeu_optee_acquire_ta_buff(&pta_rng_uuid, ASR_RNG_GET_DATA, |
| data, size, &readsize); |
| |
| if (!ret) |
| return readsize; |
| |
| return 0; |
| } |
| |
| int asr_geu_rng_register(struct asr_geu_dev *geu_dd) |
| { |
| int err = 0; |
| struct device *dev = geu_dd->dev; |
| struct asr_geu_rng *hwrng; |
| |
| hwrng = devm_kzalloc(dev, sizeof(*hwrng), GFP_KERNEL); |
| if (!hwrng) |
| return -ENOMEM; |
| |
| geu_dd->asr_rng = hwrng; |
| |
| hwrng->rng.name = "asr"; |
| hwrng->rng.read = asr_rng_read; |
| hwrng->rng.quality = 1000; |
| |
| err = hwrng_register(&hwrng->rng); |
| if (err) { |
| dev_err(dev, "failed to register asr_rng!\n"); |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| int asr_geu_rng_unregister(struct asr_geu_dev *geu_dd) |
| { |
| struct device *dev = geu_dd->dev; |
| struct asr_geu_rng *hwrng = geu_dd->asr_rng; |
| |
| hwrng_unregister(&hwrng->rng); |
| |
| devm_kfree(dev, hwrng); |
| |
| return 0; |
| } |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_AUTHOR("Yu Zhang <yuzhang@asrmicro.com>"); |
| MODULE_DESCRIPTION("ASR H/W RNG driver with optee-os"); |