1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2019 HiSilicon Limited. */ 3 4#include <linux/acpi.h> 5#include <linux/err.h> 6#include <linux/hw_random.h> 7#include <linux/io.h> 8#include <linux/iopoll.h> 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/platform_device.h> 12#include <linux/random.h> 13 14#define HISI_TRNG_REG 0x00F0 15#define HISI_TRNG_BYTES 4 16#define HISI_TRNG_QUALITY 512 17#define SLEEP_US 10 18#define TIMEOUT_US 10000 19 20struct hisi_trng { 21 void __iomem *base; 22 struct hwrng rng; 23}; 24 25static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 26{ 27 struct hisi_trng *trng; 28 int currsize = 0; 29 u32 val = 0; 30 u32 ret; 31 32 trng = container_of(rng, struct hisi_trng, rng); 33 34 do { 35 ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val, 36 val, SLEEP_US, TIMEOUT_US); 37 if (ret) 38 return currsize; 39 40 if (max - currsize >= HISI_TRNG_BYTES) { 41 memcpy(buf + currsize, &val, HISI_TRNG_BYTES); 42 currsize += HISI_TRNG_BYTES; 43 if (currsize == max) 44 return currsize; 45 continue; 46 } 47 48 /* copy remaining bytes */ 49 memcpy(buf + currsize, &val, max - currsize); 50 currsize = max; 51 } while (currsize < max); 52 53 return currsize; 54} 55 56static int hisi_trng_probe(struct platform_device *pdev) 57{ 58 struct hisi_trng *trng; 59 int ret; 60 61 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); 62 if (!trng) 63 return -ENOMEM; 64 65 trng->base = devm_platform_ioremap_resource(pdev, 0); 66 if (IS_ERR(trng->base)) 67 return PTR_ERR(trng->base); 68 69 trng->rng.name = pdev->name; 70 trng->rng.read = hisi_trng_read; 71 trng->rng.quality = HISI_TRNG_QUALITY; 72 73 ret = devm_hwrng_register(&pdev->dev, &trng->rng); 74 if (ret) 75 dev_err(&pdev->dev, "failed to register hwrng!\n"); 76 77 return ret; 78} 79 80static const struct acpi_device_id hisi_trng_acpi_match[] = { 81 { "HISI02B3", 0 }, 82 { } 83}; 84MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); 85 86static struct platform_driver hisi_trng_driver = { 87 .probe = hisi_trng_probe, 88 .driver = { 89 .name = "hisi-trng-v2", 90 .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), 91 }, 92}; 93 94module_platform_driver(hisi_trng_driver); 95 96MODULE_LICENSE("GPL v2"); 97MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>"); 98MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>"); 99MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver"); 100