0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/clk.h>
0009 #include <linux/err.h>
0010 #include <linux/kernel.h>
0011 #include <linux/hw_random.h>
0012 #include <linux/io.h>
0013 #include <linux/iopoll.h>
0014 #include <linux/module.h>
0015 #include <linux/of_device.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/slab.h>
0018
0019
0020 #define TRNG_REG_CFG_OFFSET 0x00
0021 #define TRNG_REG_RANDOMNUM_OFFSET 0x04
0022 #define TRNG_REG_STATUS_OFFSET 0x08
0023
0024
0025 #define CFG_RDY_CLR BIT(12)
0026 #define CFG_INT_MASK BIT(11)
0027 #define CFG_GEN_EN BIT(0)
0028
0029
0030 #define STATUS_RANDOM_RDY BIT(0)
0031
0032 struct ingenic_trng {
0033 void __iomem *base;
0034 struct clk *clk;
0035 struct hwrng rng;
0036 };
0037
0038 static int ingenic_trng_init(struct hwrng *rng)
0039 {
0040 struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);
0041 unsigned int ctrl;
0042
0043 ctrl = readl(trng->base + TRNG_REG_CFG_OFFSET);
0044 ctrl |= CFG_GEN_EN;
0045 writel(ctrl, trng->base + TRNG_REG_CFG_OFFSET);
0046
0047 return 0;
0048 }
0049
0050 static void ingenic_trng_cleanup(struct hwrng *rng)
0051 {
0052 struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);
0053 unsigned int ctrl;
0054
0055 ctrl = readl(trng->base + TRNG_REG_CFG_OFFSET);
0056 ctrl &= ~CFG_GEN_EN;
0057 writel(ctrl, trng->base + TRNG_REG_CFG_OFFSET);
0058 }
0059
0060 static int ingenic_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
0061 {
0062 struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);
0063 u32 *data = buf;
0064 u32 status;
0065 int ret;
0066
0067 ret = readl_poll_timeout(trng->base + TRNG_REG_STATUS_OFFSET, status,
0068 status & STATUS_RANDOM_RDY, 10, 1000);
0069 if (ret == -ETIMEDOUT) {
0070 pr_err("%s: Wait for DTRNG data ready timeout\n", __func__);
0071 return ret;
0072 }
0073
0074 *data = readl(trng->base + TRNG_REG_RANDOMNUM_OFFSET);
0075
0076 return 4;
0077 }
0078
0079 static int ingenic_trng_probe(struct platform_device *pdev)
0080 {
0081 struct ingenic_trng *trng;
0082 int ret;
0083
0084 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
0085 if (!trng)
0086 return -ENOMEM;
0087
0088 trng->base = devm_platform_ioremap_resource(pdev, 0);
0089 if (IS_ERR(trng->base)) {
0090 pr_err("%s: Failed to map DTRNG registers\n", __func__);
0091 ret = PTR_ERR(trng->base);
0092 return PTR_ERR(trng->base);
0093 }
0094
0095 trng->clk = devm_clk_get(&pdev->dev, NULL);
0096 if (IS_ERR(trng->clk)) {
0097 ret = PTR_ERR(trng->clk);
0098 pr_crit("%s: Cannot get DTRNG clock\n", __func__);
0099 return PTR_ERR(trng->clk);
0100 }
0101
0102 ret = clk_prepare_enable(trng->clk);
0103 if (ret) {
0104 pr_crit("%s: Unable to enable DTRNG clock\n", __func__);
0105 return ret;
0106 }
0107
0108 trng->rng.name = pdev->name;
0109 trng->rng.init = ingenic_trng_init;
0110 trng->rng.cleanup = ingenic_trng_cleanup;
0111 trng->rng.read = ingenic_trng_read;
0112
0113 ret = hwrng_register(&trng->rng);
0114 if (ret) {
0115 dev_err(&pdev->dev, "Failed to register hwrng\n");
0116 goto err_unprepare_clk;
0117 }
0118
0119 platform_set_drvdata(pdev, trng);
0120
0121 dev_info(&pdev->dev, "Ingenic DTRNG driver registered\n");
0122 return 0;
0123
0124 err_unprepare_clk:
0125 clk_disable_unprepare(trng->clk);
0126 return ret;
0127 }
0128
0129 static int ingenic_trng_remove(struct platform_device *pdev)
0130 {
0131 struct ingenic_trng *trng = platform_get_drvdata(pdev);
0132 unsigned int ctrl;
0133
0134 hwrng_unregister(&trng->rng);
0135
0136 ctrl = readl(trng->base + TRNG_REG_CFG_OFFSET);
0137 ctrl &= ~CFG_GEN_EN;
0138 writel(ctrl, trng->base + TRNG_REG_CFG_OFFSET);
0139
0140 clk_disable_unprepare(trng->clk);
0141
0142 return 0;
0143 }
0144
0145 static const struct of_device_id ingenic_trng_of_match[] = {
0146 { .compatible = "ingenic,x1830-dtrng" },
0147 { }
0148 };
0149 MODULE_DEVICE_TABLE(of, ingenic_trng_of_match);
0150
0151 static struct platform_driver ingenic_trng_driver = {
0152 .probe = ingenic_trng_probe,
0153 .remove = ingenic_trng_remove,
0154 .driver = {
0155 .name = "ingenic-trng",
0156 .of_match_table = ingenic_trng_of_match,
0157 },
0158 };
0159
0160 module_platform_driver(ingenic_trng_driver);
0161
0162 MODULE_LICENSE("GPL");
0163 MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>");
0164 MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
0165 MODULE_DESCRIPTION("Ingenic True Random Number Generator driver");