Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-riscv
[platform/kernel/u-boot.git] / drivers / rng / msm_rng.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * PRNG driver for Qualcomm IPQ40xx
4  *
5  * Copyright (c) 2020 Sartura Ltd.
6  *
7  * Author: Robert Marko <robert.marko@sartura.hr>
8  *
9  * Based on Linux driver
10  */
11
12 #include <asm/io.h>
13 #include <clk.h>
14 #include <common.h>
15 #include <dm.h>
16 #include <linux/bitops.h>
17 #include <rng.h>
18
19 /* Device specific register offsets */
20 #define PRNG_DATA_OUT           0x0000
21 #define PRNG_STATUS                     0x0004
22 #define PRNG_LFSR_CFG           0x0100
23 #define PRNG_CONFIG                     0x0104
24
25 /* Device specific register masks and config values */
26 #define PRNG_LFSR_CFG_MASK              0x0000ffff
27 #define PRNG_LFSR_CFG_CLOCKS    0x0000dddd
28 #define PRNG_CONFIG_HW_ENABLE   BIT(1)
29 #define PRNG_STATUS_DATA_AVAIL  BIT(0)
30
31 #define MAX_HW_FIFO_DEPTH               16
32 #define MAX_HW_FIFO_SIZE                (MAX_HW_FIFO_DEPTH * 4)
33 #define WORD_SZ                                 4
34
35 struct msm_rng_priv {
36         phys_addr_t base;
37         struct clk clk;
38 };
39
40 static int msm_rng_read(struct udevice *dev, void *data, size_t len)
41 {
42         struct msm_rng_priv *priv = dev_get_priv(dev);
43         size_t currsize = 0;
44         u32 *retdata = data;
45         size_t maxsize;
46         u32 val;
47
48         /* calculate max size bytes to transfer back to caller */
49         maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, len);
50
51         /* read random data from hardware */
52         do {
53                 val = readl_relaxed(priv->base + PRNG_STATUS);
54                 if (!(val & PRNG_STATUS_DATA_AVAIL))
55                         break;
56
57                 val = readl_relaxed(priv->base + PRNG_DATA_OUT);
58                 if (!val)
59                         break;
60
61                 *retdata++ = val;
62                 currsize += WORD_SZ;
63
64                 /* make sure we stay on 32bit boundary */
65                 if ((maxsize - currsize) < WORD_SZ)
66                         break;
67         } while (currsize < maxsize);
68
69         return 0;
70 }
71
72 static int msm_rng_enable(struct msm_rng_priv *priv, int enable)
73 {
74         u32 val;
75
76         if (enable) {
77                 /* Enable PRNG only if it is not already enabled */
78                 val = readl_relaxed(priv->base + PRNG_CONFIG);
79                 if (val & PRNG_CONFIG_HW_ENABLE) {
80                         val = readl_relaxed(priv->base + PRNG_LFSR_CFG);
81                         val &= ~PRNG_LFSR_CFG_MASK;
82                         val |= PRNG_LFSR_CFG_CLOCKS;
83                         writel(val, priv->base + PRNG_LFSR_CFG);
84
85                         val = readl_relaxed(priv->base + PRNG_CONFIG);
86                         val |= PRNG_CONFIG_HW_ENABLE;
87                         writel(val, priv->base + PRNG_CONFIG);
88                 }
89         } else {
90                 val = readl_relaxed(priv->base + PRNG_CONFIG);
91                 val &= ~PRNG_CONFIG_HW_ENABLE;
92                 writel(val, priv->base + PRNG_CONFIG);
93         }
94
95         return 0;
96 }
97
98 static int msm_rng_probe(struct udevice *dev)
99 {
100         struct msm_rng_priv *priv = dev_get_priv(dev);
101
102         int ret;
103
104         priv->base = dev_read_addr(dev);
105         if (priv->base == FDT_ADDR_T_NONE)
106                 return -EINVAL;
107
108         ret = clk_get_by_index(dev, 0, &priv->clk);
109         if (ret)
110                 return ret;
111
112         ret = clk_enable(&priv->clk);
113         if (ret < 0)
114                 return ret;
115
116         return msm_rng_enable(priv, 1);
117 }
118
119 static int msm_rng_remove(struct udevice *dev)
120 {
121         struct msm_rng_priv *priv = dev_get_priv(dev);
122
123         return msm_rng_enable(priv, 0);
124 }
125
126 static const struct dm_rng_ops msm_rng_ops = {
127         .read = msm_rng_read,
128 };
129
130 static const struct udevice_id msm_rng_match[] = {
131         { .compatible = "qcom,prng", },
132         {},
133 };
134
135 U_BOOT_DRIVER(msm_rng) = {
136         .name = "msm-rng",
137         .id = UCLASS_RNG,
138         .of_match = msm_rng_match,
139         .ops = &msm_rng_ops,
140         .probe = msm_rng_probe,
141         .remove = msm_rng_remove,
142         .priv_auto      = sizeof(struct msm_rng_priv),
143 };