1190873a0S周琰杰 (Zhou Yanjie) // SPDX-License-Identifier: GPL-2.0 2190873a0S周琰杰 (Zhou Yanjie) /* 3190873a0S周琰杰 (Zhou Yanjie) * Ingenic Random Number Generator driver 4190873a0S周琰杰 (Zhou Yanjie) * Copyright (c) 2017 PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com> 5190873a0S周琰杰 (Zhou Yanjie) * Copyright (c) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> 6190873a0S周琰杰 (Zhou Yanjie) */ 7190873a0S周琰杰 (Zhou Yanjie) 8190873a0S周琰杰 (Zhou Yanjie) #include <linux/err.h> 9190873a0S周琰杰 (Zhou Yanjie) #include <linux/kernel.h> 10190873a0S周琰杰 (Zhou Yanjie) #include <linux/hw_random.h> 11190873a0S周琰杰 (Zhou Yanjie) #include <linux/io.h> 12190873a0S周琰杰 (Zhou Yanjie) #include <linux/iopoll.h> 13190873a0S周琰杰 (Zhou Yanjie) #include <linux/module.h> 14190873a0S周琰杰 (Zhou Yanjie) #include <linux/of_device.h> 15190873a0S周琰杰 (Zhou Yanjie) #include <linux/platform_device.h> 16190873a0S周琰杰 (Zhou Yanjie) #include <linux/slab.h> 17190873a0S周琰杰 (Zhou Yanjie) 18190873a0S周琰杰 (Zhou Yanjie) /* RNG register offsets */ 19190873a0S周琰杰 (Zhou Yanjie) #define RNG_REG_ERNG_OFFSET 0x0 20190873a0S周琰杰 (Zhou Yanjie) #define RNG_REG_RNG_OFFSET 0x4 21190873a0S周琰杰 (Zhou Yanjie) 22190873a0S周琰杰 (Zhou Yanjie) /* bits within the ERND register */ 23190873a0S周琰杰 (Zhou Yanjie) #define ERNG_READY BIT(31) 24190873a0S周琰杰 (Zhou Yanjie) #define ERNG_ENABLE BIT(0) 25190873a0S周琰杰 (Zhou Yanjie) 26190873a0S周琰杰 (Zhou Yanjie) enum ingenic_rng_version { 27190873a0S周琰杰 (Zhou Yanjie) ID_JZ4780, 28190873a0S周琰杰 (Zhou Yanjie) ID_X1000, 29190873a0S周琰杰 (Zhou Yanjie) }; 30190873a0S周琰杰 (Zhou Yanjie) 31190873a0S周琰杰 (Zhou Yanjie) /* Device associated memory */ 32190873a0S周琰杰 (Zhou Yanjie) struct ingenic_rng { 33190873a0S周琰杰 (Zhou Yanjie) enum ingenic_rng_version version; 34190873a0S周琰杰 (Zhou Yanjie) 35190873a0S周琰杰 (Zhou Yanjie) void __iomem *base; 36190873a0S周琰杰 (Zhou Yanjie) struct hwrng rng; 37190873a0S周琰杰 (Zhou Yanjie) }; 38190873a0S周琰杰 (Zhou Yanjie) 39190873a0S周琰杰 (Zhou Yanjie) static int ingenic_rng_init(struct hwrng *rng) 40190873a0S周琰杰 (Zhou Yanjie) { 41190873a0S周琰杰 (Zhou Yanjie) struct ingenic_rng *priv = container_of(rng, struct ingenic_rng, rng); 42190873a0S周琰杰 (Zhou Yanjie) 43190873a0S周琰杰 (Zhou Yanjie) writel(ERNG_ENABLE, priv->base + RNG_REG_ERNG_OFFSET); 44190873a0S周琰杰 (Zhou Yanjie) 45190873a0S周琰杰 (Zhou Yanjie) return 0; 46190873a0S周琰杰 (Zhou Yanjie) } 47190873a0S周琰杰 (Zhou Yanjie) 48190873a0S周琰杰 (Zhou Yanjie) static void ingenic_rng_cleanup(struct hwrng *rng) 49190873a0S周琰杰 (Zhou Yanjie) { 50190873a0S周琰杰 (Zhou Yanjie) struct ingenic_rng *priv = container_of(rng, struct ingenic_rng, rng); 51190873a0S周琰杰 (Zhou Yanjie) 52190873a0S周琰杰 (Zhou Yanjie) writel(0, priv->base + RNG_REG_ERNG_OFFSET); 53190873a0S周琰杰 (Zhou Yanjie) } 54190873a0S周琰杰 (Zhou Yanjie) 55190873a0S周琰杰 (Zhou Yanjie) static int ingenic_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 56190873a0S周琰杰 (Zhou Yanjie) { 57190873a0S周琰杰 (Zhou Yanjie) struct ingenic_rng *priv = container_of(rng, struct ingenic_rng, rng); 58190873a0S周琰杰 (Zhou Yanjie) u32 *data = buf; 59190873a0S周琰杰 (Zhou Yanjie) u32 status; 60190873a0S周琰杰 (Zhou Yanjie) int ret; 61190873a0S周琰杰 (Zhou Yanjie) 62190873a0S周琰杰 (Zhou Yanjie) if (priv->version >= ID_X1000) { 63190873a0S周琰杰 (Zhou Yanjie) ret = readl_poll_timeout(priv->base + RNG_REG_ERNG_OFFSET, status, 64190873a0S周琰杰 (Zhou Yanjie) status & ERNG_READY, 10, 1000); 65190873a0S周琰杰 (Zhou Yanjie) if (ret == -ETIMEDOUT) { 66190873a0S周琰杰 (Zhou Yanjie) pr_err("%s: Wait for RNG data ready timeout\n", __func__); 67190873a0S周琰杰 (Zhou Yanjie) return ret; 68190873a0S周琰杰 (Zhou Yanjie) } 69190873a0S周琰杰 (Zhou Yanjie) } else { 70190873a0S周琰杰 (Zhou Yanjie) /* 71190873a0S周琰杰 (Zhou Yanjie) * A delay is required so that the current RNG data is not bit shifted 72190873a0S周琰杰 (Zhou Yanjie) * version of previous RNG data which could happen if random data is 73190873a0S周琰杰 (Zhou Yanjie) * read continuously from this device. 74190873a0S周琰杰 (Zhou Yanjie) */ 75190873a0S周琰杰 (Zhou Yanjie) udelay(20); 76190873a0S周琰杰 (Zhou Yanjie) } 77190873a0S周琰杰 (Zhou Yanjie) 78190873a0S周琰杰 (Zhou Yanjie) *data = readl(priv->base + RNG_REG_RNG_OFFSET); 79190873a0S周琰杰 (Zhou Yanjie) 80190873a0S周琰杰 (Zhou Yanjie) return 4; 81190873a0S周琰杰 (Zhou Yanjie) } 82190873a0S周琰杰 (Zhou Yanjie) 83190873a0S周琰杰 (Zhou Yanjie) static int ingenic_rng_probe(struct platform_device *pdev) 84190873a0S周琰杰 (Zhou Yanjie) { 85190873a0S周琰杰 (Zhou Yanjie) struct ingenic_rng *priv; 86190873a0S周琰杰 (Zhou Yanjie) int ret; 87190873a0S周琰杰 (Zhou Yanjie) 88190873a0S周琰杰 (Zhou Yanjie) priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 89190873a0S周琰杰 (Zhou Yanjie) if (!priv) 90190873a0S周琰杰 (Zhou Yanjie) return -ENOMEM; 91190873a0S周琰杰 (Zhou Yanjie) 92190873a0S周琰杰 (Zhou Yanjie) priv->base = devm_platform_ioremap_resource(pdev, 0); 93190873a0S周琰杰 (Zhou Yanjie) if (IS_ERR(priv->base)) { 94190873a0S周琰杰 (Zhou Yanjie) pr_err("%s: Failed to map RNG registers\n", __func__); 9511a954eeSWei Yongjun return PTR_ERR(priv->base); 96190873a0S周琰杰 (Zhou Yanjie) } 97190873a0S周琰杰 (Zhou Yanjie) 98190873a0S周琰杰 (Zhou Yanjie) priv->version = (enum ingenic_rng_version)of_device_get_match_data(&pdev->dev); 99190873a0S周琰杰 (Zhou Yanjie) 100190873a0S周琰杰 (Zhou Yanjie) priv->rng.name = pdev->name; 101190873a0S周琰杰 (Zhou Yanjie) priv->rng.init = ingenic_rng_init; 102190873a0S周琰杰 (Zhou Yanjie) priv->rng.cleanup = ingenic_rng_cleanup; 103190873a0S周琰杰 (Zhou Yanjie) priv->rng.read = ingenic_rng_read; 104190873a0S周琰杰 (Zhou Yanjie) 105190873a0S周琰杰 (Zhou Yanjie) ret = hwrng_register(&priv->rng); 106190873a0S周琰杰 (Zhou Yanjie) if (ret) { 107190873a0S周琰杰 (Zhou Yanjie) dev_err(&pdev->dev, "Failed to register hwrng\n"); 10811a954eeSWei Yongjun return ret; 109190873a0S周琰杰 (Zhou Yanjie) } 110190873a0S周琰杰 (Zhou Yanjie) 111190873a0S周琰杰 (Zhou Yanjie) platform_set_drvdata(pdev, priv); 112190873a0S周琰杰 (Zhou Yanjie) 113190873a0S周琰杰 (Zhou Yanjie) dev_info(&pdev->dev, "Ingenic RNG driver registered\n"); 114190873a0S周琰杰 (Zhou Yanjie) return 0; 115190873a0S周琰杰 (Zhou Yanjie) } 116190873a0S周琰杰 (Zhou Yanjie) 117190873a0S周琰杰 (Zhou Yanjie) static int ingenic_rng_remove(struct platform_device *pdev) 118190873a0S周琰杰 (Zhou Yanjie) { 119190873a0S周琰杰 (Zhou Yanjie) struct ingenic_rng *priv = platform_get_drvdata(pdev); 120190873a0S周琰杰 (Zhou Yanjie) 121190873a0S周琰杰 (Zhou Yanjie) hwrng_unregister(&priv->rng); 122190873a0S周琰杰 (Zhou Yanjie) 123190873a0S周琰杰 (Zhou Yanjie) writel(0, priv->base + RNG_REG_ERNG_OFFSET); 124190873a0S周琰杰 (Zhou Yanjie) 125190873a0S周琰杰 (Zhou Yanjie) return 0; 126190873a0S周琰杰 (Zhou Yanjie) } 127190873a0S周琰杰 (Zhou Yanjie) 128190873a0S周琰杰 (Zhou Yanjie) static const struct of_device_id ingenic_rng_of_match[] = { 129190873a0S周琰杰 (Zhou Yanjie) { .compatible = "ingenic,jz4780-rng", .data = (void *) ID_JZ4780 }, 130190873a0S周琰杰 (Zhou Yanjie) { .compatible = "ingenic,x1000-rng", .data = (void *) ID_X1000 }, 131190873a0S周琰杰 (Zhou Yanjie) { /* sentinel */ } 132190873a0S周琰杰 (Zhou Yanjie) }; 133190873a0S周琰杰 (Zhou Yanjie) MODULE_DEVICE_TABLE(of, ingenic_rng_of_match); 134190873a0S周琰杰 (Zhou Yanjie) 135190873a0S周琰杰 (Zhou Yanjie) static struct platform_driver ingenic_rng_driver = { 136190873a0S周琰杰 (Zhou Yanjie) .probe = ingenic_rng_probe, 137190873a0S周琰杰 (Zhou Yanjie) .remove = ingenic_rng_remove, 138190873a0S周琰杰 (Zhou Yanjie) .driver = { 139190873a0S周琰杰 (Zhou Yanjie) .name = "ingenic-rng", 140190873a0S周琰杰 (Zhou Yanjie) .of_match_table = ingenic_rng_of_match, 141190873a0S周琰杰 (Zhou Yanjie) }, 142190873a0S周琰杰 (Zhou Yanjie) }; 143190873a0S周琰杰 (Zhou Yanjie) 144190873a0S周琰杰 (Zhou Yanjie) module_platform_driver(ingenic_rng_driver); 145190873a0S周琰杰 (Zhou Yanjie) 146190873a0S周琰杰 (Zhou Yanjie) MODULE_LICENSE("GPL"); 147190873a0S周琰杰 (Zhou Yanjie) MODULE_AUTHOR("PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>"); 148190873a0S周琰杰 (Zhou Yanjie) MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>"); 149190873a0S周琰杰 (Zhou Yanjie) MODULE_DESCRIPTION("Ingenic Random Number Generator driver"); 150