linux/drivers/char/hw_random/xiphera-trng.c
Rob Herring 0788257aee hwrng: Explicitly include correct DT includes
The DT of_device.h and of_platform.h date back to the separate
of_platform_bus_type before it was merged into the regular platform bus.
As part of that merge prepping Arm DT support 13 years ago, they
"temporarily" include each other. They also include platform_device.h
and of.h. As a result, there's a pretty much random mix of those include
files used throughout the tree. In order to detangle these headers and
replace the implicit includes with struct declarations, users need to
explicitly include the correct includes.

Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2023-08-04 17:09:26 +08:00

148 lines
3.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2020 Xiphera Ltd. */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/hw_random.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#define CONTROL_REG 0x00000000
#define STATUS_REG 0x00000004
#define RAND_REG 0x00000000
#define HOST_TO_TRNG_RESET 0x00000001
#define HOST_TO_TRNG_RELEASE_RESET 0x00000002
#define HOST_TO_TRNG_ENABLE 0x80000000
#define HOST_TO_TRNG_ZEROIZE 0x80000004
#define HOST_TO_TRNG_ACK_ZEROIZE 0x80000008
#define HOST_TO_TRNG_READ 0x8000000F
/* trng statuses */
#define TRNG_ACK_RESET 0x000000AC
#define TRNG_SUCCESSFUL_STARTUP 0x00000057
#define TRNG_FAILED_STARTUP 0x000000FA
#define TRNG_NEW_RAND_AVAILABLE 0x000000ED
struct xiphera_trng {
void __iomem *mem;
struct hwrng rng;
};
static int xiphera_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
{
struct xiphera_trng *trng = container_of(rng, struct xiphera_trng, rng);
int ret = 0;
while (max >= sizeof(u32)) {
/* check for data */
if (readl(trng->mem + STATUS_REG) == TRNG_NEW_RAND_AVAILABLE) {
*(u32 *)buf = readl(trng->mem + RAND_REG);
/*
* Inform the trng of the read
* and re-enable it to produce a new random number
*/
writel(HOST_TO_TRNG_READ, trng->mem + CONTROL_REG);
writel(HOST_TO_TRNG_ENABLE, trng->mem + CONTROL_REG);
ret += sizeof(u32);
buf += sizeof(u32);
max -= sizeof(u32);
} else {
break;
}
}
return ret;
}
static int xiphera_trng_probe(struct platform_device *pdev)
{
int ret;
struct xiphera_trng *trng;
struct device *dev = &pdev->dev;
trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);
if (!trng)
return -ENOMEM;
trng->mem = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(trng->mem))
return PTR_ERR(trng->mem);
/*
* the trng needs to be reset first which might not happen in time,
* hence we incorporate a small delay to ensure proper behaviour
*/
writel(HOST_TO_TRNG_RESET, trng->mem + CONTROL_REG);
usleep_range(100, 200);
if (readl(trng->mem + STATUS_REG) != TRNG_ACK_RESET) {
/*
* there is a small chance the trng is just not ready yet,
* so we try one more time. If the second time fails, we give up
*/
usleep_range(100, 200);
if (readl(trng->mem + STATUS_REG) != TRNG_ACK_RESET) {
dev_err(dev, "failed to reset the trng ip\n");
return -ENODEV;
}
}
/*
* once again, to ensure proper behaviour we sleep
* for a while after zeroizing the trng
*/
writel(HOST_TO_TRNG_RELEASE_RESET, trng->mem + CONTROL_REG);
writel(HOST_TO_TRNG_ENABLE, trng->mem + CONTROL_REG);
writel(HOST_TO_TRNG_ZEROIZE, trng->mem + CONTROL_REG);
msleep(20);
if (readl(trng->mem + STATUS_REG) != TRNG_SUCCESSFUL_STARTUP) {
/* diagnose the reason for the failure */
if (readl(trng->mem + STATUS_REG) == TRNG_FAILED_STARTUP) {
dev_err(dev, "trng ip startup-tests failed\n");
return -ENODEV;
}
dev_err(dev, "startup-tests yielded no response\n");
return -ENODEV;
}
writel(HOST_TO_TRNG_ACK_ZEROIZE, trng->mem + CONTROL_REG);
trng->rng.name = pdev->name;
trng->rng.read = xiphera_trng_read;
trng->rng.quality = 900;
ret = devm_hwrng_register(dev, &trng->rng);
if (ret) {
dev_err(dev, "failed to register rng device: %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, trng);
return 0;
}
static const struct of_device_id xiphera_trng_of_match[] = {
{ .compatible = "xiphera,xip8001b-trng", },
{},
};
MODULE_DEVICE_TABLE(of, xiphera_trng_of_match);
static struct platform_driver xiphera_trng_driver = {
.driver = {
.name = "xiphera-trng",
.of_match_table = xiphera_trng_of_match,
},
.probe = xiphera_trng_probe,
};
module_platform_driver(xiphera_trng_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Atte Tommiska");
MODULE_DESCRIPTION("Xiphera FPGA-based true random number generator driver");