// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 HiSilicon Limited. */ #include #include #include #include #include #include #include #include #include #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 "); MODULE_AUTHOR("Zaibo Xu "); MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver");