Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (C) 2020 Xiphera Ltd. */
  3
  4#include <linux/kernel.h>
  5#include <linux/module.h>
  6#include <linux/mod_devicetable.h>
  7#include <linux/err.h>
  8#include <linux/io.h>
  9#include <linux/hw_random.h>
 10#include <linux/of_device.h>
 11#include <linux/platform_device.h>
 12#include <linux/delay.h>
 13
 14#define CONTROL_REG			0x00000000
 15#define STATUS_REG			0x00000004
 16#define RAND_REG			0x00000000
 17
 18#define HOST_TO_TRNG_RESET		0x00000001
 19#define HOST_TO_TRNG_RELEASE_RESET	0x00000002
 20#define HOST_TO_TRNG_ENABLE		0x80000000
 21#define HOST_TO_TRNG_ZEROIZE		0x80000004
 22#define HOST_TO_TRNG_ACK_ZEROIZE	0x80000008
 23#define HOST_TO_TRNG_READ		0x8000000F
 24
 25/* trng statuses */
 26#define TRNG_ACK_RESET			0x000000AC
 27#define TRNG_SUCCESSFUL_STARTUP		0x00000057
 28#define TRNG_FAILED_STARTUP		0x000000FA
 29#define TRNG_NEW_RAND_AVAILABLE		0x000000ED
 30
 31struct xiphera_trng {
 32	void __iomem *mem;
 33	struct hwrng rng;
 34};
 35
 36static int xiphera_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
 37{
 38	struct xiphera_trng *trng = container_of(rng, struct xiphera_trng, rng);
 39	int ret = 0;
 40
 41	while (max >= sizeof(u32)) {
 42		/* check for data */
 43		if (readl(trng->mem + STATUS_REG) == TRNG_NEW_RAND_AVAILABLE) {
 44			*(u32 *)buf = readl(trng->mem + RAND_REG);
 45			/*
 46			 * Inform the trng of the read
 47			 * and re-enable it to produce a new random number
 48			 */
 49			writel(HOST_TO_TRNG_READ, trng->mem + CONTROL_REG);
 50			writel(HOST_TO_TRNG_ENABLE, trng->mem + CONTROL_REG);
 51			ret += sizeof(u32);
 52			buf += sizeof(u32);
 53			max -= sizeof(u32);
 54		} else {
 55			break;
 56		}
 57	}
 58	return ret;
 59}
 60
 61static int xiphera_trng_probe(struct platform_device *pdev)
 62{
 63	int ret;
 64	struct xiphera_trng *trng;
 65	struct device *dev = &pdev->dev;
 66
 67	trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);
 68	if (!trng)
 69		return -ENOMEM;
 70
 71	trng->mem = devm_platform_ioremap_resource(pdev, 0);
 72	if (IS_ERR(trng->mem))
 73		return PTR_ERR(trng->mem);
 74
 75	/*
 76	 * the trng needs to be reset first which might not happen in time,
 77	 * hence we incorporate a small delay to ensure proper behaviour
 78	 */
 79	writel(HOST_TO_TRNG_RESET, trng->mem + CONTROL_REG);
 80	usleep_range(100, 200);
 81
 82	if (readl(trng->mem + STATUS_REG) != TRNG_ACK_RESET) {
 83		/*
 84		 * there is a small chance the trng is just not ready yet,
 85		 * so we try one more time. If the second time fails, we give up
 86		 */
 87		usleep_range(100, 200);
 88		if (readl(trng->mem + STATUS_REG) != TRNG_ACK_RESET) {
 89			dev_err(dev, "failed to reset the trng ip\n");
 90			return -ENODEV;
 91		}
 92	}
 93
 94	/*
 95	 * once again, to ensure proper behaviour we sleep
 96	 * for a while after zeroizing the trng
 97	 */
 98	writel(HOST_TO_TRNG_RELEASE_RESET, trng->mem + CONTROL_REG);
 99	writel(HOST_TO_TRNG_ENABLE, trng->mem + CONTROL_REG);
100	writel(HOST_TO_TRNG_ZEROIZE, trng->mem + CONTROL_REG);
101	msleep(20);
102
103	if (readl(trng->mem + STATUS_REG) != TRNG_SUCCESSFUL_STARTUP) {
104		/* diagnose the reason for the failure */
105		if (readl(trng->mem + STATUS_REG) == TRNG_FAILED_STARTUP) {
106			dev_err(dev, "trng ip startup-tests failed\n");
107			return -ENODEV;
108		}
109		dev_err(dev, "startup-tests yielded no response\n");
110		return -ENODEV;
111	}
112
113	writel(HOST_TO_TRNG_ACK_ZEROIZE, trng->mem + CONTROL_REG);
114
115	trng->rng.name = pdev->name;
116	trng->rng.read = xiphera_trng_read;
117	trng->rng.quality = 900;
118
119	ret = devm_hwrng_register(dev, &trng->rng);
120	if (ret) {
121		dev_err(dev, "failed to register rng device: %d\n", ret);
122		return ret;
123	}
124
125	platform_set_drvdata(pdev, trng);
126
127	return 0;
128}
129
130static const struct of_device_id xiphera_trng_of_match[] = {
131	{ .compatible = "xiphera,xip8001b-trng", },
132	{},
133};
134MODULE_DEVICE_TABLE(of, xiphera_trng_of_match);
135
136static struct platform_driver xiphera_trng_driver = {
137	.driver = {
138		.name = "xiphera-trng",
139		.of_match_table	= xiphera_trng_of_match,
140	},
141	.probe = xiphera_trng_probe,
142};
143
144module_platform_driver(xiphera_trng_driver);
145
146MODULE_LICENSE("GPL");
147MODULE_AUTHOR("Atte Tommiska");
148MODULE_DESCRIPTION("Xiphera FPGA-based true random number generator driver");