blob: aab602c5ed0bfed8b7cb9593fe22a8cef8f0f61a [file] [log] [blame]
Robert Markofa5d33f2020-10-08 22:05:13 +02001// 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
Robert Markofa5d33f2020-10-08 22:05:13 +020012#include <clk.h>
Robert Markofa5d33f2020-10-08 22:05:13 +020013#include <dm.h>
Robert Markofa5d33f2020-10-08 22:05:13 +020014#include <rng.h>
Heinrich Schuchardt10a45cc2024-02-13 00:44:47 +010015#include <asm/io.h>
16#include <linux/bitops.h>
Robert Markofa5d33f2020-10-08 22:05:13 +020017
18/* Device specific register offsets */
19#define PRNG_DATA_OUT 0x0000
20#define PRNG_STATUS 0x0004
21#define PRNG_LFSR_CFG 0x0100
22#define PRNG_CONFIG 0x0104
23
24/* Device specific register masks and config values */
25#define PRNG_LFSR_CFG_MASK 0x0000ffff
26#define PRNG_LFSR_CFG_CLOCKS 0x0000dddd
27#define PRNG_CONFIG_HW_ENABLE BIT(1)
28#define PRNG_STATUS_DATA_AVAIL BIT(0)
29
30#define MAX_HW_FIFO_DEPTH 16
31#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4)
32#define WORD_SZ 4
33
34struct msm_rng_priv {
35 phys_addr_t base;
36 struct clk clk;
Neil Armstrong13ce2612024-11-25 18:12:56 +010037 bool skip_init;
Robert Markofa5d33f2020-10-08 22:05:13 +020038};
39
40static 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;
Sam Daybe3e3f22025-02-12 07:01:55 +000047 int ret;
48
49 ret = clk_enable(&priv->clk);
50 if (ret < 0)
51 return ret;
Robert Markofa5d33f2020-10-08 22:05:13 +020052
53 /* calculate max size bytes to transfer back to caller */
54 maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, len);
55
56 /* read random data from hardware */
57 do {
58 val = readl_relaxed(priv->base + PRNG_STATUS);
59 if (!(val & PRNG_STATUS_DATA_AVAIL))
60 break;
61
62 val = readl_relaxed(priv->base + PRNG_DATA_OUT);
63 if (!val)
64 break;
65
66 *retdata++ = val;
67 currsize += WORD_SZ;
68
69 /* make sure we stay on 32bit boundary */
70 if ((maxsize - currsize) < WORD_SZ)
71 break;
72 } while (currsize < maxsize);
73
Sam Daybe3e3f22025-02-12 07:01:55 +000074 clk_disable(&priv->clk);
75
Robert Markofa5d33f2020-10-08 22:05:13 +020076 return 0;
77}
78
79static int msm_rng_enable(struct msm_rng_priv *priv, int enable)
80{
81 u32 val;
82
83 if (enable) {
84 /* Enable PRNG only if it is not already enabled */
85 val = readl_relaxed(priv->base + PRNG_CONFIG);
Sam Day7051a462025-02-12 07:01:39 +000086 if (!(val & PRNG_CONFIG_HW_ENABLE)) {
Robert Markofa5d33f2020-10-08 22:05:13 +020087 val = readl_relaxed(priv->base + PRNG_LFSR_CFG);
88 val &= ~PRNG_LFSR_CFG_MASK;
89 val |= PRNG_LFSR_CFG_CLOCKS;
90 writel(val, priv->base + PRNG_LFSR_CFG);
91
92 val = readl_relaxed(priv->base + PRNG_CONFIG);
93 val |= PRNG_CONFIG_HW_ENABLE;
94 writel(val, priv->base + PRNG_CONFIG);
95 }
96 } else {
97 val = readl_relaxed(priv->base + PRNG_CONFIG);
98 val &= ~PRNG_CONFIG_HW_ENABLE;
99 writel(val, priv->base + PRNG_CONFIG);
100 }
101
102 return 0;
103}
104
105static int msm_rng_probe(struct udevice *dev)
106{
107 struct msm_rng_priv *priv = dev_get_priv(dev);
108
109 int ret;
110
Neil Armstrong13ce2612024-11-25 18:12:56 +0100111 priv->skip_init = (bool)dev_get_driver_data(dev);
112
Robert Markofa5d33f2020-10-08 22:05:13 +0200113 priv->base = dev_read_addr(dev);
114 if (priv->base == FDT_ADDR_T_NONE)
115 return -EINVAL;
116
Neil Armstrong13ce2612024-11-25 18:12:56 +0100117 if (priv->skip_init)
118 return 0;
119
Robert Markofa5d33f2020-10-08 22:05:13 +0200120 ret = clk_get_by_index(dev, 0, &priv->clk);
121 if (ret)
122 return ret;
123
124 ret = clk_enable(&priv->clk);
125 if (ret < 0)
126 return ret;
127
Sam Daybe3e3f22025-02-12 07:01:55 +0000128 ret = msm_rng_enable(priv, 1);
129 clk_disable(&priv->clk);
130 return ret;
Robert Markofa5d33f2020-10-08 22:05:13 +0200131}
132
133static int msm_rng_remove(struct udevice *dev)
134{
135 struct msm_rng_priv *priv = dev_get_priv(dev);
136
Neil Armstrong13ce2612024-11-25 18:12:56 +0100137 if (priv->skip_init)
138 return 0;
139
Robert Markofa5d33f2020-10-08 22:05:13 +0200140 return msm_rng_enable(priv, 0);
141}
142
143static const struct dm_rng_ops msm_rng_ops = {
144 .read = msm_rng_read,
145};
146
147static const struct udevice_id msm_rng_match[] = {
Neil Armstrong13ce2612024-11-25 18:12:56 +0100148 { .compatible = "qcom,prng", .data = (ulong)false },
149 { .compatible = "qcom,prng-ee", .data = (ulong)true },
150 { .compatible = "qcom,trng", .data = (ulong)true },
Robert Markofa5d33f2020-10-08 22:05:13 +0200151 {},
152};
153
154U_BOOT_DRIVER(msm_rng) = {
155 .name = "msm-rng",
156 .id = UCLASS_RNG,
157 .of_match = msm_rng_match,
158 .ops = &msm_rng_ops,
159 .probe = msm_rng_probe,
160 .remove = msm_rng_remove,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700161 .priv_auto = sizeof(struct msm_rng_priv),
Robert Markofa5d33f2020-10-08 22:05:13 +0200162};