[linux-yocto] [PATCH 37/42] char: hwrng: AXXIA TRNG driver added for AXM55xx

Cristian Bercaru cristian.bercaru at windriver.com
Thu Jun 11 01:32:22 PDT 2015


From: Sreedevi Joshi <sreedevi.joshi at intel.com>

AXXIA TRNG block driver for random number generation has
been added. This provides HW Random number generation using
AXXIA HW block. When enabled in the device tree,
/dev/hwrng device is available and random numbers can be read
from there.

Signed-off-by: Sreedevi Joshi <sreedevi.joshi at intel.com>
---
 arch/arm/boot/dts/axm5508-amarillo.dts |    4 +
 arch/arm/boot/dts/axm5512-amarillo.dts |    4 +
 arch/arm/boot/dts/axm5516-amarillo.dts |    4 +
 arch/arm/boot/dts/axm55xx.dtsi         |    7 +
 drivers/char/hw_random/Kconfig         |    9 +
 drivers/char/hw_random/Makefile        |    1 +
 drivers/char/hw_random/axxia-rng.c     |  584 ++++++++++++++++++++++++++++++++
 7 files changed, 613 insertions(+)
 create mode 100644 drivers/char/hw_random/axxia-rng.c

diff --git a/arch/arm/boot/dts/axm5508-amarillo.dts b/arch/arm/boot/dts/axm5508-amarillo.dts
index c24234a..1ba6a3c 100644
--- a/arch/arm/boot/dts/axm5508-amarillo.dts
+++ b/arch/arm/boot/dts/axm5508-amarillo.dts
@@ -72,6 +72,10 @@
 	status = "okay";
 };
 
+&trng {
+	status = "okay";
+};
+
 &serial0 {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/axm5512-amarillo.dts b/arch/arm/boot/dts/axm5512-amarillo.dts
index 5567b80..8930fe1 100644
--- a/arch/arm/boot/dts/axm5512-amarillo.dts
+++ b/arch/arm/boot/dts/axm5512-amarillo.dts
@@ -72,6 +72,10 @@
 	status = "okay";
 };
 
+&trng {
+	status = "okay";
+};
+
 &serial0 {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/axm5516-amarillo.dts b/arch/arm/boot/dts/axm5516-amarillo.dts
index 59eeb53..a30df3c 100644
--- a/arch/arm/boot/dts/axm5516-amarillo.dts
+++ b/arch/arm/boot/dts/axm5516-amarillo.dts
@@ -76,6 +76,10 @@
 	status = "okay";
 };
 
+&trng {
+	status = "okay";
+};
+
 &serial0 {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/axm55xx.dtsi b/arch/arm/boot/dts/axm55xx.dtsi
index 72ed71f..abfbd63 100644
--- a/arch/arm/boot/dts/axm55xx.dtsi
+++ b/arch/arm/boot/dts/axm55xx.dtsi
@@ -329,6 +329,13 @@
 			status = "disabled";
 		};
 
+		trng: trng at 20101a0000 {
+			compatible = "lsi,trng";
+			reg = <0x20 0x101a0000 0 0x20000>;
+			interrupts = <0 8 4>;
+			status = "disabled";
+		};
+
 		nca at 2020100000 {
 			compatible = "lsi,nca";
 			reg = <0x20 0x20100000 0 0x20000>;
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 2f2b084..9f13423 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -20,6 +20,15 @@ config HW_RANDOM
 
 	  If unsure, say Y.
 
+config HW_RANDOM_AXXIA
+	tristate "Intel Axxia HW TRNG"
+	depends on HW_RANDOM
+	---help---
+	The True Random Number Generator(TRNG) is a random number generator
+	as part of Intel-Axxia chips.
+	This provids a driver for the HW block. RNG device is usually created
+	as /dev/hwrng. Random numbers can be read from this device.
+
 config HW_RANDOM_TIMERIOMEM
 	tristate "Timer IOMEM HW Random Number Generator support"
 	depends on HW_RANDOM && HAS_IOMEM
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 3ae7755..f9980cb 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_HW_RANDOM) += rng-core.o
 rng-core-y := core.o
+obj-$(CONFIG_HW_RANDOM_AXXIA) += axxia-rng.o
 obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
 obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
 obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
diff --git a/drivers/char/hw_random/axxia-rng.c b/drivers/char/hw_random/axxia-rng.c
new file mode 100644
index 0000000..b752e6b
--- /dev/null
+++ b/drivers/char/hw_random/axxia-rng.c
@@ -0,0 +1,584 @@
+/*
+ *  Copyright (C) 2015 Intel Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Device driver for AXXIA True Random Number Generator (TRNG)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/hw_random.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+
+
+/* For internal testing only.. */
+static int trng_test_mode = -1;
+
+#define TRNG_AXM55xx_DEADMAN_LP_CNT             0x1000
+#define NCP_AXM55xx_TRNG_DOUT_COUNT		(4)
+#define TRNG_AXM55xx_SLEEP_USECS		100
+
+/* TRNG registers */
+struct trng_regs {
+	u32 dummy;		/* 0x00 */
+	u32 scratch;		/* 0x04 */
+	u32 reserved_0c[2];	/* 0x8,0x0c */
+	u32 cmd;		/* 0x10 */
+	u32 reserved_1c[3];	/* 0x14..0x1c */
+	u32 sts;		/* 0x20 */
+	u32 reserved_2c[3];	/* 0x24..0x2c */
+	u32 rosc_sts;		/* 0x30 */
+	u32 reserved_3c[3];	/* 0x34..0x3c */
+	u32 dout;		/* 0x40 */
+	u32 reserved_4c[3];	/* 0x44..0x4c */
+	u32 rosc_out;		/* 0x50 */
+	u32 reserved_5c[3];	/* 0x54..0x5c */
+	u32 req_len;		/* 0x60 */
+	u32 reserved_6c[3];	/* 0x64..0x6c */
+	u32 add_in_len;		/* 0x70 */
+	u32 reserved_7c[3];	/* 0x74..0x7c */
+	u32 ctrl;		/* 0x80 */
+	u32 reserved_fc[7];	/* 0x84..0x9c */
+	u32 entropy[8];		/* 0x100-0x11c */
+	u32 reserved_1fc[52];	/* 0x120..0x1fc */
+	u32 add_in[8];		/* 0x200-0x21c */
+	u32 reserved_2fc[52];	/* 0x220-0x2fc */
+	u32 int_status;		/* 0x300 */
+	u32 int_enable;		/* 0x304 */
+	u32 int_frc;		/* 0x308 */
+};
+
+
+#ifdef __TRNG_SIMULATION
+struct trng_regs _trng_regs;
+#endif
+
+/******************************************************/
+/* register definitions generated from RDL */
+
+/******************************************************/
+#define TRNG_AXM55xx_REG_SCRATCH        (0x00000004)  /* RW              */
+#define TRNG_AXM55xx_REG_CMD            (0x00000010)  /*  W              */
+#define TRNG_AXM55xx_REG_STS            (0x00000020)  /* R               */
+#define TRNG_AXM55xx_REG_ROSC_STS       (0x00000030)  /* Rclr            */
+#define TRNG_AXM55xx_REG_DOUT           (0x00000040)  /* R               */
+#define TRNG_AXM55xx_REG_ROSC_OUT       (0x00000050)  /* R               */
+#define TRNG_AXM55xx_REG_REQ_LEN        (0x00000060)  /* RW              */
+#define TRNG_AXM55xx_REG_ADD_IN_LEN     (0x00000070)  /* RW              */
+#define TRNG_AXM55xx_REG_CTRL           (0x00000080)  /* RW              */
+#define TRNG_AXM55xx_REG_ENTROPY        (0x00000100)  /*  W 0x100-0x11c  */
+#define TRNG_AXM55xx_REG_ADD_IN         (0x00000200)  /*  W 0x200-0x21c  */
+#define TRNG_AXM55xx_REG_ISR            (0x00000300)  /* RWoclr          */
+#define TRNG_AXM55xx_REG_IER            (0x00000304)  /* RW              */
+#define TRNG_AXM55xx_REG_IFR            (0x00000308)  /* RW              */
+
+
+enum {
+	TRNG_AXM55xx_CMD_INSTANTIATE = 0x1,
+	TRNG_AXM55xx_CMD_RESEED = 0x2,
+	TRNG_AXM55xx_CMD_GENERATE = 0x3,
+	TRNG_AXM55xx_CMD_TEST = 0x4,
+	TRNG_AXM55xx_CMD_UNINSTANTIATE = 0x5,
+	TRNG_AXM55xx_CMD_STANDBY = 0x6,
+	TRNG_AXM55xx_CMD_RESET = 0x7,
+} ncp_trng_acp34xx_reg_cmds_t;
+
+struct ncp_trng_axm55xx_reg_sts_t {
+#ifdef NCP_BIG_ENDIAN
+	unsigned data_cnt:16;
+	unsigned prediction_resistance:1;
+	unsigned continuous_test_off:1;
+	unsigned reseed_required:1;
+	unsigned ro_entropy:1;
+	unsigned rdy:1;
+	unsigned err:1;
+	unsigned ro_test_in_progress:1;
+	unsigned rng_test_in_progress:1;
+	unsigned continuous_test_failed:1;
+	unsigned ro_test_failed:1;
+	unsigned rng_test_failed:1;
+	unsigned data_valid:1;
+	unsigned standby:1;
+	unsigned cmd:3;
+#else
+	unsigned cmd:3;
+	unsigned standby:1;
+	unsigned data_valid:1;
+	unsigned rng_test_failed:1;
+	unsigned ro_test_failed:1;
+	unsigned continuous_test_failed:1;
+	unsigned rng_test_in_progress:1;
+	unsigned ro_test_in_progress:1;
+	unsigned err:1;
+	unsigned rdy:1;
+	unsigned ro_entropy:1;
+	unsigned reseed_required:1;
+	unsigned continuous_test_off:1;
+	unsigned prediction_resistance:1;
+	unsigned data_cnt:16;
+#endif
+} ncp_trng_axm55xx_reg_sts_t;
+
+#define TRNG_AXM55xx_STS_RDY_MSK        (0x0800)
+#define TRNG_AXM55xx_STS_ERR_MSK        (0x0400)
+#define TRNG_AXM55xx_STS_DOUT_VALID_MSK (0x0010)
+#define TRNG_AXM55xx_STS_CMD_MSK        (0x0003)
+
+
+#define TRNG_AXM55xx_REQ_LEN_MAX	255
+#define TRNG_AXM55xx_CTRL_RO_ENTROPY_MSK        (0x0001)
+
+
+/******************************************************/
+/* end of RDL register definitions */
+/******************************************************/
+
+struct trng_dev {
+	struct kref ref;
+	struct platform_device *pdev;
+	unsigned long flags;
+#define FLAG_REGISTERED        0	/* Misc device registered */
+	struct trng_regs __iomem *regs;
+	unsigned int irq;
+	struct hwrng rng_dev;
+	unsigned int words_gen;
+	unsigned int words_avail;
+};
+
+#define rngdev_to_trng(mdev) container_of(mdev, struct trng_dev, trng_dev)
+
+/* Called when removed and last reference is released */
+static void trng_destroy(struct kref *ref);
+
+
+
+static irqreturn_t trng_isr(int irq_no, void *arg)
+{
+	struct trng_dev *priv = arg;
+	u32 status = readl(&priv->regs->int_status);
+
+	pr_debug("trng: int status %#x\n", status);
+
+	/* Handle interrupt */
+	/* ... */
+
+	/* Write bits to clear interrupt status */
+	writel(status, &priv->regs->int_status);
+
+	return IRQ_HANDLED;
+}
+
+
+static int
+axxia_trng_poll(
+	struct trng_dev *dev,
+	unsigned int	waitmask,
+	unsigned int	*regval)
+{
+	int          deadman = 0;
+
+	do {
+		if (trng_test_mode == -1)
+			*regval = readl(&dev->regs->sts);
+		else
+			*regval = readl(&dev->regs->rosc_sts);
+		if (*regval & waitmask)
+			break;
+
+		usleep_range(TRNG_AXM55xx_SLEEP_USECS,
+				TRNG_AXM55xx_SLEEP_USECS+1);
+		deadman++;
+
+	} while (deadman < TRNG_AXM55xx_DEADMAN_LP_CNT);
+
+	if (*regval & TRNG_AXM55xx_STS_ERR_MSK)
+		return  -1;
+	if (deadman >= TRNG_AXM55xx_DEADMAN_LP_CNT)
+		return -1;
+
+	return 0;
+}
+
+static int axxia_trng_init(struct hwrng *rng)
+{
+	static struct trng_dev *dev;
+	unsigned int val = 0;
+	unsigned int j = 0;
+	unsigned int pers_str[9] = { 0x41435033, 0x34303020, 0x52756e74,
+				0x696d6520, 0x456e7669, 0x726f6e6d,
+				0x656e7420, 0x4c634920, 0x00000000 };
+
+	if (!rng)
+		return -EINVAL;
+
+	dev = (struct trng_dev *) (rng->priv);
+
+	if (!dev)
+		return -EINVAL;
+
+	dev->words_gen = 0;
+	dev->words_avail = 0;
+
+	/* 1. Reset to start work from known state. */
+	writel(TRNG_AXM55xx_CMD_RESET, &(dev->regs->cmd));
+
+	/* 2. Poll status register until rdy or err bits are set */
+	if (axxia_trng_poll(dev,
+			TRNG_AXM55xx_STS_RDY_MSK | TRNG_AXM55xx_STS_ERR_MSK,
+			&val) != 0) {
+		/* couldn't initialize */
+		return -EIO;
+	}
+
+	/* 3. Ring_oscillators' entropy, prediction resistance OFF,
+	* continuous test ON
+	*/
+	writel(TRNG_AXM55xx_CTRL_RO_ENTROPY_MSK, &dev->regs->ctrl);
+
+	/* 4. Compute and Write random initialization key */
+	get_random_bytes(pers_str, 32);
+
+	/*setup add_in_len */
+	writel(256, &dev->regs->add_in_len);
+
+	for (j = 0; j < 8; j++)
+		writel(pers_str[j], &dev->regs->add_in[j]);
+
+	/* Issue INSTANTIATE cmd to tell the device to finish internal setup */
+	writel(TRNG_AXM55xx_CMD_INSTANTIATE, &dev->regs->cmd);
+
+	/* Poll until RDY or ERR bits are set */
+	if (axxia_trng_poll(dev,
+			TRNG_AXM55xx_STS_RDY_MSK | TRNG_AXM55xx_STS_ERR_MSK,
+			&val) != 0) {
+		/* failure. */
+		return -EIO;
+	}
+
+	if ((val & TRNG_AXM55xx_STS_CMD_MSK) != TRNG_AXM55xx_CMD_INSTANTIATE) {
+		/* failure. */
+		return -EIO;
+	}
+
+	/* No additioanl input */
+	writel(0, &dev->regs->add_in_len);
+
+	/* Length of random bit stream desired: 255 *128 bits */
+	dev->words_gen = TRNG_AXM55xx_REQ_LEN_MAX * (128/32);
+	writel(TRNG_AXM55xx_REQ_LEN_MAX, &dev->regs->req_len);
+
+	/* Start the data generator */
+	writel(TRNG_AXM55xx_CMD_GENERATE, &dev->regs->cmd);
+
+	return 0;
+}
+
+static void axxia_trng_destroy(struct hwrng *rng)
+{
+	static struct trng_dev *dev;
+	if ((!rng))
+		return;
+
+	dev = (struct trng_dev *) (rng->priv);
+
+	if ((!dev) || !(dev->regs))
+		return;
+
+	/* reset*/
+	writel(TRNG_AXM55xx_CMD_RESET, &(dev->regs->cmd));
+}
+
+
+static int axxia_trng_fill_buf(void *buf, u32 rword,
+				unsigned int index,
+				unsigned int  max)
+{
+	int k = 0;
+	int num = 0;
+	int i = index;
+
+	for (k = 0;  k < 4; k++) {
+		((unsigned char *)(buf))[i] =
+			(rword & (0xFF << k*8)) >> (k*8);
+		num++;
+		i++;
+		if (i >= max)
+			break;
+	}
+	return num;
+}
+
+static int axxia_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+	static struct trng_dev *dev;
+	unsigned int j = 0, num = 0;
+	u32 rword;
+	u32 rsts;
+	struct ncp_trng_axm55xx_reg_sts_t sts = {0};
+
+
+	if ((!rng) || (!buf))
+		return -EINVAL;
+
+	dev = (struct trng_dev *) (rng->priv);
+
+	if (!dev)
+		return -EINVAL;
+
+	/* test mode is 0 or 1, read from ROSC_STS + ROSC_OUT */
+	if ((trng_test_mode == 0) || (trng_test_mode == 1)) {
+		for (j = 0; j < max; ) {
+			rsts = readl(&dev->regs->rosc_sts);
+			if (!rsts)
+				continue;
+
+			rword = readl(&dev->regs->rosc_out);
+			num = axxia_trng_fill_buf(buf, rword, j, max);
+			j += num;
+			if (j >= max)
+				break;
+
+			/* copy status register as well */
+			if (trng_test_mode > 0) {
+				num = axxia_trng_fill_buf(buf, rsts, j, max);
+				j += num;
+			}
+		}
+		return j;
+	}
+
+	if (trng_test_mode != -1)
+		return -EINVAL;
+	for (j = 0; j < max; ) {
+		/* Read from STS + DOUT */
+		if (dev->words_avail) {
+			/*
+			we read 4  bytes at a time from device.
+			*/
+			rword = readl(&dev->regs->dout);
+			num = axxia_trng_fill_buf(buf, rword, j, max);
+			j += num;
+			dev->words_avail--;
+			dev->words_gen--;
+			continue;
+		} else if (dev->words_gen == 0) {
+
+			dev->words_gen = TRNG_AXM55xx_REQ_LEN_MAX * 4;
+			writel(TRNG_AXM55xx_REQ_LEN_MAX, &dev->regs->req_len);
+			/* And start the data generator ... */
+			writel(TRNG_AXM55xx_CMD_GENERATE, &dev->regs->cmd);
+			/* If it can't wait, return as much as we have.
+			above operation will make it ready for next time.
+			*/
+			if (!wait)
+				break;
+
+			/* wait. status checked in next pass. */
+			usleep_range(TRNG_AXM55xx_SLEEP_USECS,
+					TRNG_AXM55xx_SLEEP_USECS + 1);
+		} else {
+			/* get next data count */
+			sts = *((struct ncp_trng_axm55xx_reg_sts_t *)
+				&(dev->regs->sts));
+
+			if (sts.err)
+				return -EIO;
+			else if (sts.data_valid)
+				dev->words_avail = sts.data_cnt;
+			else if (sts.cmd != TRNG_AXM55xx_CMD_GENERATE)
+				return -EIO;
+		}
+	}
+
+	return j;
+}
+
+
+
+static ssize_t trng_attr_testmode_show(struct device_driver *drv,
+				       char *buf)
+{
+	ssize_t ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", trng_test_mode);
+	return ret;
+}
+
+
+
+static ssize_t trng_attr_testmode_store(struct device_driver *drv,
+				       const char *buf, size_t count)
+{
+	/* do we need locks? */
+	sscanf(buf, "%d", &trng_test_mode);
+
+	return count;
+}
+
+
+static DRIVER_ATTR(trng_test_mode, S_IRUGO | S_IWUSR,
+		   trng_attr_testmode_show,
+		   trng_attr_testmode_store);
+
+
+/**
+ * trng_probe
+ *
+ * Initialize device.
+ */
+static int trng_probe(struct platform_device *pdev)
+{
+	static struct trng_dev *dev;
+	void __iomem *regs;
+	int rc;
+	u32 *pregs;
+
+	pr_debug("!!!!TRNG: trng_probe()\n");
+	/* Allocate space for device private data */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	dev_set_drvdata(&pdev->dev, &dev);
+	kref_init(&dev->ref);
+	dev->pdev = pdev;
+
+	/* Map hardware registers */
+	regs = of_iomap(pdev->dev.of_node, 0);
+	if (!regs) {
+		rc = -EINVAL;
+		goto err;
+	}
+	pregs = (u32 *) regs;
+#ifdef __TRNG_SIMULATION
+	dev->regs = &_trng_regs;
+
+#else
+	dev->regs = regs;
+#endif
+	/* Attach to IRQ */
+	dev->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+	rc = request_irq(dev->irq, trng_isr, 0, "trng", &dev);
+	if (rc)
+		goto err;
+
+	/* Initialize hardware */
+	/* ... */
+
+	/*
+	 * Register device client interface
+	 */
+	dev->rng_dev.name = "axxia-trng";
+	dev->rng_dev.init = axxia_trng_init;
+	dev->rng_dev.cleanup = axxia_trng_destroy;
+	dev->rng_dev.read = axxia_trng_read;
+	dev->rng_dev.priv = (unsigned long) dev;
+
+	rc = hwrng_register(&dev->rng_dev);
+	if (rc)
+		goto err;
+	set_bit(FLAG_REGISTERED, &dev->flags);
+
+	/* test_mode */
+	rc = driver_create_file(pdev->dev.driver, &driver_attr_trng_test_mode);
+	if (rc) {
+		pr_info("Can't create testmode?\n");
+		goto err;
+	}
+
+	return 0;
+
+ err:
+	if (dev)
+		kref_put(&dev->ref, trng_destroy);
+	dev_err(&pdev->dev, "Failed to probe device (%d)\n", rc);
+	return rc;
+}
+
+/**
+ * trng_remove
+ */
+static int trng_remove(struct platform_device *pdev)
+{
+	struct trng_dev *dev = dev_get_drvdata(&pdev->dev);
+	/* test_mode */
+	driver_remove_file(pdev->dev.driver, &driver_attr_trng_test_mode);
+
+	kref_put(&dev->ref, trng_destroy);
+	return 0;
+}
+
+/*
+ * trng_destroy
+ *
+ * Called when refcount reaches zero to unregister device and free resources.
+ */
+static void trng_destroy(struct kref *ref)
+{
+	struct trng_dev *dev = container_of(ref, struct trng_dev, ref);
+
+	dev_set_drvdata(&dev->pdev->dev, NULL);
+	if (test_and_clear_bit(FLAG_REGISTERED, &dev->flags))
+		hwrng_unregister(&dev->rng_dev);
+	if (dev->irq)
+		free_irq(dev->irq, &dev);
+	iounmap(dev->regs);
+	kfree(dev);
+}
+
+#ifdef CONFIG_PM
+static int trng_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	return -ENOSYS;
+}
+
+static int trng_resume(struct platform_device *pdev)
+{
+	return -ENOSYS;
+}
+#else
+#define trng_suspend	NULL
+#define trng_resume	NULL
+#endif
+
+static const struct of_device_id trng_of_ids[] = {
+	{.compatible = "lsi,trng"},
+	{}
+};
+
+static struct platform_driver trng_driver = {
+	.driver = {
+		   .name = "trng",
+		   .owner = THIS_MODULE,
+		   .of_match_table = trng_of_ids,
+		   },
+	.probe = trng_probe,
+	.remove = trng_remove,
+	.suspend = trng_suspend,
+	.resume = trng_resume
+};
+
+module_platform_driver(trng_driver);
+
+MODULE_AUTHOR("Sreedevi Joshi");
+MODULE_DESCRIPTION("AXXIA TRNG- Random Number Generator driver");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5



More information about the linux-yocto mailing list