--- /dev/null
+/*
+ ******************************************************************************
+ * @file starfive-trng.c
+ * @author StarFive Technology
+ * @version V1.0
+ * @date 09/08/2021
+ * @brief
+ ******************************************************************************
+ * @copy
+ *
+ * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+ * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+ * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd.
+ */
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/delay.h>
+
+#include "starfive-trng.h"
+
+
+#define to_trng(p) container_of(p, struct trng, rng)
+
+struct trng {
+ struct device *dev;
+ void __iomem *base;
+ u32 mode;
+ u32 ctl_cmd;
+ u32 test_mode;
+ u32 reseed;
+ u32 opmode;
+ volatile int trng_reseed_done;
+ volatile int trng_random_done;
+ struct hwrng rng;
+};
+
+static inline void trng_wait_till_idle(struct trng *hrng)
+{
+ while(readl(hrng->base + CCORE_STAT) & (CCORE_STAT_RAND_GENERATING|CCORE_STAT_RAND_SEEDING))
+ ;
+}
+
+static int trng_put_nonce(struct trng *hrng, const void *nonce_in, int len)
+{
+ memcpy_toio(hrng->base + CCORE_SEED0, nonce_in, len);
+ return 0;
+}
+
+
+static inline int is_random_done(struct trng *hrng)
+{
+ u32 stat;
+
+ if(hrng->opmode == poll_mode) {
+ stat = readl(hrng->base + CCORE_STAT);
+ if((stat & CCORE_STAT_RAND_GENERATING) != CCORE_STAT_RAND_GENERATING) {
+ hrng->trng_random_done = 1;
+ }
+ }
+
+ return (hrng->trng_random_done);
+}
+
+static inline int is_reseed_done(struct trng *hrng)
+{
+ u32 stat;
+
+ if(hrng->opmode == poll_mode) {
+ stat = readl(hrng->base + CCORE_STAT);
+ if(stat & CCORE_STAT_SEEDED) {
+ hrng->trng_reseed_done = 1;
+ }
+ }
+
+ return (hrng->trng_reseed_done);
+}
+
+
+static inline void trng_irq_mask_clear(struct trng *hrng)
+{
+ // clear register: ISTAT
+ u32 data = readl(hrng->base + CCORE_ISTAT);
+ writel(data, hrng->base + CCORE_ISTAT);
+}
+
+
+static int trng_random_reseed(struct trng *hrng)
+{
+ writel(CCORE_CTRL_EXEC_RANDRESEED, hrng->base + CCORE_CTRL);
+
+ do {
+ mdelay(10);
+ } while(!is_reseed_done(hrng));
+ hrng->trng_reseed_done = 0;
+
+ // start random
+ writel(CCORE_CTRL_GENE_RANDOM, hrng->base + CCORE_CTRL);
+ return 0;
+}
+
+
+static int trng_nonce_reseed(struct trng *hrng, const void *nonce_in, int len)
+{
+ writel(CCORE_CTRL_EXEC_NONCRESEED, hrng->base + CCORE_CTRL);
+ trng_put_nonce(hrng, nonce_in, len);
+
+ do {
+ mdelay(10);
+ } while(!is_reseed_done(hrng));
+ hrng->trng_reseed_done = 0;
+
+ // start random
+ writel(CCORE_CTRL_GENE_RANDOM, hrng->base + CCORE_CTRL);
+ return 0;
+}
+
+static int trng_cmd(struct trng *hrng, u32 cmd) {
+ int res = 0;
+ u32 trng_nonce[8] = {
+ 0xcefaedfe, 0xefbeadde, 0xcefaedfe, 0xefbeadde,
+ 0xd2daadab, 0x00000000, 0x00000000, 0x00000000,
+ };
+
+ // wait till idle
+ trng_wait_till_idle(hrng);
+
+ //start trng
+ switch (cmd) {
+ case CCORE_CTRL_EXEC_NOP:
+ case CCORE_CTRL_GENE_RANDOM:
+ writel(cmd, hrng->base + CCORE_CTRL);
+ break;
+
+ case CCORE_CTRL_EXEC_RANDRESEED:
+ trng_random_reseed(hrng);
+ break;
+
+ case CCORE_CTRL_EXEC_NONCRESEED:
+ trng_nonce_reseed(hrng, trng_nonce, sizeof(trng_nonce));
+ break;
+ default:
+ res = -1;
+ break;
+ }
+
+ return res;
+}
+
+
+static int trng_init(struct hwrng *rng)
+{
+ struct trng *hrng = to_trng(rng);
+ u32 mode, smode = 0;
+
+ //disable Auto Request/Age register
+ writel(AUTOAGE_DISABLED, hrng->base + CCORE_AUTO_AGE);
+ writel(AUTOREQ_DISABLED, hrng->base + CCORE_AUTO_RQSTS);
+
+ // clear register: ISTAT
+ trng_irq_mask_clear(hrng);
+
+ //set smode/mode
+ mode = readl(hrng->base + CCORE_MODE);
+ smode = readl(hrng->base + CCORE_SMODE);
+
+ switch(hrng->mode) {
+ case PRNG_128BIT:
+ mode &= ~CCORE_MODE_R256;
+ break;
+ case PRNG_256BIT:
+ mode |= CCORE_MODE_R256;
+ break;
+ default:
+ dev_info(hrng->dev, "Use Default mode PRNG_256BIT\r\n");
+ mode |= CCORE_MODE_R256;
+ break;
+ }
+
+ if(hrng->test_mode == 1) {
+ smode |= CCORE_SMODE_MISSION_MODE;
+ }
+
+ if(hrng->reseed == NONCE_RESEED) {
+ smode |= CCORE_SMODE_NONCE_MODE;
+ }
+
+ writel(mode, hrng->base + CCORE_MODE);
+ writel(smode, hrng->base + CCORE_SMODE);
+
+ //clear int_mode
+ if(hrng->opmode == int_mode) {
+ writel(0, hrng->base + CCORE_IE);
+ }
+ return 0;
+}
+
+static irqreturn_t trng_irq(int irq, void *priv)
+{
+ u32 status;
+ struct trng *hrng = (struct trng *)priv;
+
+ status = readl(hrng->base + CCORE_ISTAT);
+ if (status & CCORE_ISTAT_RAND_RDY) {
+
+ writel(CCORE_ISTAT_RAND_RDY, hrng->base + CCORE_ISTAT);
+ //dev_info(hrng->dev, "rand ready\r\n");
+ hrng->trng_random_done = 1;
+ }
+
+ if (status & CCORE_ISTAT_SEED_DONE) {
+
+ writel(CCORE_ISTAT_SEED_DONE, hrng->base + CCORE_ISTAT);
+ //dev_info(hrng->dev, "seed ready\r\n");
+ hrng->trng_reseed_done = 1 ;
+ }
+
+
+ if (status & CCORE_ISTAT_AGE_ALARM) {
+
+ writel(CCORE_ISTAT_AGE_ALARM, hrng->base + CCORE_ISTAT);
+ //dev_info(hrng->dev, "age alarm\r\n");
+ }
+
+ if (status & CCORE_ISTAT_LFSR_LOOKUP) {
+
+ writel(CCORE_ISTAT_LFSR_LOOKUP, hrng->base + CCORE_ISTAT);
+ //dev_info(hrng->dev, "lfsr lookup\r\n");
+ }
+
+ trng_irq_mask_clear(hrng);
+
+ return IRQ_HANDLED;
+}
+
+static void trng_cleanup(struct hwrng *rng)
+{
+ struct trng *hrng = to_trng(rng);
+
+ writel(0, hrng->base + CCORE_CTRL);
+}
+
+
+static int trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct trng *hrng = to_trng(rng);
+ u32 intr = 0;
+
+ trng_cmd(hrng, CCORE_CTRL_EXEC_NOP);
+ //trng_wait_till_idle(hrng);
+
+ if(hrng->mode == PRNG_256BIT)
+ max = min_t(size_t, max, (CCORE_RAND_LEN * 4));
+ else
+ max = min_t(size_t, max, (CCORE_RAND_LEN/2 * 4));
+
+ hrng->trng_random_done = 0;
+ if(hrng->opmode == int_mode) {
+ intr |= CCORE_IE_ALL;
+ }
+ writel(intr, hrng->base + CCORE_IE);
+
+ trng_cmd(hrng, hrng->ctl_cmd);
+ //trng_wait_till_idle(hrng);
+
+ if(wait)
+ {
+ do {
+ mdelay(10);
+ } while(!is_random_done(hrng));
+ }
+
+ if(is_random_done(hrng))
+ {
+ memcpy_fromio(buf, hrng->base + CCORE_RAND0, max);
+ }
+ else
+ max = 0;
+
+ trng_cmd(hrng, CCORE_CTRL_EXEC_NOP);
+ trng_wait_till_idle(hrng);
+ writel(0, hrng->base + CCORE_IE);
+
+ return max;
+}
+
+static int trng_probe(struct platform_device *pdev)
+{
+ int ret;
+ int irq;
+ struct trng *rng;
+ struct resource *res;
+
+ rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
+ if (!rng){
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, rng);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rng->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rng->base)){
+ return PTR_ERR(rng->base);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "Couldn't get irq %d\n", irq);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, trng_irq, 0, pdev->name,
+ (void *)rng);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get interrupt working.\n");
+ return ret;
+ }
+
+ rng->rng.name = pdev->name;
+ rng->rng.init = trng_init;
+ rng->rng.cleanup = trng_cleanup;
+ rng->rng.read = trng_read;
+
+ rng->mode = PRNG_256BIT;
+ rng->ctl_cmd = CCORE_CTRL_EXEC_RANDRESEED;
+ if(rng->ctl_cmd == CCORE_CTRL_EXEC_NONCRESEED)
+ {
+ rng->opmode = poll_mode;
+ rng->test_mode = 1;
+ rng->reseed = NONCE_RESEED;
+ }
+ else
+ {
+ rng->opmode = int_mode;
+ rng->test_mode = 0;
+ rng->reseed = RANDOM_RESEED;
+ }
+ rng->dev = &pdev->dev;
+
+ ret = devm_hwrng_register(&pdev->dev, &rng->rng);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register hwrng\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id trng_dt_ids[] = {
+ { .compatible = "starfive,trng" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, trng_dt_ids);
+
+
+static struct platform_driver trng_driver = {
+ .probe = trng_probe,
+ .driver = {
+ .name = "trng",
+ .of_match_table = of_match_ptr(trng_dt_ids),
+ },
+};
+
+module_platform_driver(trng_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jenny Zhang <jenny.zhang@starfivetech.com>");
+MODULE_DESCRIPTION("Starfive true random number generator driver");
--- /dev/null
+/*
+ ******************************************************************************
+ * @file starfive-trng.h
+ * @author StarFive Technology
+ * @version V1.0
+ * @date 09/08/2021
+ * @brief
+ ******************************************************************************
+ * @copy
+ *
+ * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+ * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+ * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd.
+ */
+
+#define CCORE_CTRL 0x00
+#define CCORE_STAT 0x04
+#define CCORE_MODE 0x08
+#define CCORE_SMODE 0x0C
+#define CCORE_IE 0x10
+#define CCORE_ISTAT 0x14
+#define CCORE_FEATURES 0x1C
+#define CCORE_RAND0 0x20
+#define CCORE_RAND1 0x24
+#define CCORE_RAND2 0x28
+#define CCORE_RAND3 0x2C
+#define CCORE_RAND4 0x30
+#define CCORE_RAND5 0x34
+#define CCORE_RAND6 0x38
+#define CCORE_RAND7 0x3C
+#define CCORE_SEED0 0x40
+#define CCORE_SEED1 0x44
+#define CCORE_SEED2 0x48
+#define CCORE_SEED3 0x4C
+#define CCORE_SEED4 0x50
+#define CCORE_SEED5 0x54
+#define CCORE_SEED6 0x58
+#define CCORE_SEED7 0x5C
+#define CCORE_AUTO_RQSTS 0x60
+#define CCORE_AUTO_AGE 0x64
+#define CCORE_BUILD_CONFIG 0x68
+
+/* CTRL CMD*/
+#define CCORE_CTRL_EXEC_NOP (0x0)
+#define CCORE_CTRL_GENE_RANDOM (0x1)
+#define CCORE_CTRL_EXEC_RANDRESEED (0x2)
+#define CCORE_CTRL_EXEC_NONCRESEED (0x3)
+
+
+/* STAT */
+#define _CCORE_STAT_NONCE_MODE 2
+#define _CCORE_STAT_R256 3
+#define _CCORE_STAT_MISSION_MODE 8
+#define _CCORE_STAT_SEEDED 9
+#define _CCORE_STAT_LAST_RESEED 16
+#define _CCORE_STAT_SRVC_RQST 27
+#define _CCORE_STAT_RAND_GENERATING 30
+#define _CCORE_STAT_RAND_SEEDING 31
+
+#define CCORE_STAT_NONCE_MODE (1UL << _CCORE_STAT_NONCE_MODE)
+#define CCORE_STAT_R256 (1UL << _CCORE_STAT_R256)
+#define CCORE_STAT_MISSION_MODE (1UL << _CCORE_STAT_MISSION_MODE)
+#define CCORE_STAT_SEEDED (1UL << _CCORE_STAT_SEEDED)
+#define CCORE_STAT_LAST_RESEED(x) ((x) << _CCORE_STAT_LAST_RESEED)
+#define CCORE_STAT_SRVC_RQST (1UL << _CCORE_STAT_SRVC_RQST)
+#define CCORE_STAT_RAND_GENERATING (1UL << _CCORE_STAT_RAND_GENERATING)
+#define CCORE_STAT_RAND_SEEDING (1UL << _CCORE_STAT_RAND_SEEDING)
+
+
+
+/* MODE */
+#define _CCORE_MODE_R256 3
+#define CCORE_MODE_R256 (1UL << _CCORE_MODE_R256)
+
+/* SMODE */
+#define _CCORE_SMODE_NONCE_MODE 2
+#define _CCORE_SMODE_MISSION_MODE 8
+#define _CCORE_SMODE_MAX_REJECTS 16
+
+#define CCORE_SMODE_NONCE_MODE (1UL << _CCORE_SMODE_NONCE_MODE)
+#define CCORE_SMODE_MISSION_MODE (1UL << _CCORE_SMODE_MISSION_MODE)
+#define CCORE_SMODE_MAX_REJECTS(x) ((x) << _CCORE_SMODE_MAX_REJECTS)
+
+
+/* IE */
+#define _CCORE_IE_RAND_REY_EN 0
+#define _CCORE_IE_SEED_DONE_EN 1
+#define _CCORE_IE_AGE_ALARM_EN 2
+#define _CCORE_IE_RQST_ALARM_EN 3
+#define _CCORE_IE_LOCKUP_EN 4
+#define _CCORE_IE_GLBL_EN 31
+
+
+#define CCORE_IE_RAND_REY_EN (1UL << _CCORE_IE_RAND_REY_EN)
+#define CCORE_IE_SEED_DONE_EN (1UL << _CCORE_IE_SEED_DONE_EN)
+#define CCORE_IE_AGE_ALARM_EN (1UL << _CCORE_IE_AGE_ALARM_EN)
+#define CCORE_IE_RQST_ALARM_EN (1UL << _CCORE_IE_RQST_ALARM_EN)
+#define CCORE_IE_LOCKUP_EN (1UL << _CCORE_IE_LOCKUP_EN)
+#define CCORE_IE_GLBL_EN (1UL << _CCORE_IE_GLBL_EN)
+
+#define CCORE_IE_ALL (CCORE_IE_GLBL_EN | CCORE_IE_RAND_REY_EN | CCORE_IE_SEED_DONE_EN | \
+ CCORE_IE_AGE_ALARM_EN | CCORE_IE_RQST_ALARM_EN | CCORE_IE_LOCKUP_EN)
+
+/* ISTAT */
+#define _CCORE_ISTAT_RAND_RDY 0
+#define _CCORE_ISTAT_SEED_DONE 1
+#define _CCORE_ISTAT_AGE_ALARM 2
+#define _CCORE_ISTAT_RQST_ALARM 3
+#define _CCORE_ISTAT_LFSR_LOOKUP 4
+
+
+#define CCORE_ISTAT_RAND_RDY (1UL << _CCORE_ISTAT_RAND_RDY)
+#define CCORE_ISTAT_SEED_DONE (1UL << _CCORE_ISTAT_SEED_DONE)
+#define CCORE_ISTAT_AGE_ALARM (1UL << _CCORE_ISTAT_AGE_ALARM)
+#define CCORE_ISTAT_LFSR_LOOKUP (1UL << _CCORE_ISTAT_LFSR_LOOKUP)
+
+
+/* FEATURES */
+#define _CCORE_FEATURES_MAX_RAND_LENGTH 0
+#define _CCORE_FEATURES_RAND_SEED_AVAIL 2
+#define _CCORE_FEATURES_MISSION_MODE_RESET_STATE 3
+
+#define CCORE_FEATURES_MAX_RAND_LENGTH(x) (x << _CCORE_FEATURES_MAX_RAND_LENGTH)
+#define CCORE_FEATURES_RAND_SEED_AVAIL (1UL << _CCORE_FEATURES_RAND_SEED_AVAIL)
+#define CCORE_FEATURES_MISSION_MODE_RESET_STATE (1UL << _CCORE_FEATURES_MISSION_MODE_RESET_STATE)
+
+
+#define AUTOREQ_DISABLED (0x0)
+#define AUTOAGE_DISABLED (0x0)
+
+
+/* BUILD_CONFIG */
+#define _CCORE_BUILD_CONFIG_MAX_PRNG_LEN 2
+#define _CCORE_BUILD_CONFIG_PRNG_LEN_AFTER_REST 3
+#define _CCORE_BUILD_CONFIG_MODE_AFTER_REST 4
+#define _CCORE_BUILD_CONFIG_AUTO_RESEED_LOOPBACK 5
+
+#define CCORE_BUILD_CONFIG_MAX_PRNG_LEN (1UL << _CCORE_BUILD_CONFIG_MAX_PRNG_LEN)
+#define CCORE_BUILD_CONFIG_PRNG_LEN_AFTER_REST (1UL << _CCORE_BUILD_CONFIG_PRNG_LEN_AFTER_REST)
+#define CCORE_BUILD_CONFIG_MODE_AFTER_REST (1UL << _CCORE_BUILD_CONFIG_MODE_AFTER_REST)
+#define CCORE_BUILD_CONFIG_AUTO_RESEED_LOOPBACK (1UL << _CCORE_BUILD_CONFIG_AUTO_RESEED_LOOPBACK)
+
+
+#define CCORE_RAND_LEN 8
+#define CCORE_SEED_LEN 8
+
+enum sec_opmode {
+ poll_mode,
+ int_mode,
+};
+
+enum sec_trng_reseed {
+ RANDOM_RESEED,
+ NONCE_RESEED,
+};
+
+enum sec_trng_mode {
+ PRNG_128BIT, //PRNG set up for 128bit maximum
+ PRNG_256BIT, //PRNG set up for 256bit maximum
+};
+