| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2019 HiSilicon Limited. */ |
| |
| #include <linux/acpi.h> |
| #include <linux/err.h> |
| #include <linux/hw_random.h> |
| #include <linux/io.h> |
| #include <linux/iopoll.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/random.h> |
| |
| #define HISI_TRNG_REG 0x00F0 |
| #define HISI_TRNG_BYTES 4 |
| #define HISI_TRNG_QUALITY 512 |
| #define SLEEP_US 10 |
| #define TIMEOUT_US 10000 |
| |
| struct hisi_trng { |
| void __iomem *base; |
| struct hwrng rng; |
| }; |
| |
| static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) |
| { |
| struct hisi_trng *trng; |
| int currsize = 0; |
| u32 val = 0; |
| u32 ret; |
| |
| trng = container_of(rng, struct hisi_trng, rng); |
| |
| do { |
| ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val, |
| val, SLEEP_US, TIMEOUT_US); |
| if (ret) |
| return currsize; |
| |
| if (max - currsize >= HISI_TRNG_BYTES) { |
| memcpy(buf + currsize, &val, HISI_TRNG_BYTES); |
| currsize += HISI_TRNG_BYTES; |
| if (currsize == max) |
| return currsize; |
| continue; |
| } |
| |
| /* copy remaining bytes */ |
| memcpy(buf + currsize, &val, max - currsize); |
| currsize = max; |
| } while (currsize < max); |
| |
| return currsize; |
| } |
| |
| static int hisi_trng_probe(struct platform_device *pdev) |
| { |
| struct hisi_trng *trng; |
| int ret; |
| |
| trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); |
| if (!trng) |
| return -ENOMEM; |
| |
| trng->base = devm_platform_ioremap_resource(pdev, 0); |
| if (IS_ERR(trng->base)) |
| return PTR_ERR(trng->base); |
| |
| trng->rng.name = pdev->name; |
| trng->rng.read = hisi_trng_read; |
| trng->rng.quality = HISI_TRNG_QUALITY; |
| |
| ret = devm_hwrng_register(&pdev->dev, &trng->rng); |
| if (ret) |
| dev_err(&pdev->dev, "failed to register hwrng!\n"); |
| |
| return ret; |
| } |
| |
| static const struct acpi_device_id hisi_trng_acpi_match[] = { |
| { "HISI02B3", 0 }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); |
| |
| static struct platform_driver hisi_trng_driver = { |
| .probe = hisi_trng_probe, |
| .driver = { |
| .name = "hisi-trng-v2", |
| .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), |
| }, |
| }; |
| |
| module_platform_driver(hisi_trng_driver); |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>"); |
| MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>"); |
| MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver"); |