hc
2024-10-12 a5969cabbb4660eab42b6ef0412cbbd1200cf14d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// 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");