From: 周琰杰 (Zhou Yanjie) Date: Thu, 23 Jul 2020 06:24:46 +0000 (+0800) Subject: crypto: ingenic - Add hardware RNG for Ingenic JZ4780 and X1000 X-Git-Tag: v5.10.7~1843^2~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=190873a0ea4500433ae818521cad20d37f9ee059;p=platform%2Fkernel%2Flinux-rpi.git crypto: ingenic - Add hardware RNG for Ingenic JZ4780 and X1000 Add JZ4780 SoC and X1000 SoC random number generator driver, based on PrasannaKumar Muralidharan's JZ4780 RNG driver. Tested-by: 周正 (Zhou Zheng) Tested-by: Mathieu Malaterre Suggested-by: Jeffrey Walton Signed-off-by: PrasannaKumar Muralidharan Signed-off-by: 周琰杰 (Zhou Yanjie) Signed-off-by: Herbert Xu --- diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 98f95a0..f976a49 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -267,6 +267,21 @@ config HW_RANDOM_IMX_RNGC If unsure, say Y. +config HW_RANDOM_INGENIC_RNG + tristate "Ingenic Random Number Generator support" + depends on HW_RANDOM + depends on MACH_JZ4780 || MACH_X1000 + default HW_RANDOM + help + This driver provides kernel-side support for the Random Number Generator + hardware found in ingenic JZ4780 and X1000 SoC. MIPS Creator CI20 uses + JZ4780 SoC, YSH & ATIL CU1000-Neo uses X1000 SoC. + + To compile this driver as a module, choose M here: the + module will be called ingenic-rng. + + If unsure, say Y. + config HW_RANDOM_NOMADIK tristate "ST-Ericsson Nomadik Random Number Generator support" depends on ARCH_NOMADIK diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 4ed9feb..26ae068 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o obj-$(CONFIG_HW_RANDOM_IMX_RNGC) += imx-rngc.o +obj-$(CONFIG_HW_RANDOM_INGENIC_RNG) += ingenic-rng.o obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o diff --git a/drivers/char/hw_random/ingenic-rng.c b/drivers/char/hw_random/ingenic-rng.c new file mode 100644 index 0000000..d704cef --- /dev/null +++ b/drivers/char/hw_random/ingenic-rng.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Ingenic Random Number Generator driver + * Copyright (c) 2017 PrasannaKumar Muralidharan + * Copyright (c) 2020 周琰杰 (Zhou Yanjie) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* RNG register offsets */ +#define RNG_REG_ERNG_OFFSET 0x0 +#define RNG_REG_RNG_OFFSET 0x4 + +/* bits within the ERND register */ +#define ERNG_READY BIT(31) +#define ERNG_ENABLE BIT(0) + +enum ingenic_rng_version { + ID_JZ4780, + ID_X1000, +}; + +/* Device associated memory */ +struct ingenic_rng { + enum ingenic_rng_version version; + + void __iomem *base; + struct hwrng rng; +}; + +static int ingenic_rng_init(struct hwrng *rng) +{ + struct ingenic_rng *priv = container_of(rng, struct ingenic_rng, rng); + + writel(ERNG_ENABLE, priv->base + RNG_REG_ERNG_OFFSET); + + return 0; +} + +static void ingenic_rng_cleanup(struct hwrng *rng) +{ + struct ingenic_rng *priv = container_of(rng, struct ingenic_rng, rng); + + writel(0, priv->base + RNG_REG_ERNG_OFFSET); +} + +static int ingenic_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct ingenic_rng *priv = container_of(rng, struct ingenic_rng, rng); + u32 *data = buf; + u32 status; + int ret; + + if (priv->version >= ID_X1000) { + ret = readl_poll_timeout(priv->base + RNG_REG_ERNG_OFFSET, status, + status & ERNG_READY, 10, 1000); + if (ret == -ETIMEDOUT) { + pr_err("%s: Wait for RNG data ready timeout\n", __func__); + return ret; + } + } else { + /* + * A delay is required so that the current RNG data is not bit shifted + * version of previous RNG data which could happen if random data is + * read continuously from this device. + */ + udelay(20); + } + + *data = readl(priv->base + RNG_REG_RNG_OFFSET); + + return 4; +} + +static int ingenic_rng_probe(struct platform_device *pdev) +{ + struct ingenic_rng *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) { + pr_err("%s: Failed to map RNG registers\n", __func__); + ret = PTR_ERR(priv->base); + goto err_free_rng; + } + + priv->version = (enum ingenic_rng_version)of_device_get_match_data(&pdev->dev); + + priv->rng.name = pdev->name; + priv->rng.init = ingenic_rng_init; + priv->rng.cleanup = ingenic_rng_cleanup; + priv->rng.read = ingenic_rng_read; + + ret = hwrng_register(&priv->rng); + if (ret) { + dev_err(&pdev->dev, "Failed to register hwrng\n"); + goto err_free_rng; + } + + platform_set_drvdata(pdev, priv); + + dev_info(&pdev->dev, "Ingenic RNG driver registered\n"); + return 0; + +err_free_rng: + kfree(priv); + return ret; +} + +static int ingenic_rng_remove(struct platform_device *pdev) +{ + struct ingenic_rng *priv = platform_get_drvdata(pdev); + + hwrng_unregister(&priv->rng); + + writel(0, priv->base + RNG_REG_ERNG_OFFSET); + + return 0; +} + +static const struct of_device_id ingenic_rng_of_match[] = { + { .compatible = "ingenic,jz4780-rng", .data = (void *) ID_JZ4780 }, + { .compatible = "ingenic,x1000-rng", .data = (void *) ID_X1000 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ingenic_rng_of_match); + +static struct platform_driver ingenic_rng_driver = { + .probe = ingenic_rng_probe, + .remove = ingenic_rng_remove, + .driver = { + .name = "ingenic-rng", + .of_match_table = ingenic_rng_of_match, + }, +}; + +module_platform_driver(ingenic_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("PrasannaKumar Muralidharan "); +MODULE_AUTHOR("周琰杰 (Zhou Yanjie) "); +MODULE_DESCRIPTION("Ingenic Random Number Generator driver");