brintos

brintos / linux-shallow public Read only

0
0
Text · 3.8 KiB · 4af64f7 Raw
146 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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/platform_device.h>11#include <linux/delay.h>12 13#define CONTROL_REG			0x0000000014#define STATUS_REG			0x0000000415#define RAND_REG			0x0000000016 17#define HOST_TO_TRNG_RESET		0x0000000118#define HOST_TO_TRNG_RELEASE_RESET	0x0000000219#define HOST_TO_TRNG_ENABLE		0x8000000020#define HOST_TO_TRNG_ZEROIZE		0x8000000421#define HOST_TO_TRNG_ACK_ZEROIZE	0x8000000822#define HOST_TO_TRNG_READ		0x8000000F23 24/* trng statuses */25#define TRNG_ACK_RESET			0x000000AC26#define TRNG_SUCCESSFUL_STARTUP		0x0000005727#define TRNG_FAILED_STARTUP		0x000000FA28#define TRNG_NEW_RAND_AVAILABLE		0x000000ED29 30struct xiphera_trng {31	void __iomem *mem;32	struct hwrng rng;33};34 35static int xiphera_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)36{37	struct xiphera_trng *trng = container_of(rng, struct xiphera_trng, rng);38	int ret = 0;39 40	while (max >= sizeof(u32)) {41		/* check for data */42		if (readl(trng->mem + STATUS_REG) == TRNG_NEW_RAND_AVAILABLE) {43			*(u32 *)buf = readl(trng->mem + RAND_REG);44			/*45			 * Inform the trng of the read46			 * and re-enable it to produce a new random number47			 */48			writel(HOST_TO_TRNG_READ, trng->mem + CONTROL_REG);49			writel(HOST_TO_TRNG_ENABLE, trng->mem + CONTROL_REG);50			ret += sizeof(u32);51			buf += sizeof(u32);52			max -= sizeof(u32);53		} else {54			break;55		}56	}57	return ret;58}59 60static int xiphera_trng_probe(struct platform_device *pdev)61{62	int ret;63	struct xiphera_trng *trng;64	struct device *dev = &pdev->dev;65 66	trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);67	if (!trng)68		return -ENOMEM;69 70	trng->mem = devm_platform_ioremap_resource(pdev, 0);71	if (IS_ERR(trng->mem))72		return PTR_ERR(trng->mem);73 74	/*75	 * the trng needs to be reset first which might not happen in time,76	 * hence we incorporate a small delay to ensure proper behaviour77	 */78	writel(HOST_TO_TRNG_RESET, trng->mem + CONTROL_REG);79	usleep_range(100, 200);80 81	if (readl(trng->mem + STATUS_REG) != TRNG_ACK_RESET) {82		/*83		 * there is a small chance the trng is just not ready yet,84		 * so we try one more time. If the second time fails, we give up85		 */86		usleep_range(100, 200);87		if (readl(trng->mem + STATUS_REG) != TRNG_ACK_RESET) {88			dev_err(dev, "failed to reset the trng ip\n");89			return -ENODEV;90		}91	}92 93	/*94	 * once again, to ensure proper behaviour we sleep95	 * for a while after zeroizing the trng96	 */97	writel(HOST_TO_TRNG_RELEASE_RESET, trng->mem + CONTROL_REG);98	writel(HOST_TO_TRNG_ENABLE, trng->mem + CONTROL_REG);99	writel(HOST_TO_TRNG_ZEROIZE, trng->mem + CONTROL_REG);100	msleep(20);101 102	if (readl(trng->mem + STATUS_REG) != TRNG_SUCCESSFUL_STARTUP) {103		/* diagnose the reason for the failure */104		if (readl(trng->mem + STATUS_REG) == TRNG_FAILED_STARTUP) {105			dev_err(dev, "trng ip startup-tests failed\n");106			return -ENODEV;107		}108		dev_err(dev, "startup-tests yielded no response\n");109		return -ENODEV;110	}111 112	writel(HOST_TO_TRNG_ACK_ZEROIZE, trng->mem + CONTROL_REG);113 114	trng->rng.name = pdev->name;115	trng->rng.read = xiphera_trng_read;116	trng->rng.quality = 900;117 118	ret = devm_hwrng_register(dev, &trng->rng);119	if (ret) {120		dev_err(dev, "failed to register rng device: %d\n", ret);121		return ret;122	}123 124	return 0;125}126 127static const struct of_device_id xiphera_trng_of_match[] = {128	{ .compatible = "xiphera,xip8001b-trng", },129	{},130};131MODULE_DEVICE_TABLE(of, xiphera_trng_of_match);132 133static struct platform_driver xiphera_trng_driver = {134	.driver = {135		.name = "xiphera-trng",136		.of_match_table	= xiphera_trng_of_match,137	},138	.probe = xiphera_trng_probe,139};140 141module_platform_driver(xiphera_trng_driver);142 143MODULE_LICENSE("GPL");144MODULE_AUTHOR("Atte Tommiska");145MODULE_DESCRIPTION("Xiphera FPGA-based true random number generator driver");146